blob: a7b2d74955b41aa8667e73c4ea01dc7cb6f7781c [file] [log] [blame]
//===--- ExtInfo.cpp - Extended information for function types ------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements ASTExtInfo, SILExtInfo and related classes.
//
//===----------------------------------------------------------------------===//
#include "swift/AST/ClangModuleLoader.h"
#include "swift/AST/ExtInfo.h"
#include "clang/AST/Type.h"
#include "llvm/ADT/Optional.h"
namespace swift {
// MARK: - ClangTypeInfo
bool operator==(ClangTypeInfo lhs, ClangTypeInfo rhs) {
if (lhs.type == rhs.type)
return true;
if (lhs.type && rhs.type)
return lhs.type->getCanonicalTypeInternal() ==
rhs.type->getCanonicalTypeInternal();
return false;
}
bool operator!=(ClangTypeInfo lhs, ClangTypeInfo rhs) {
return !(lhs == rhs);
}
ClangTypeInfo ClangTypeInfo::getCanonical() const {
if (!type)
return ClangTypeInfo();
return ClangTypeInfo(type->getCanonicalTypeInternal().getTypePtr());
}
void ClangTypeInfo::printType(ClangModuleLoader *cml,
llvm::raw_ostream &os) const {
cml->printClangType(type, os);
}
void ClangTypeInfo::dump(llvm::raw_ostream &os,
const clang::ASTContext &ctx) const {
if (type) {
type->dump(os, ctx);
} else {
os << "<nullptr>";
}
}
// MARK: - UnexpectedClangTypeError
Optional<UnexpectedClangTypeError> UnexpectedClangTypeError::checkClangType(
SILFunctionTypeRepresentation silRep,
const clang::Type *type, bool expectNonnullForCOrBlock, bool expectCanonical) {
#ifdef NDEBUG
return None;
#else
bool isBlock = true;
switch (silRep) {
case SILFunctionTypeRepresentation::CFunctionPointer:
isBlock = false;
LLVM_FALLTHROUGH;
case SILFunctionTypeRepresentation::Block: {
if (!type) {
if (expectNonnullForCOrBlock)
return {{Kind::NullForCOrBlock, type}};
return None;
}
if (expectCanonical && !type->isCanonicalUnqualified())
return {{Kind::NonCanonical, type}};
if (isBlock && !type->isBlockPointerType())
return {{Kind::NotBlockPointer, type}};
if (!isBlock && !(type->isFunctionPointerType()
|| type->isFunctionReferenceType()))
return {{Kind::NotFunctionPointerOrReference, type}};
return None;
}
default: {
if (type)
return {{Kind::NonnullForNonCOrBlock, type}};
return None;
}
}
#endif
}
void UnexpectedClangTypeError::dump() {
#if SWIFT_BUILD_ONLY_SYNTAXPARSERLIB
return; // not needed for the parser library.
#endif
auto &e = llvm::errs();
using Kind = UnexpectedClangTypeError::Kind;
switch (errorKind) {
case Kind::NullForCOrBlock: {
e << "Expected non-null Clang type for @convention(c)/@convention(block)"
<< " function but found nullptr.";
return;
}
case Kind::NonnullForNonCOrBlock: {
e << ("Expected null Clang type for non-@convention(c),"
" non-@convention(block) function but found:\n");
type->dump();
return;
}
case Kind::NotBlockPointer: {
e << ("Expected block pointer type for @convention(block) function but"
" found:\n");
type->dump();
return;
}
case Kind::NotFunctionPointerOrReference: {
e << ("Expected function pointer/reference type for @convention(c) function"
" but found:\n");
type->dump();
return;
}
case Kind::NonCanonical: {
e << "Expected canonicalized Clang type but found:\n";
type->dump();
return;
}
}
llvm_unreachable("Unhandled case for UnexpectedClangTypeError");
}
// [NOTE: ExtInfo-Clang-type-invariant]
// At the SIL level, all @convention(c) and @convention(block) function types
// are expected to carry a ClangTypeInfo. This is not enforced at the AST level
// because we may synthesize types which are not convertible to Clang types.
// 1. Type errors: If we have a type error, we may end up generating (say) a
// @convention(c) function type that has an ErrorType as a parameter.
// 2. Bridging: The representation can change during bridging. For example, an
// @convention(swift) function can be bridged to an @convention(block)
// function. Since this happens during SILGen, we may see a "funny" type
// like @convention(c) () -> @convention(swift) () -> () at the AST level.
// MARK: - ASTExtInfoBuilder
void ASTExtInfoBuilder::checkInvariants() const {
// See [NOTE: ExtInfo-Clang-type-invariant]
if (auto error = UnexpectedClangTypeError::checkClangType(
getSILRepresentation(), clangTypeInfo.getType(), false, false)) {
error.getValue().dump();
llvm_unreachable("Ill-formed ASTExtInfoBuilder.");
}
}
// MARK: - ASTExtInfo
ASTExtInfo ASTExtInfoBuilder::build() const {
checkInvariants();
return ASTExtInfo(*this);
}
// MARK: - SILExtInfoBuilder
void SILExtInfoBuilder::checkInvariants() const {
// See [NOTE: ExtInfo-Clang-type-invariant]
// [FIXME: Clang-type-plumbing] Strengthen check when UseClangFunctionTypes
// is removed.
if (auto error = UnexpectedClangTypeError::checkClangType(
getRepresentation(), clangTypeInfo.getType(), false, true)) {
error.getValue().dump();
llvm_unreachable("Ill-formed SILExtInfoBuilder.");
}
}
SILExtInfo SILExtInfoBuilder::build() const {
checkInvariants();
return SILExtInfo(*this);
}
// MARK: - SILExtInfo
Optional<UnexpectedClangTypeError> SILExtInfo::checkClangType() const {
return UnexpectedClangTypeError::checkClangType(
getRepresentation(), getClangTypeInfo().getType(), true, true);
}
} // end namespace swift