diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index be98a06362e3c..9b58089141b5b 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -595,6 +595,10 @@ class ASTContext final { const FunctionType::ExtInfo incompleteExtInfo, FunctionTypeRepresentation trueRep); + /// Get the Swift declaration that a Clang declaration was exported from, + /// if applicable. + const Decl *getSwiftDeclForExportedClangDecl(const clang::Decl *decl); + /// Determine whether the given Swift type is representable in a /// given foreign language. ForeignRepresentationInfo diff --git a/include/swift/AST/ClangModuleLoader.h b/include/swift/AST/ClangModuleLoader.h index 0e3d109226fd0..595c55b420c33 100644 --- a/include/swift/AST/ClangModuleLoader.h +++ b/include/swift/AST/ClangModuleLoader.h @@ -14,10 +14,12 @@ #define SWIFT_AST_CLANG_MODULE_LOADER_H #include "swift/AST/ModuleLoader.h" +#include "swift/Basic/TaggedUnion.h" namespace clang { class ASTContext; class CompilerInstance; +class Decl; class Preprocessor; class Sema; class TargetInfo; @@ -26,6 +28,7 @@ class Type; namespace swift { +class Decl; class DeclContext; class VisibleDeclConsumer; @@ -40,6 +43,69 @@ enum class ClangTypeKind { ObjCProtocol, }; +/// A path for serializing a declaration. +class StableSerializationPath { +public: + struct ExternalPath { + enum ComponentKind { + /// A named record type (but not a template specialization) + Record, + + /// A named enum type + Enum, + + /// A C++ namespace + Namespace, + + /// A typedef + Typedef, + + /// A typedef's anonymous tag declaration. Identifier is empty. + TypedefAnonDecl, + + /// An Objective-C interface. + ObjCInterface, + + /// An Objective-C protocol. + ObjCProtocol, + }; + + static bool requiresIdentifier(ComponentKind kind) { + return kind != TypedefAnonDecl; + } + + SmallVector, 2> Path; + + void add(ComponentKind kind, Identifier name) { + Path.push_back({kind, name}); + } + }; +private: + TaggedUnion Union; + +public: + StableSerializationPath() {} + StableSerializationPath(const Decl *d) : Union(d) {} + StableSerializationPath(ExternalPath ext) : Union(ext) {} + + explicit operator bool() const { return !Union.empty(); } + + bool isSwiftDecl() const { return Union.isa(); } + const Decl *getSwiftDecl() const { + assert(isSwiftDecl()); + return Union.get(); + } + + bool isExternalPath() const { return Union.isa(); } + const ExternalPath &getExternalPath() const { + assert(isExternalPath()); + return Union.get(); + } + + SWIFT_DEBUG_DUMP; + void dump(raw_ostream &os) const; +}; + class ClangModuleLoader : public ModuleLoader { private: virtual void anchor(); @@ -111,6 +177,41 @@ class ClangModuleLoader : public ModuleLoader { /// Print the Clang type. virtual void printClangType(const clang::Type *type, llvm::raw_ostream &os) const = 0; + + /// Try to find a stable serialization path for the given declaration, + /// if there is one. + virtual StableSerializationPath + findStableSerializationPath(const clang::Decl *decl) const = 0; + + /// Try to resolve a stable serialization path down to the original + /// declaration. + virtual const clang::Decl * + resolveStableSerializationPath(const StableSerializationPath &path) const = 0; + + /// Determine whether the given type is serializable. + /// + /// If \c checkCanonical is true, checks the canonical type, + /// not the given possibly-sugared type. In general: + /// - non-canonical representations should be preserving the + /// sugared type even if it isn't serializable, since that + /// maintains greater source fidelity; + /// - semantic checks need to be checking the serializability + /// of the canonical type, since it's always potentially + /// necessary to serialize that (e.g. in SIL); and + /// - serializers can try to serialize the sugared type to + /// maintain source fidelity and just fall back on the canonical + /// type if that's not possible. + /// + /// The expectation here is that this predicate is meaningful + /// independent of the actual form of serialization: the types + /// that we can't reliably binary-serialize without an absolute + /// Clang AST cross-reference are the same types that won't + /// reliably round-trip through a textual format. At the very + /// least, it's probably best to use conservative predicates + /// that work both ways so that language behavior doesn't differ + /// based on subtleties like the target module interface format. + virtual bool isSerializable(const clang::Type *type, + bool checkCanonical) const = 0; }; } // namespace swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 945e7c94fd301..97a5388345962 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2660,6 +2660,10 @@ ERROR(assoc_conformance_from_implementation_only_module,none, "cannot use conformance of %0 to %1 in associated type %3 (inferred as " "%4); %2 has been imported as implementation-only", (Type, DeclName, Identifier, Type, Type)) +ERROR(unexportable_clang_function_type,none, + "cannot export the underlying C type of the function type %0; " + "it may use anonymous types or types defined outside of a module", + (Type)) WARNING(warn_implementation_only_conflict,none, "%0 inconsistently imported as implementation-only", diff --git a/include/swift/AST/PrettyStackTrace.h b/include/swift/AST/PrettyStackTrace.h index b18b8c7a3fa41..0714bd6c83e96 100644 --- a/include/swift/AST/PrettyStackTrace.h +++ b/include/swift/AST/PrettyStackTrace.h @@ -24,6 +24,10 @@ #include "swift/AST/Identifier.h" #include "swift/AST/Type.h" +namespace clang { + class Type; +} + namespace swift { class ASTContext; class Decl; @@ -132,6 +136,17 @@ class PrettyStackTraceType : public llvm::PrettyStackTraceEntry { virtual void print(llvm::raw_ostream &OS) const; }; +/// PrettyStackTraceClangType - Observe that we are processing a +/// specific Clang type. +class PrettyStackTraceClangType : public llvm::PrettyStackTraceEntry { + const clang::Type *TheType; + const char *Action; +public: + PrettyStackTraceClangType(const char *action, const clang::Type *type) + : TheType(type), Action(action) {} + virtual void print(llvm::raw_ostream &OS) const; +}; + /// Observe that we are processing a specific type representation. class PrettyStackTraceTypeRepr : public llvm::PrettyStackTraceEntry { ASTContext &Context; diff --git a/include/swift/ClangImporter/ClangImporter.h b/include/swift/ClangImporter/ClangImporter.h index 9ac0d73ef7b9b..c736cdd2a9473 100644 --- a/include/swift/ClangImporter/ClangImporter.h +++ b/include/swift/ClangImporter/ClangImporter.h @@ -422,6 +422,16 @@ class ClangImporter final : public ClangModuleLoader { SourceLoc loc) const override; void printClangType(const clang::Type *type, llvm::raw_ostream &os) const override; + + StableSerializationPath + findStableSerializationPath(const clang::Decl *decl) const override; + + const clang::Decl * + resolveStableSerializationPath( + const StableSerializationPath &path) const override; + + bool isSerializable(const clang::Type *type, + bool checkCanonical) const override; }; ImportDecl *createImportDecl(ASTContext &Ctx, DeclContext *DC, ClangNode ClangN, diff --git a/include/swift/ClangImporter/SwiftAbstractBasicReader.h b/include/swift/ClangImporter/SwiftAbstractBasicReader.h new file mode 100644 index 0000000000000..f75f7261e0e60 --- /dev/null +++ b/include/swift/ClangImporter/SwiftAbstractBasicReader.h @@ -0,0 +1,95 @@ +//===- SwiftAbstractBasicReader.h - Clang serialization adapter -*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 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 provides an intermediate CRTP class which implements most of +// Clang's AbstractBasicReader interface, paralleling the behavior defined +// in SwiftAbstractBasicWriter. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_CLANGIMPORTER_SWIFTABSTRACTBASICREADER_H +#define SWIFT_CLANGIMPORTER_SWIFTABSTRACTBASICREADER_H + +#include "clang/AST/AbstractTypeReader.h" + +// This include is required to instantiate the template code in +// AbstractBasicReader.h, i.e. it's a workaround to an include-what-you-use +// violation. +#include "clang/AST/DeclObjC.h" + +namespace swift { + +/// An implementation of Clang's AbstractBasicReader interface for a Swift +/// datastream-based reader. This is paired with the AbstractBasicWriter +/// implementation in SwiftAbstractBasicWriter.h. Note that the general +/// expectation is that the types and declarations involved will have passed +/// a serializability check when this is used for actual deserialization. +/// +/// The subclass must implement: +/// uint64_t readUInt64(); +/// clang::IdentifierInfo *readIdentifier(); +/// clang::Stmt *readStmtRef(); +/// clang::Decl *readDeclRef(); +template +class DataStreamBasicReader + : public clang::serialization::DataStreamBasicReader { + using super = clang::serialization::DataStreamBasicReader; +public: + using super::asImpl; + using super::getASTContext; + + DataStreamBasicReader(clang::ASTContext &ctx) : super(ctx) {} + + /// Perform all the calls necessary to write out the given type. + clang::QualType readTypeRef() { + auto kind = clang::Type::TypeClass(asImpl().readUInt64()); + return clang::serialization::AbstractTypeReader(asImpl()).read(kind); + } + + bool readBool() { + return asImpl().readUInt64() != 0; + } + + uint32_t readUInt32() { + return uint32_t(asImpl().readUInt64()); + } + + clang::Selector readSelector() { + uint64_t numArgsPlusOne = asImpl().readUInt64(); + + // The null case. + if (numArgsPlusOne == 0) + return clang::Selector(); + + unsigned numArgs = unsigned(numArgsPlusOne - 1); + SmallVector chunks; + for (unsigned i = 0, e = std::max(numArgs, 1U); i != e; ++i) + chunks.push_back(asImpl().readIdentifier()); + + return getASTContext().Selectors.getSelector(numArgs, chunks.data()); + } + + clang::SourceLocation readSourceLocation() { + // Always read null. + return clang::SourceLocation(); + } + + clang::QualType readQualType() { + clang::Qualifiers quals = asImpl().readQualifiers(); + clang::QualType type = asImpl().readTypeRef(); + return getASTContext().getQualifiedType(type, quals); + } +}; + +} + +#endif diff --git a/include/swift/ClangImporter/SwiftAbstractBasicWriter.h b/include/swift/ClangImporter/SwiftAbstractBasicWriter.h new file mode 100644 index 0000000000000..486bee2d01b21 --- /dev/null +++ b/include/swift/ClangImporter/SwiftAbstractBasicWriter.h @@ -0,0 +1,90 @@ +//===- SwiftAbstractBasicWriter.h - Clang serialization adapter -*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 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 provides an intermediate CRTP class which implements most of +// Clang's AbstractBasicWriter interface, allowing largely the same logic +// to be used for both the importer's "can this be serialized" checks and +// the serializer's actual serialization logic. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_CLANGIMPORTER_SWIFTABSTRACTBASICWRITER_H +#define SWIFT_CLANGIMPORTER_SWIFTABSTRACTBASICWRITER_H + +#include "clang/AST/AbstractTypeWriter.h" + +namespace swift { + +/// An implementation of Clang's AbstractBasicWriter interface for a Swift +/// datastream-based reader. This is paired with the AbstractBasicReader +/// implementation in SwiftAbstractBasicReader.h. Note that the general +/// expectation is that the types and declarations involved will have passed +/// a serializability check when this is used for actual serialization. +/// The code in this class is also used when implementing that +/// serializability check and so must be a little more cautious. +/// +/// The subclass must implement: +/// void writeUInt64(uint64_t value); +/// void writeIdentifier(const clang::IdentifierInfo *ident); +/// void writeStmtRef(const clang::Stmt *stmt); +/// void writeDeclRef(const clang::Decl *decl); +template +class DataStreamBasicWriter + : public clang::serialization::DataStreamBasicWriter { + using super = clang::serialization::DataStreamBasicWriter; +public: + using super::asImpl; + + /// Perform all the calls necessary to write out the given type. + void writeTypeRef(const clang::Type *type) { + asImpl().writeUInt64(uint64_t(type->getTypeClass())); + clang::serialization::AbstractTypeWriter(asImpl()).write(type); + } + + void writeBool(bool value) { + asImpl().writeUInt64(uint64_t(value)); + } + + void writeUInt32(uint32_t value) { + asImpl().writeUInt64(uint64_t(value)); + } + + void writeSelector(clang::Selector selector) { + if (selector.isNull()) { + asImpl().writeUInt64(0); + return; + } + + asImpl().writeUInt64(selector.getNumArgs() + 1); + for (unsigned i = 0, e = std::max(selector.getNumArgs(), 1U); i != e; ++i) + asImpl().writeIdentifier(selector.getIdentifierInfoForSlot(i)); + } + + void writeSourceLocation(clang::SourceLocation loc) { + // DataStreamBasicReader will always read null; the serializability + // check overrides this to complain about non-null source locations. + } + + void writeQualType(clang::QualType type) { + assert(!type.isNull()); + + auto split = type.split(); + asImpl().writeQualifiers(split.Quals); + + // Just recursively visit the given type. + asImpl().writeTypeRef(split.Ty); + } +}; + +} + +#endif diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index a5d494d2805df..fba8d7f465ecd 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -4391,6 +4391,18 @@ ASTContext::getClangFunctionType(ArrayRef params, return impl.Converter.getValue().getFunctionType(params, resultTy, trueRep); } +const Decl * +ASTContext::getSwiftDeclForExportedClangDecl(const clang::Decl *decl) { + auto &impl = getImpl(); + + // If we haven't exported anything yet, this must not be how we found + // this declaration. + if (!impl.Converter) return nullptr; + + return impl.Converter->getSwiftDeclForExportedClangDecl(decl); +} + + CanGenericSignature ASTContext::getSingleGenericParameterSignature() const { if (auto theSig = getImpl().SingleGenericParameterSignature) return theSig; diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index dd93348812558..2a34900d480df 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -17,6 +17,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/ASTPrinter.h" #include "swift/AST/ASTVisitor.h" +#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/Initializer.h" @@ -3808,3 +3809,44 @@ StringRef swift::getAccessorKindString(AccessorKind value) { llvm_unreachable("Unhandled AccessorKind in switch."); } + +void StableSerializationPath::dump() const { + dump(llvm::errs()); +} + +static StringRef getExternalPathComponentKindString( + StableSerializationPath::ExternalPath::ComponentKind kind) { + switch (kind) { +#define CASE(ID, STRING) \ + case StableSerializationPath::ExternalPath::ID: return STRING; + CASE(Record, "record") + CASE(Enum, "enum") + CASE(Namespace, "namespace") + CASE(Typedef, "typedef") + CASE(TypedefAnonDecl, "anonymous tag") + CASE(ObjCInterface, "@interface") + CASE(ObjCProtocol, "@protocol") +#undef CASE + } + llvm_unreachable("bad kind"); +} + +void StableSerializationPath::dump(llvm::raw_ostream &os) const { + if (isSwiftDecl()) { + os << "clang decl of:\n"; + getSwiftDecl()->dump(os, 2); + } else { + auto &path = getExternalPath(); + using ExternalPath = StableSerializationPath::ExternalPath; + os << "external path: "; + size_t index = 0; + for (auto &entry : path.Path) { + if (index++) os << " -> "; + os << getExternalPathComponentKindString(entry.first); + if (ExternalPath::requiresIdentifier(entry.first)) { + os << "(" << entry.second << ")"; + } + } + os << "\n"; + } +} diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index eeabdf4de8a65..45cc7d59752a5 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3529,24 +3529,7 @@ class TypePrinter : public TypeVisitor { return; } - bool isSimple = T->hasSimpleTypeRepr(); - if (!isSimple && T->is()) { - auto opaqueTy = T->castTo(); - switch (Options.OpaqueReturnTypePrinting) { - case PrintOptions::OpaqueReturnTypePrintingMode::StableReference: - case PrintOptions::OpaqueReturnTypePrintingMode::Description: - isSimple = true; - break; - case PrintOptions::OpaqueReturnTypePrintingMode::WithOpaqueKeyword: - isSimple = false; - break; - case PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword: { - isSimple = opaqueTy->getExistentialType()->hasSimpleTypeRepr(); - break; - } - } - } - + bool isSimple = isSimpleUnderPrintOptions(T); if (isSimple) { visit(T); } else { @@ -3556,6 +3539,28 @@ class TypePrinter : public TypeVisitor { } } + /// Determinee whether the given type has a simple representation + /// under the current print options. + bool isSimpleUnderPrintOptions(Type T) { + if (auto typealias = dyn_cast(T.getPointer())) { + if (shouldDesugarTypeAliasType(typealias)) + return isSimpleUnderPrintOptions(typealias->getSinglyDesugaredType()); + } else if (auto opaque = + dyn_cast(T.getPointer())) { + switch (Options.OpaqueReturnTypePrinting) { + case PrintOptions::OpaqueReturnTypePrintingMode::StableReference: + case PrintOptions::OpaqueReturnTypePrintingMode::Description: + return true; + case PrintOptions::OpaqueReturnTypePrintingMode::WithOpaqueKeyword: + return false; + case PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword: + return isSimpleUnderPrintOptions(opaque->getExistentialType()); + } + llvm_unreachable("bad opaque-return-type printing mode"); + } + return T->hasSimpleTypeRepr(); + } + template void printModuleContext(T *Ty) { FileUnit *File = cast(Ty->getDecl()->getModuleScopeContext()); @@ -3693,8 +3698,12 @@ class TypePrinter : public TypeVisitor { Printer << BUILTIN_TYPE_NAME_SILTOKEN; } + bool shouldDesugarTypeAliasType(TypeAliasType *T) { + return Options.PrintForSIL || Options.PrintTypeAliasUnderlyingType; + } + void visitTypeAliasType(TypeAliasType *T) { - if (Options.PrintForSIL || Options.PrintTypeAliasUnderlyingType) { + if (shouldDesugarTypeAliasType(T)) { visit(T->getSinglyDesugaredType()); return; } diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index 56b07d0dcd17b..14b9aec5f289f 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -397,6 +397,8 @@ clang::QualType ClangTypeConverter::visitProtocolType(ProtocolType *type) { PDecl->getASTContext(), proto->getObjCRuntimeName(runtimeNameBuffer))); + registerExportedClangDecl(proto, PDecl); + auto clangType = clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy, &PDecl, 1); return clangCtx.getObjCObjectPointerType(clangType); @@ -446,6 +448,8 @@ clang::QualType ClangTypeConverter::visitClassType(ClassType *type) { CDecl->getASTContext(), swiftDecl->getObjCRuntimeName(runtimeNameBuffer))); + registerExportedClangDecl(swiftDecl, CDecl); + auto clangType = clangCtx.getObjCInterfaceType(CDecl); return clangCtx.getObjCObjectPointerType(clangType); } @@ -726,3 +730,19 @@ clang::QualType ClangTypeConverter::convert(Type type) { Cache.insert({type, result}); return result; } + +void ClangTypeConverter::registerExportedClangDecl(Decl *swiftDecl, + const clang::Decl *clangDecl) { + assert(clangDecl->isCanonicalDecl() && + "generated Clang declaration for Swift declaration should not " + "have multiple declarations"); + ReversedExportMap.insert({clangDecl, swiftDecl}); +} + +Decl *ClangTypeConverter::getSwiftDeclForExportedClangDecl( + const clang::Decl *decl) const { + // We don't need to canonicalize the declaration because these exported + // declarations are never redeclarations. + auto it = ReversedExportMap.find(decl); + return (it != ReversedExportMap.end() ? it->second : nullptr); +} diff --git a/lib/AST/ClangTypeConverter.h b/lib/AST/ClangTypeConverter.h index b25f6b6dffb92..450b9e30cf19a 100644 --- a/lib/AST/ClangTypeConverter.h +++ b/lib/AST/ClangTypeConverter.h @@ -33,6 +33,7 @@ class ClangTypeConverter : using super = TypeVisitor; llvm::DenseMap Cache; + llvm::DenseMap ReversedExportMap; bool StdlibTypesAreCached = false; @@ -73,11 +74,19 @@ class ClangTypeConverter : ArrayRef params, Type resultTy, AnyFunctionType::Representation repr); + /// Check whether the given Clang declaration is an export of a Swift + /// declaration introduced by this converter, and if so, return the original + /// Swift declaration. + Decl *getSwiftDeclForExportedClangDecl(const clang::Decl *decl) const; + private: clang::QualType convert(Type type); clang::QualType convertMemberType(NominalTypeDecl *DC, StringRef memberName); + void registerExportedClangDecl(Decl *swiftDecl, + const clang::Decl *clangDecl); + clang::QualType reverseBuiltinTypeMapping(StructType *type); friend TypeVisitor; diff --git a/lib/AST/PrettyStackTrace.cpp b/lib/AST/PrettyStackTrace.cpp index 893a83502d1fd..750e661c66af1 100644 --- a/lib/AST/PrettyStackTrace.cpp +++ b/lib/AST/PrettyStackTrace.cpp @@ -27,6 +27,7 @@ #include "swift/AST/TypeRepr.h" #include "swift/AST/TypeVisitor.h" #include "swift/Basic/SourceManager.h" +#include "clang/AST/Type.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/MemoryBuffer.h" @@ -209,6 +210,15 @@ void swift::printTypeDescription(llvm::raw_ostream &out, Type type, if (addNewline) out << '\n'; } +void PrettyStackTraceClangType::print(llvm::raw_ostream &out) const { + out << "While " << Action << ' '; + if (TheType == nullptr) { + out << "NULL clang type!\n"; + return; + } + TheType->dump(out); +} + void PrettyStackTraceTypeRepr::print(llvm::raw_ostream &out) const { out << "While " << Action << " type "; TheType->print(out); diff --git a/lib/ClangImporter/CMakeLists.txt b/lib/ClangImporter/CMakeLists.txt index 6417184633427..d1fb2a6f8d0a0 100644 --- a/lib/ClangImporter/CMakeLists.txt +++ b/lib/ClangImporter/CMakeLists.txt @@ -17,6 +17,7 @@ add_swift_host_library(swiftClangImporter STATIC ImportMacro.cpp ImportName.cpp ImportType.cpp + Serializability.cpp SwiftLookupTable.cpp ) target_link_libraries(swiftClangImporter PRIVATE diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 517fde6fce0bc..f39a1194c7ce1 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -963,6 +963,22 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation bool isOverAligned(const clang::TypeDecl *typeDecl); bool isOverAligned(clang::QualType type); + /// Determines whether the given Clang type is serializable in a + /// Swift AST. This should only be called after successfully importing + /// the type, because it will look for a stable serialization path for any + /// referenced declarations, which may depend on whether there's a known + /// import of it. (It will not try to import the declaration to avoid + /// circularity problems.) + /// + /// Note that this will only check the requested sugaring of the given + /// type (depending on \c checkCanonical); the canonical type may be + /// serializable even if the non-canonical type is not, or vice-versa. + bool isSerializable(clang::QualType type, bool checkCanonical); + + /// Try to find a stable Swift serialization path for the given Clang + /// declaration. + StableSerializationPath findStableSerializationPath(const clang::Decl *decl); + /// Look up and attempt to import a Clang declaration with /// the given name. Decl *importDeclByName(StringRef name); diff --git a/lib/ClangImporter/Serializability.cpp b/lib/ClangImporter/Serializability.cpp new file mode 100644 index 0000000000000..2606964d6d078 --- /dev/null +++ b/lib/ClangImporter/Serializability.cpp @@ -0,0 +1,329 @@ +//===--- Serializability.cpp - Swift serializability of Clang AST refs ----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 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 support for working with StableSerializationPaths +// and determining whether references to Clang declarations and types are +// serializable. +// +// The expectation here is that the same basic predicates are +// interesting for both binary (.swiftmodule) and textual +// (.swiftinterface) serialization. For textual serialization, the +// key question is whether a printed representation will round-trip. +// +//===----------------------------------------------------------------------===// + +#include "ImporterImpl.h" +#include "swift/ClangImporter/SwiftAbstractBasicWriter.h" + +using namespace swift; + +using ExternalPath = StableSerializationPath::ExternalPath; + +static bool isSameDecl(const clang::Decl *lhs, const clang::Decl *rhs) { + return lhs == rhs || lhs->getCanonicalDecl() == rhs->getCanonicalDecl(); +} + +namespace { +class SerializationPathFinder { + ClangImporter::Implementation &Impl; +public: + SerializationPathFinder(ClangImporter::Implementation &impl) : Impl(impl) {} + + StableSerializationPath find(const clang::Decl *decl) { + // We can't do anything with non-NamedDecl declarations. + auto named = dyn_cast(decl); + if (!named) return StableSerializationPath(); + + if (decl->isFromASTFile()) { + return findImportedPath(named); + } + + // If the declaration isn't from an AST file, it might be something that + // we built automatically when exporting a Swift type. + if (auto swiftDecl = + Impl.SwiftContext.getSwiftDeclForExportedClangDecl(decl)) + return swiftDecl; + + // Otherwise we have no way to find it. + return StableSerializationPath(); + } + +private: + Identifier getIdentifier(const clang::IdentifierInfo *clangIdent) { + return Impl.SwiftContext.getIdentifier(clangIdent->getName()); + } + + StableSerializationPath findImportedPath(const clang::NamedDecl *decl) { + // We've almost certainly imported this declaration, look for it. + if (auto swiftDecl = Impl.importDeclCached(decl, Impl.CurrentVersion)) { + // The serialization code doesn't allow us to cross-reference + // typealias declarations directly. We could fix that, but it's + // easier to just avoid doing so and fall into the external-path code. + if (!isa(swiftDecl)) { + // Only accept this declaration if it round-trips. + if (auto swiftClangDecl = swiftDecl->getClangDecl()) + if (isSameDecl(decl, swiftClangDecl)) + return swiftDecl; + } + } + + // Otherwise, check to see if it's something we can easily find. + ExternalPath path; + if (findExternalPath(decl, path)) + return std::move(path); + + // Otherwise we have no way to find it. + return StableSerializationPath(); + } + + bool findExternalPath(const clang::NamedDecl *decl, ExternalPath &path) { + if (auto tag = dyn_cast(decl)) + return findExternalPath(tag, path); + if (auto alias = dyn_cast(decl)) + return findExternalPath(alias, path); + if (auto proto = dyn_cast(decl)) + return findExternalPath(proto, path); + if (auto iface = dyn_cast(decl)) + return findExternalPath(iface, path); + return false; + } + + bool findExternalPath(const clang::TagDecl *decl, ExternalPath &path) { + // We can't handle class template specializations right now. + if (isa(decl)) + return false; + + // Named tags are straightforward. + if (auto name = decl->getIdentifier()) { + if (!findExternalPath(decl->getDeclContext(), path)) return false; + path.add(decl->isEnum() ? ExternalPath::Enum : ExternalPath::Record, + getIdentifier(name)); + return true; + } + + // We can handle anonymous tags if they're defined in an obvious + // position in a typedef. + if (auto alias = decl->getTypedefNameForAnonDecl()) { + auto aliasTag = alias->getAnonDeclWithTypedefName(/*any*/true); + if (aliasTag && isSameDecl(decl, aliasTag)) { + if (!findExternalPath(alias, path)) return false; + path.add(ExternalPath::TypedefAnonDecl, Identifier()); + return true; + } + } + + // Otherwise we're stuck. + return false; + } + + bool findExternalPath(const clang::TypedefNameDecl *decl, + ExternalPath &path) { + auto name = decl->getIdentifier(); + if (!name) return false; + if (!findExternalPath(decl->getDeclContext(), path)) return false; + path.add(ExternalPath::Typedef, getIdentifier(name)); + return true; + } + + bool findExternalPath(const clang::ObjCProtocolDecl *decl, + ExternalPath &path) { + auto name = decl->getIdentifier(); + if (!name) return false; + path.add(ExternalPath::ObjCProtocol, getIdentifier(name)); + return true; + } + + bool findExternalPath(const clang::ObjCInterfaceDecl *decl, + ExternalPath &path) { + auto name = decl->getIdentifier(); + if (!name) return false; + path.add(ExternalPath::ObjCInterface, getIdentifier(name)); + return true; + } + + bool findExternalPath(const clang::DeclContext *dc, ExternalPath &path) { + // If we've reached the translation unit, we're done. + if (isa(dc)) + return true; + + // Linkage specifications don't contribute to the path. + if (isa(dc)) + return findExternalPath(dc->getParent(), path); + + // Handle namespaces. + if (auto ns = dyn_cast(dc)) { + // Don't try to handle anonymous namespaces. + auto name = ns->getIdentifier(); + if (!name) return false; + + // Drill to the parent. + if (!findExternalPath(dc->getParent(), path)) return false; + + path.Path.push_back({ExternalPath::Namespace, getIdentifier(name)}); + return true; + } + + // Handle types. + if (auto tag = dyn_cast(dc)) + return findExternalPath(tag, path); + + // Can't handle anything else. + return false; + } +}; +} // end anonymous namespace + + +StableSerializationPath +ClangImporter::findStableSerializationPath(const clang::Decl *decl) const { + return Impl.findStableSerializationPath(decl); +} + +StableSerializationPath +ClangImporter::Implementation::findStableSerializationPath( + const clang::Decl *decl) { + return SerializationPathFinder(*this).find(decl); +} + +const clang::Decl * +ClangImporter::resolveStableSerializationPath( + const StableSerializationPath &path) const { + if (!path) return nullptr; + + if (path.isSwiftDecl()) { + return path.getSwiftDecl()->getClangDecl(); + } + + auto &extpath = path.getExternalPath(); + auto &clangCtx = getClangASTContext(); + + const clang::Decl *decl = nullptr; + + // Perform a lookup in the current context (`decl` if set, and + // otherwise the translation unit). + auto lookup = [&](Identifier name) -> clang::DeclContext::lookup_result { + if (name.empty()) return clang::DeclContext::lookup_result(); + + const clang::DeclContext *dc; + if (decl) { + dc = dyn_cast(decl); + if (!dc) return clang::DeclContext::lookup_result(); + } else { + dc = clangCtx.getTranslationUnitDecl(); + } + + auto ident = &clangCtx.Idents.get(name.str()); + return dc->lookup(ident); + }; + + for (auto step : extpath.Path) { + // Handle the non-lookup steps here. + if (step.first == ExternalPath::TypedefAnonDecl) { + if (auto alias = dyn_cast_or_null(decl)) + return alias->getAnonDeclWithTypedefName(); + return nullptr; + } + + assert(ExternalPath::requiresIdentifier(step.first) && + "should've handled all non-lookup kinds above"); + + const clang::Decl *resultDecl = nullptr; + for (auto lookupDecl : lookup(step.second)) { + auto isAcceptable = [](const clang::Decl *decl, + ExternalPath::ComponentKind kind) { + switch (kind) { + case ExternalPath::Record: + return isa(decl); + case ExternalPath::Enum: + return isa(decl); + case ExternalPath::Namespace: + return isa(decl); + case ExternalPath::Typedef: + return isa(decl); + case ExternalPath::ObjCInterface: + return isa(decl); + case ExternalPath::ObjCProtocol: + return isa(decl); + case ExternalPath::TypedefAnonDecl: + llvm_unreachable("should have been filtered above"); + } + llvm_unreachable("bad kind"); + }; + + // Ignore unacceptable declarations. + if (!isAcceptable(lookupDecl, step.first)) + continue; + + // Bail out if we find multiple matching declarations. + // TODO: make an effort to filter by the target module? + if (resultDecl && !isSameDecl(resultDecl, lookupDecl)) + return nullptr; + + resultDecl = lookupDecl; + } + + // Bail out if lookup found nothing. + if (!resultDecl) return nullptr; + + decl = resultDecl; + } + + return decl; +} + +namespace { + /// The logic here for the supported cases must match the logic in + /// ClangToSwiftBasicWriter in Serialization.cpp. + struct ClangTypeSerializationChecker : + DataStreamBasicWriter { + ClangImporter::Implementation &Impl; + bool IsSerializable = true; + + ClangTypeSerializationChecker(ClangImporter::Implementation &impl) + : Impl(impl) {} + + void writeUInt64(uint64_t value) {} + void writeIdentifier(const clang::IdentifierInfo *ident) {} + void writeStmtRef(const clang::Stmt *stmt) { + if (stmt != nullptr) + IsSerializable = false; + } + void writeDeclRef(const clang::Decl *decl) { + if (decl && !Impl.findStableSerializationPath(decl)) + IsSerializable = false; + } + void writeSourceLocation(clang::SourceLocation loc) { + // If a source location is written into a type, it's likely to be + // something like the location of a VLA which we shouldn't simply + // replace with a meaningless location. + if (loc.isValid()) + IsSerializable = false; + } + }; +} + +bool ClangImporter::isSerializable(const clang::Type *type, + bool checkCanonical) const { + return Impl.isSerializable(clang::QualType(type, 0), checkCanonical); +} + +bool ClangImporter::Implementation::isSerializable(clang::QualType type, + bool checkCanonical) { + if (checkCanonical) + type = getClangASTContext().getCanonicalType(type); + + // Make a pass over the type as if we were serializing it, flagging + // anything that we can't stably serialize. + ClangTypeSerializationChecker checker(*this); + checker.writeQualType(type); + return checker.IsSerializable; +} diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index bc6e859d74ab7..c2bf7c8a00d39 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -18,6 +18,7 @@ #include "TypeAccessScopeChecker.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" +#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Pattern.h" @@ -1534,6 +1535,24 @@ class ExportabilityChecker : public DeclVisitor { visitSubstitutionMap(ty->getSubstitutionMap()); return Action::Continue; } + + // We diagnose unserializable Clang function types in the + // post-visitor so that we diagnose any unexportable component + // types first. + Action walkToTypePost(Type T) override { + if (auto fnType = T->getAs()) { + if (auto clangType = fnType->getClangFunctionType()) { + auto loader = T->getASTContext().getClangModuleLoader(); + // Serialization will serialize the sugared type if it can, + // but we need the canonical type to be serializable or else + // canonicalization (e.g. in SIL) might break things. + if (!loader->isSerializable(clangType, /*check canonical*/ true)) { + diagnoser.diagnoseClangFunctionType(T, clangType); + } + } + } + return TypeDeclFinder::walkToTypePost(T); + } }; type.walk(ProblematicTypeFinder(SF, diagnoser)); @@ -1605,6 +1624,10 @@ class ExportabilityChecker : public DeclVisitor { offendingConformance->getProtocol()->getFullName(), static_cast(reason), M->getName()); } + + void diagnoseClangFunctionType(Type fnType, const clang::Type *type) const { + D->diagnose(diag::unexportable_clang_function_type, fnType); + } }; Diagnoser getDiagnoser(const Decl *D, Reason reason = Reason::General) { diff --git a/lib/Serialization/DeclTypeRecordNodes.def b/lib/Serialization/DeclTypeRecordNodes.def index 70cb3c8b8de68..3bb2f0bc6fcee 100644 --- a/lib/Serialization/DeclTypeRecordNodes.def +++ b/lib/Serialization/DeclTypeRecordNodes.def @@ -190,6 +190,8 @@ OTHER(SELF_PROTOCOL_CONFORMANCE, 251) OTHER(XREF_OPAQUE_RETURN_TYPE_PATH_PIECE, 252) +OTHER(CLANG_TYPE, 253) + #undef RECORD #undef DECLTYPERECORDNODES_HAS_RECORD_VAL #undef RECORD_VAL diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 78d5bed929e30..6f83c62ceacbd 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -30,6 +30,7 @@ #include "swift/AST/TypeCheckRequests.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/ClangImporter/ClangModule.h" +#include "swift/ClangImporter/SwiftAbstractBasicReader.h" #include "swift/Serialization/SerializedModuleLoader.h" #include "swift/Basic/Defer.h" #include "swift/Basic/Statistic.h" @@ -227,6 +228,24 @@ getActualDefaultArgKind(uint8_t raw) { return None; } +static Optional +getActualClangDeclPathComponentKind(uint64_t raw) { + switch (static_cast(raw)) { +#define CASE(ID) \ + case serialization::ClangDeclPathComponentKind::ID: \ + return StableSerializationPath::ExternalPath::ID; + CASE(Record) + CASE(Enum) + CASE(Namespace) + CASE(Typedef) + CASE(TypedefAnonDecl) + CASE(ObjCInterface) + CASE(ObjCProtocol) +#undef CASE + } + return None; +} + ParameterList *ModuleFile::readParameterList() { using namespace decls_block; @@ -4784,19 +4803,19 @@ class TypeDeserializer { uint8_t rawRepresentation, rawDiffKind; bool noescape = false, throws; GenericSignature genericSig; - clang::Type *clangFunctionType = nullptr; + TypeID clangTypeID; - // FIXME: [clang-function-type-serialization] Deserialize a clang::Type out - // of the record. if (!isGeneric) { decls_block::FunctionTypeLayout::readRecord( - scratch, resultID, rawRepresentation, noescape, throws, rawDiffKind); + scratch, resultID, rawRepresentation, clangTypeID, + noescape, throws, rawDiffKind); } else { GenericSignatureID rawGenericSig; decls_block::GenericFunctionTypeLayout::readRecord( scratch, resultID, rawRepresentation, throws, rawDiffKind, rawGenericSig); genericSig = MF.getGenericSignature(rawGenericSig); + clangTypeID = 0; } auto representation = getActualFunctionTypeRepresentation(rawRepresentation); @@ -4807,6 +4826,14 @@ class TypeDeserializer { if (!diffKind.hasValue()) MF.fatal(); + const clang::Type *clangFunctionType = nullptr; + if (clangTypeID) { + auto loadedClangType = MF.getClangType(clangTypeID); + if (!loadedClangType) + return loadedClangType.takeError(); + clangFunctionType = loadedClangType.get(); + } + auto info = FunctionType::ExtInfo(*representation, noescape, throws, *diffKind, clangFunctionType); @@ -5144,10 +5171,8 @@ class TypeDeserializer { GenericSignatureID rawGenericSig; SubstitutionMapID rawSubs; ArrayRef variableData; - clang::FunctionType *clangFunctionType = nullptr; + ClangTypeID clangFunctionTypeID; - // FIXME: [clang-function-type-serialization] Deserialize a - // clang::FunctionType out of the record. decls_block::SILFunctionTypeLayout::readRecord(scratch, rawCoroutineKind, rawCalleeConvention, @@ -5162,6 +5187,7 @@ class TypeDeserializer { isGenericSignatureImplied, rawGenericSig, rawSubs, + clangFunctionTypeID, variableData); // Process the ExtInfo. @@ -5174,6 +5200,18 @@ class TypeDeserializer { if (!diffKind.hasValue()) MF.fatal(); + const clang::FunctionType *clangFunctionType = nullptr; + if (clangFunctionTypeID) { + auto clangType = MF.getClangType(clangFunctionTypeID); + if (!clangType) + return clangType.takeError(); + // FIXME: allow block pointers here. + clangFunctionType = + dyn_cast_or_null(clangType.get()); + if (!clangFunctionType) + MF.fatal(); + } + SILFunctionType::ExtInfo extInfo(*representation, pseudogeneric, noescape, *diffKind, clangFunctionType); @@ -5454,6 +5492,116 @@ Expected TypeDeserializer::getTypeCheckedImpl() { } } +namespace { + +class SwiftToClangBasicReader : + public swift::DataStreamBasicReader { + + ModuleFile &MF; + ClangModuleLoader &ClangLoader; + ArrayRef Record; + +public: + SwiftToClangBasicReader(ModuleFile &MF, ClangModuleLoader &clangLoader, + ArrayRef record) + : DataStreamBasicReader(clangLoader.getClangASTContext()), + MF(MF), ClangLoader(clangLoader), Record(record) {} + + uint64_t readUInt64() { + uint64_t value = Record[0]; + Record = Record.drop_front(); + return value; + } + + Identifier readSwiftIdentifier() { + return MF.getIdentifier(IdentifierID(readUInt64())); + } + + clang::IdentifierInfo *readIdentifier() { + Identifier swiftIdent = readSwiftIdentifier(); + return &getASTContext().Idents.get(swiftIdent.str()); + } + + clang::Stmt *readStmtRef() { + // Should only be allowed with null statements. + return nullptr; + } + + clang::Decl *readDeclRef() { + uint64_t refKind = readUInt64(); + + // Null reference. + if (refKind == 0) return nullptr; + + // Swift declaration. + if (refKind == 1) { + swift::Decl *swiftDecl = MF.getDecl(DeclID(readUInt64())); + return const_cast( + ClangLoader.resolveStableSerializationPath(swiftDecl)); + } + + // External path. + if (refKind == 2) { + using ExternalPath = StableSerializationPath::ExternalPath; + ExternalPath path; + uint64_t length = readUInt64(); + path.Path.reserve(length); + for (uint64_t i = 0; i != length; ++i) { + auto kind = getActualClangDeclPathComponentKind(readUInt64()); + if (!kind) return nullptr; + Identifier name = ExternalPath::requiresIdentifier(*kind) + ? readSwiftIdentifier() + : Identifier(); + path.add(*kind, name); + } + return const_cast( + ClangLoader.resolveStableSerializationPath(std::move(path))); + } + + // Unknown kind? + return nullptr; + } +}; + +} // end anonymous namespace + +llvm::Expected +ModuleFile::getClangType(ClangTypeID TID) { + if (TID == 0) + return nullptr; + + assert(TID <= ClangTypes.size() && "invalid type ID"); + auto &typeOrOffset = ClangTypes[TID-1]; + + if (typeOrOffset.isComplete()) + return typeOrOffset; + + BCOffsetRAII restoreOffset(DeclTypeCursor); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(typeOrOffset)); + + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclTypeCursor.advance()); + + if (entry.Kind != llvm::BitstreamEntry::Record) { + fatal(); + } + + SmallVector scratch; + StringRef blobData; + unsigned recordID = fatalIfUnexpected( + DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); + + if (recordID != decls_block::CLANG_TYPE) + fatal(); + + auto &clangLoader = *getContext().getClangModuleLoader(); + auto clangType = + SwiftToClangBasicReader(*this, clangLoader, scratch).readTypeRef() + .getTypePtr(); + typeOrOffset = clangType; + return clangType; +} + Decl *handleErrorAndSupplyMissingClassMember(ASTContext &context, llvm::Error &&error, ClassDecl *containingClass) { diff --git a/lib/Serialization/ModuleFile.cpp b/lib/Serialization/ModuleFile.cpp index 510a62db0c63b..c73d573629f08 100644 --- a/lib/Serialization/ModuleFile.cpp +++ b/lib/Serialization/ModuleFile.cpp @@ -986,6 +986,10 @@ bool ModuleFile::readIndexBlock(llvm::BitstreamCursor &cursor) { assert(blobData.empty()); allocateBuffer(Types, scratch); break; + case index_block::CLANG_TYPE_OFFSETS: + assert(blobData.empty()); + allocateBuffer(ClangTypes, scratch); + break; case index_block::IDENTIFIER_OFFSETS: assert(blobData.empty()); allocateBuffer(Identifiers, scratch); diff --git a/lib/Serialization/ModuleFile.h b/lib/Serialization/ModuleFile.h index 2ae1a88a3da06..0d45cfb47a65f 100644 --- a/lib/Serialization/ModuleFile.h +++ b/lib/Serialization/ModuleFile.h @@ -23,6 +23,7 @@ #include "swift/AST/TypeLoc.h" #include "swift/Serialization/Validation.h" #include "swift/Basic/LLVM.h" +#include "clang/AST/Type.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/TinyPtrVector.h" @@ -318,6 +319,9 @@ class ModuleFile /// Types referenced by this module. MutableArrayRef> Types; + /// Clang types referenced by this module. + MutableArrayRef> ClangTypes; + /// Generic signatures referenced by this module. MutableArrayRef> GenericSignatures; @@ -879,6 +883,10 @@ class ModuleFile /// Returns the type with the given ID, deserializing it if needed. llvm::Expected getTypeChecked(serialization::TypeID TID); + /// Returns the Clang type with the given ID, deserializing it if needed. + llvm::Expected + getClangType(serialization::ClangTypeID TID); + /// Returns the base name with the given ID, deserializing it if needed. DeclBaseName getDeclBaseName(serialization::IdentifierID IID); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index c2769a9c5ef97..f79836e2b9ba1 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -55,7 +55,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 535; // top-level var decls +const uint16_t SWIFTMODULE_VERSION_MINOR = 536; // Clang function types /// A standard hash seed used for all string hashes in a serialized module. /// @@ -71,6 +71,10 @@ using TypeIDField = DeclIDField; using TypeIDWithBitField = BCFixed<32>; +// ClangTypeID must be the same as DeclID because it is stored in the same way. +using ClangTypeID = TypeID; +using ClangTypeIDField = TypeIDField; + // IdentifierID must be the same as DeclID because it is stored in the same way. using IdentifierID = DeclID; using IdentifierIDField = DeclIDField; @@ -549,6 +553,18 @@ enum class ImportControl : uint8_t { }; using ImportControlField = BCFixed<2>; +// These IDs must \em not be renumbered or reordered without incrementing +// the module version. +enum class ClangDeclPathComponentKind : uint8_t { + Record = 0, + Enum, + Namespace, + Typedef, + TypedefAnonDecl, + ObjCInterface, + ObjCProtocol, +}; + // Encodes a VersionTuple: // // Major @@ -852,6 +868,11 @@ namespace decls_block { #include "DeclTypeRecordNodes.def" }; + using ClangTypeLayout = BCRecordLayout< + CLANG_TYPE, + BCArray> + >; + using BuiltinAliasTypeLayout = BCRecordLayout< BUILTIN_ALIAS_TYPE, DeclIDField, // typealias decl @@ -903,6 +924,7 @@ namespace decls_block { FUNCTION_TYPE, TypeIDField, // output FunctionTypeRepresentationField, // representation + ClangTypeIDField, // type BCFixed<1>, // noescape? BCFixed<1>, // throws? DifferentiabilityKindField // differentiability kind @@ -1001,6 +1023,7 @@ namespace decls_block { BCFixed<1>, // generic signature implied GenericSignatureIDField, // generic signature SubstitutionMapIDField, // substitutions + ClangTypeIDField, // clang function type, for foreign conventions BCArray // parameter types/conventions, alternating // followed by result types/conventions, alternating // followed by error result type/convention @@ -1914,7 +1937,8 @@ namespace index_block { ORDERED_TOP_LEVEL_DECLS, SUBSTITUTION_MAP_OFFSETS, - LastRecordKind = SUBSTITUTION_MAP_OFFSETS, + CLANG_TYPE_OFFSETS, + LastRecordKind = CLANG_TYPE_OFFSETS, }; constexpr const unsigned RecordIDFieldWidth = 5; diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 18abc58342f53..9dcf19486acf9 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -41,6 +41,7 @@ #include "swift/Basic/Version.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/ClangImporter/ClangModule.h" +#include "swift/ClangImporter/SwiftAbstractBasicWriter.h" #include "swift/Demangling/ManglingMacros.h" #include "swift/Serialization/SerializationOptions.h" #include "swift/Strings.h" @@ -69,6 +70,10 @@ using namespace llvm::support; using swift::version::Version; using llvm::BCBlockRAII; +ASTContext &SerializerBase::getASTContext() { + return M->getASTContext(); +} + /// Used for static_assert. static constexpr bool declIDFitsIn32Bits() { using Int32Info = std::numeric_limits; @@ -585,6 +590,29 @@ serialization::TypeID Serializer::addTypeRef(Type ty) { return TypesToSerialize.addRef(ty); } +serialization::ClangTypeID Serializer::addClangTypeRef(const clang::Type *ty) { + if (!ty) return 0; + + // Try to serialize the non-canonical type, but fall back to the + // canonical type if necessary. + auto loader = getASTContext().getClangModuleLoader(); + bool isSerializable; + if (loader->isSerializable(ty, false)) { + isSerializable = true; + } else if (!ty->isCanonicalUnqualified()) { + ty = ty->getCanonicalTypeInternal().getTypePtr(); + isSerializable = loader->isSerializable(ty, false); + } else { + isSerializable = false; + } + if (!isSerializable) { + PrettyStackTraceClangType trace("staging a serialized reference to", ty); + llvm::report_fatal_error("Clang function type is not serializable"); + } + + return ClangTypesToSerialize.addRef(ty); +} + IdentifierID Serializer::addDeclBaseNameRef(DeclBaseName ident) { switch (ident.getKind()) { case DeclBaseName::Kind::Normal: { @@ -745,6 +773,7 @@ void Serializer::writeBlockInfoBlock() { BLOCK_RECORD(index_block, LOCAL_DECL_CONTEXT_OFFSETS); BLOCK_RECORD(index_block, GENERIC_SIGNATURE_OFFSETS); BLOCK_RECORD(index_block, SUBSTITUTION_MAP_OFFSETS); + BLOCK_RECORD(index_block, CLANG_TYPE_OFFSETS); BLOCK_RECORD(index_block, LOCAL_TYPE_DECLS); BLOCK_RECORD(index_block, NORMAL_CONFORMANCE_OFFSETS); BLOCK_RECORD(index_block, SIL_LAYOUT_OFFSETS); @@ -1517,6 +1546,25 @@ getStableCtorInitializerKind(swift::CtorInitializerKind K){ llvm_unreachable("Unhandled CtorInitializerKind in switch."); } +static serialization::ClangDeclPathComponentKind +getStableClangDeclPathComponentKind( + StableSerializationPath::ExternalPath::ComponentKind kind) { + switch (kind) { +#define CASE(ID) \ + case StableSerializationPath::ExternalPath::ID: \ + return serialization::ClangDeclPathComponentKind::ID; + CASE(Record) + CASE(Enum) + CASE(Namespace) + CASE(Typedef) + CASE(TypedefAnonDecl) + CASE(ObjCInterface) + CASE(ObjCProtocol) +#undef CASE + } + llvm_unreachable("bad kind"); +} + void Serializer::writeCrossReference(const DeclContext *DC, uint32_t pathLen) { using namespace decls_block; @@ -4032,11 +4080,14 @@ class Serializer::TypeSerializer : public TypeVisitor { void visitFunctionType(const FunctionType *fnTy) { using namespace decls_block; - // FIXME: [clang-function-type-serialization] Serialize the clang type here + auto resultType = S.addTypeRef(fnTy->getResult()); + auto clangType = S.addClangTypeRef(fnTy->getClangFunctionType()); + unsigned abbrCode = S.DeclTypeAbbrCodes[FunctionTypeLayout::Code]; FunctionTypeLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, - S.addTypeRef(fnTy->getResult()), + resultType, getRawStableFunctionTypeRepresentation(fnTy->getRepresentation()), + clangType, fnTy->isNoEscape(), fnTy->throws(), getRawStableDifferentiabilityKind(fnTy->getDifferentiabilityKind())); @@ -4108,7 +4159,9 @@ class Serializer::TypeSerializer : public TypeVisitor { variableData.push_back(TypeID(conv)); } - auto sig = fnTy->getSubstGenericSignature(); + auto sigID = S.addGenericSignatureRef(fnTy->getSubstGenericSignature()); + auto substMapID = S.addSubstitutionMapRef(fnTy->getSubstitutions()); + auto clangTypeID = S.addClangTypeRef(fnTy->getClangFunctionType()); auto stableCoroutineKind = getRawStableSILCoroutineKind(fnTy->getCoroutineKind()); @@ -4127,9 +4180,7 @@ class Serializer::TypeSerializer : public TypeVisitor { stableDiffKind, fnTy->hasErrorResult(), fnTy->getParameters().size(), fnTy->getNumYields(), fnTy->getNumResults(), fnTy->isGenericSignatureImplied(), - S.addGenericSignatureRef(sig), - S.addSubstitutionMapRef(fnTy->getSubstitutions()), - variableData); + sigID, substMapID, clangTypeID, variableData); if (auto conformance = fnTy->getWitnessMethodConformanceOrInvalid()) S.writeConformance(conformance, S.DeclTypeAbbrCodes); @@ -4221,6 +4272,96 @@ void Serializer::writeASTBlockEntity(Type ty) { TypeSerializer(*this).visit(ty); } +namespace { +class ClangToSwiftBasicWriter : + public swift::DataStreamBasicWriter { + + Serializer &S; + SmallVectorImpl &Record; + using TypeWriter = + clang::serialization::AbstractTypeWriter; + TypeWriter Types; + + ClangModuleLoader *getClangLoader() { + return S.getASTContext().getClangModuleLoader(); + } + +public: + ClangToSwiftBasicWriter(Serializer &S, SmallVectorImpl &record) + : S(S), Record(record), Types(*this) {} + + void writeUInt64(uint64_t value) { + Record.push_back(value); + } + + void writeIdentifier(const clang::IdentifierInfo *value) { + IdentifierID id = 0; + if (value) { + id = S.addDeclBaseNameRef( + S.getASTContext().getIdentifier(value->getName())); + } + Record.push_back(id); + } + + void writeStmtRef(const clang::Stmt *stmt) { + // The deserializer should always read null, and isSerializable + // should be checking that we don't see a non-null statement here. + if (stmt) { + llvm::report_fatal_error("serializing a non-null Clang statement or" + " expression reference"); + } + } + + void writeDeclRef(const clang::Decl *decl) { + if (!decl) { + Record.push_back(/*no declaration*/ 0); + return; + } + + auto path = getClangLoader()->findStableSerializationPath(decl); + if (!path) { + decl->dump(llvm::errs()); + llvm::report_fatal_error("failed to find a stable Swift serialization" + " path for the above Clang declaration"); + } + + if (path.isSwiftDecl()) { + Record.push_back(/*swift declaration*/ 1); + Record.push_back(S.addDeclRef(path.getSwiftDecl())); + return; + } + + assert(path.isExternalPath()); + auto &ext = path.getExternalPath(); + Record.push_back(/*external path*/ 2); + Record.push_back(ext.Path.size()); + for (auto &elt : ext.Path) { + auto kind = elt.first; + auto stableKind = unsigned(getStableClangDeclPathComponentKind(kind)); + Record.push_back(stableKind); + if (ext.requiresIdentifier(kind)) + Record.push_back(S.addDeclBaseNameRef(elt.second)); + } + } +}; + +} + +void Serializer::writeASTBlockEntity(const clang::Type *ty) { + using namespace decls_block; + PrettyStackTraceClangType traceRAII("serializing clang type", ty); + assert(ClangTypesToSerialize.hasRef(ty)); + + // Serialize the type as an opaque sequence of data. + SmallVector typeData; + ClangToSwiftBasicWriter(*this, typeData).writeTypeRef(ty); + + // Write that in an opaque record. + unsigned abbrCode = DeclTypeAbbrCodes[ClangTypeLayout::Code]; + ClangTypeLayout::emitRecord(Out, ScratchRecord, abbrCode, + typeData); +} + template bool Serializer::writeASTBlockEntitiesIfNeeded( SpecificASTBlockRecordKeeper &entities) { @@ -4263,6 +4404,8 @@ void Serializer::writeAllDeclsAndTypes() { registerDeclTypeAbbr(); registerDeclTypeAbbr(); + registerDeclTypeAbbr(); + registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); @@ -4347,6 +4490,7 @@ void Serializer::writeAllDeclsAndTypes() { wroteSomething |= writeASTBlockEntitiesIfNeeded(DeclsToSerialize); wroteSomething |= writeASTBlockEntitiesIfNeeded(TypesToSerialize); + wroteSomething |= writeASTBlockEntitiesIfNeeded(ClangTypesToSerialize); wroteSomething |= writeASTBlockEntitiesIfNeeded(LocalDeclContextsToSerialize); wroteSomething |= @@ -4804,6 +4948,7 @@ void Serializer::writeAST(ModuleOrSourceFile DC, index_block::OffsetsLayout Offsets(Out); writeOffsets(Offsets, DeclsToSerialize); writeOffsets(Offsets, TypesToSerialize); + writeOffsets(Offsets, ClangTypesToSerialize); writeOffsets(Offsets, LocalDeclContextsToSerialize); writeOffsets(Offsets, GenericSignaturesToSerialize); writeOffsets(Offsets, SubstitutionMapsToSerialize); diff --git a/lib/Serialization/Serialization.h b/lib/Serialization/Serialization.h index 3e66e0ae4877a..a446d34e0b3c7 100644 --- a/lib/Serialization/Serialization.h +++ b/lib/Serialization/Serialization.h @@ -27,6 +27,10 @@ #include #include +namespace clang { + class Type; +} + namespace swift { class SILModule; @@ -63,6 +67,8 @@ class SerializerBase { public: SerializerBase(ArrayRef signature, ModuleOrSourceFile DC); + + ASTContext &getASTContext(); }; class Serializer : public SerializerBase { @@ -193,6 +199,10 @@ class Serializer : public SerializerBase { index_block::TYPE_OFFSETS> TypesToSerialize; + ASTBlockRecordKeeper + ClangTypesToSerialize; + ASTBlockRecordKeeper LocalDeclContextsToSerialize; @@ -313,6 +323,9 @@ class Serializer : public SerializerBase { /// Writes the given type. void writeASTBlockEntity(Type ty); + /// Writes the given Clang type. + void writeASTBlockEntity(const clang::Type *ty); + /// Writes a generic signature. void writeASTBlockEntity(GenericSignature sig); @@ -376,6 +389,13 @@ class Serializer : public SerializerBase { /// \returns The ID for the given Type in this module. TypeID addTypeRef(Type ty); + /// Records the use of the given C type. + /// + /// The type will be scheduled for serialization if necessary., + /// + /// \returns The ID for the given type in this module. + ClangTypeID addClangTypeRef(const clang::Type *ty); + /// Records the use of the given DeclBaseName. /// /// The Identifier will be scheduled for serialization if necessary. diff --git a/test/ClangImporter/unserializable-clang-function-types.swift b/test/ClangImporter/unserializable-clang-function-types.swift new file mode 100644 index 0000000000000..7332e437876cd --- /dev/null +++ b/test/ClangImporter/unserializable-clang-function-types.swift @@ -0,0 +1,6 @@ +// RUN: %target-swift-frontend -typecheck -swift-version 5 -emit-module-interface-path - -sdk %clang-importer-sdk -enable-library-evolution %s -experimental-print-full-convention -verify + +import ctypes + +public var f1 : UnserializableFunctionPointer? +// expected-error@-1 {{cannot export the underlying C type of the function type 'UnserializableFunctionPointer' (aka '@convention(c) () -> Optional'); it may use anonymous types or types defined outside of a module}} diff --git a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h index 4d4a7cf018575..5898fd4bbb9c4 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h @@ -224,6 +224,17 @@ typedef struct Dummy { Dummy * (*getFunctionPointer3(void))(Dummy *); +// These two function types should be serializable despite the struct +// declarations being incomplete and therefore (currently) unimportable. +typedef struct ForwardInTypedefForFP *OpaqueTypedefForFP; +typedef OpaqueTypedefForFP (*FunctionPointerReturningOpaqueTypedef)(void); + +typedef struct ForwardInTypedefForFP2 *OpaqueTypedefForFP2; +typedef OpaqueTypedefForFP2 (*FunctionPointerReturningOpaqueTypedef2)(void); + +// This will probably never be serializable. +typedef struct { int x; int y; } *(*UnserializableFunctionPointer)(void); + //===--- // Unions //===--- diff --git a/test/Sema/clang_types_in_ast.swift b/test/Sema/clang_types_in_ast.swift index f5bcbf6f37cfc..e087c0e66151a 100644 --- a/test/Sema/clang_types_in_ast.swift +++ b/test/Sema/clang_types_in_ast.swift @@ -8,9 +8,7 @@ // rdar://problem/57644243 : We shouldn't crash if -use-clang-function-types is not enabled. // RUN: %target-swift-frontend %s -typecheck -DNOCRASH3 -I %t -// FIXME: [clang-function-type-serialization] This should stop crashing once we -// start serializing clang function types. -// RUN: not --crash %target-swift-frontend %s -typecheck -DCRASH -I %t -use-clang-function-types +// RUN: %target-swift-frontend %s -typecheck -DCRASH -I %t -use-clang-function-types #if NOCRASH1 public func my_signal() -> Optional<@convention(c) (Int32) -> Void> { diff --git a/test/Serialization/Inputs/def_clang_function_types.swift b/test/Serialization/Inputs/def_clang_function_types.swift new file mode 100644 index 0000000000000..b5d28f25c0f8f --- /dev/null +++ b/test/Serialization/Inputs/def_clang_function_types.swift @@ -0,0 +1,15 @@ +import ctypes + +public var has_fp_type: FunctionPointerReturningOpaqueTypedef? + +public func use_inout(arg: inout T) {} + +@inlinable +public func use_fp_internally() { + // This currently bypasses the safety checks we do in export-checking + // because we don't check inlinable function bodies. It's plausible + // code, but more importantly it actually appears in the non-Darwin + // Foundation code. + var x: FunctionPointerReturningOpaqueTypedef2? = nil + use_inout(arg: &x) +} diff --git a/test/Serialization/clang-function-types.swift b/test/Serialization/clang-function-types.swift new file mode 100644 index 0000000000000..d85eadf358fe7 --- /dev/null +++ b/test/Serialization/clang-function-types.swift @@ -0,0 +1,30 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t %S/Inputs/def_clang_function_types.swift -use-clang-function-types +// RUN: llvm-bcanalyzer %t/def_clang_function_types.swiftmodule | %FileCheck -check-prefix=CHECK-BCANALYZER %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -use-clang-function-types -experimental-print-full-convention -emit-sil -sil-debug-serialization -I %t %s -O | %FileCheck %s + +import def_clang_function_types + +// CHECK-BCANALYZER-LABEL: (INDEX_BLOCK): +// CHECK-BCANALYZER: CLANG_TYPE_OFFSETS + +// CHECK-LABEL: sil hidden @$s4main5test1yyF +func test1() { + // FIXME: this mangling will have to change + // CHECK: global_addr @$s24def_clang_function_types11has_fp_types13OpaquePointerVSgyXCSgvp : $*Optional<@convention(c) () -> Optional> + let fp = has_fp_type + _ = fp?() +} +// CHECK-LABEL: } // end sil function '$s4main5test1yyF' + +// CHECK-LABEL: sil hidden @$s4main5test2yyF +func test2() { + use_fp_internally() +} +// CHECK-LABEL: } // end sil function '$s4main5test2yyF' + +// CHECK-LABEL: sil public_external [canonical] @$s24def_clang_function_types17use_fp_internallyyyF +// CHECK: enum $Optional<@convention(c) () -> Optional>, #Optional.none!enumelt +// CHECK: [[FN:%.*]] = function_ref @$s24def_clang_function_types9use_inout3argyxz_tlF : $@convention(thin) <τ_0_0> (@inout τ_0_0) -> () +// CHECK: apply [[FN]]<(@convention(c) () -> OpaquePointer?)?> +// CHECK-LABEL: } // end sil function '$s24def_clang_function_types17use_fp_internallyyyF'