From b3750a7c137aa411225e6ac7d558f0e51ee02c4a Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 30 Jan 2020 17:07:06 -0800 Subject: [PATCH 1/3] Add a PrettyStackTrace for working with Clang types. --- include/swift/AST/PrettyStackTrace.h | 15 +++++++++++++++ lib/AST/PrettyStackTrace.cpp | 10 ++++++++++ 2 files changed, 25 insertions(+) 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/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); From f17a70c57bdf5e920e4fb53df19b78c657d0942f Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 6 Feb 2020 16:28:16 -0500 Subject: [PATCH 2/3] Fix a bug with the printing of optional typealiases. --- lib/AST/ASTPrinter.cpp | 47 +++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 19 deletions(-) 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; } From faee21b626877b526241feb9788b76fc758b6b2e Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 30 Jan 2020 17:07:36 -0800 Subject: [PATCH 3/3] Implement Swift serialization and deserialization of Clang types. As part of this, we have to change the type export rules to prevent `@convention(c)` function types from being used in exported interfaces if they aren't serializable. This is a more conservative version of the original rule I had, which was to import such function-pointer types as opaque pointers. That rule would've completely prevented importing function-pointer types defined in bridging headers and so simply doesn't work, so we're left trying to catch the unsupportable cases retroactively. This has the unfortunate consequence that we can't necessarily serialize the internal state of the compiler, but that was already true due to normal type uses of aggregate types from bridging headers; if we can teach the compiler to reliably serialize such types, we should be able to use the same mechanisms for function types. This PR doesn't flip the switch to use Clang function types by default, so many of the clang-function-type-serialization FIXMEs are still in place. --- include/swift/AST/ASTContext.h | 4 + include/swift/AST/ClangModuleLoader.h | 101 ++++++ include/swift/AST/DiagnosticsSema.def | 4 + include/swift/ClangImporter/ClangImporter.h | 10 + .../ClangImporter/SwiftAbstractBasicReader.h | 95 +++++ .../ClangImporter/SwiftAbstractBasicWriter.h | 90 +++++ lib/AST/ASTContext.cpp | 12 + lib/AST/ASTDumper.cpp | 42 +++ lib/AST/ClangTypeConverter.cpp | 20 ++ lib/AST/ClangTypeConverter.h | 9 + lib/ClangImporter/CMakeLists.txt | 1 + lib/ClangImporter/ImporterImpl.h | 16 + lib/ClangImporter/Serializability.cpp | 329 ++++++++++++++++++ lib/Sema/TypeCheckAccess.cpp | 23 ++ lib/Serialization/DeclTypeRecordNodes.def | 2 + lib/Serialization/Deserialization.cpp | 162 ++++++++- lib/Serialization/ModuleFile.cpp | 4 + lib/Serialization/ModuleFile.h | 8 + lib/Serialization/ModuleFormat.h | 28 +- lib/Serialization/Serialization.cpp | 157 ++++++++- lib/Serialization/Serialization.h | 20 ++ .../unserializable-clang-function-types.swift | 6 + .../clang-importer-sdk/usr/include/ctypes.h | 11 + test/Sema/clang_types_in_ast.swift | 4 +- .../Inputs/def_clang_function_types.swift | 15 + test/Serialization/clang-function-types.swift | 30 ++ 26 files changed, 1185 insertions(+), 18 deletions(-) create mode 100644 include/swift/ClangImporter/SwiftAbstractBasicReader.h create mode 100644 include/swift/ClangImporter/SwiftAbstractBasicWriter.h create mode 100644 lib/ClangImporter/Serializability.cpp create mode 100644 test/ClangImporter/unserializable-clang-function-types.swift create mode 100644 test/Serialization/Inputs/def_clang_function_types.swift create mode 100644 test/Serialization/clang-function-types.swift 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/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/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/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'