Skip to content

Commit faee21b

Browse files
committed
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.
1 parent f17a70c commit faee21b

26 files changed

+1185
-18
lines changed

include/swift/AST/ASTContext.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,10 @@ class ASTContext final {
595595
const FunctionType::ExtInfo incompleteExtInfo,
596596
FunctionTypeRepresentation trueRep);
597597

598+
/// Get the Swift declaration that a Clang declaration was exported from,
599+
/// if applicable.
600+
const Decl *getSwiftDeclForExportedClangDecl(const clang::Decl *decl);
601+
598602
/// Determine whether the given Swift type is representable in a
599603
/// given foreign language.
600604
ForeignRepresentationInfo

include/swift/AST/ClangModuleLoader.h

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414
#define SWIFT_AST_CLANG_MODULE_LOADER_H
1515

1616
#include "swift/AST/ModuleLoader.h"
17+
#include "swift/Basic/TaggedUnion.h"
1718

1819
namespace clang {
1920
class ASTContext;
2021
class CompilerInstance;
22+
class Decl;
2123
class Preprocessor;
2224
class Sema;
2325
class TargetInfo;
@@ -26,6 +28,7 @@ class Type;
2628

2729
namespace swift {
2830

31+
class Decl;
2932
class DeclContext;
3033
class VisibleDeclConsumer;
3134

@@ -40,6 +43,69 @@ enum class ClangTypeKind {
4043
ObjCProtocol,
4144
};
4245

46+
/// A path for serializing a declaration.
47+
class StableSerializationPath {
48+
public:
49+
struct ExternalPath {
50+
enum ComponentKind {
51+
/// A named record type (but not a template specialization)
52+
Record,
53+
54+
/// A named enum type
55+
Enum,
56+
57+
/// A C++ namespace
58+
Namespace,
59+
60+
/// A typedef
61+
Typedef,
62+
63+
/// A typedef's anonymous tag declaration. Identifier is empty.
64+
TypedefAnonDecl,
65+
66+
/// An Objective-C interface.
67+
ObjCInterface,
68+
69+
/// An Objective-C protocol.
70+
ObjCProtocol,
71+
};
72+
73+
static bool requiresIdentifier(ComponentKind kind) {
74+
return kind != TypedefAnonDecl;
75+
}
76+
77+
SmallVector<std::pair<ComponentKind, Identifier>, 2> Path;
78+
79+
void add(ComponentKind kind, Identifier name) {
80+
Path.push_back({kind, name});
81+
}
82+
};
83+
private:
84+
TaggedUnion<void, const Decl *, ExternalPath> Union;
85+
86+
public:
87+
StableSerializationPath() {}
88+
StableSerializationPath(const Decl *d) : Union(d) {}
89+
StableSerializationPath(ExternalPath ext) : Union(ext) {}
90+
91+
explicit operator bool() const { return !Union.empty(); }
92+
93+
bool isSwiftDecl() const { return Union.isa<const Decl*>(); }
94+
const Decl *getSwiftDecl() const {
95+
assert(isSwiftDecl());
96+
return Union.get<const Decl*>();
97+
}
98+
99+
bool isExternalPath() const { return Union.isa<ExternalPath>(); }
100+
const ExternalPath &getExternalPath() const {
101+
assert(isExternalPath());
102+
return Union.get<ExternalPath>();
103+
}
104+
105+
SWIFT_DEBUG_DUMP;
106+
void dump(raw_ostream &os) const;
107+
};
108+
43109
class ClangModuleLoader : public ModuleLoader {
44110
private:
45111
virtual void anchor();
@@ -111,6 +177,41 @@ class ClangModuleLoader : public ModuleLoader {
111177
/// Print the Clang type.
112178
virtual void printClangType(const clang::Type *type,
113179
llvm::raw_ostream &os) const = 0;
180+
181+
/// Try to find a stable serialization path for the given declaration,
182+
/// if there is one.
183+
virtual StableSerializationPath
184+
findStableSerializationPath(const clang::Decl *decl) const = 0;
185+
186+
/// Try to resolve a stable serialization path down to the original
187+
/// declaration.
188+
virtual const clang::Decl *
189+
resolveStableSerializationPath(const StableSerializationPath &path) const = 0;
190+
191+
/// Determine whether the given type is serializable.
192+
///
193+
/// If \c checkCanonical is true, checks the canonical type,
194+
/// not the given possibly-sugared type. In general:
195+
/// - non-canonical representations should be preserving the
196+
/// sugared type even if it isn't serializable, since that
197+
/// maintains greater source fidelity;
198+
/// - semantic checks need to be checking the serializability
199+
/// of the canonical type, since it's always potentially
200+
/// necessary to serialize that (e.g. in SIL); and
201+
/// - serializers can try to serialize the sugared type to
202+
/// maintain source fidelity and just fall back on the canonical
203+
/// type if that's not possible.
204+
///
205+
/// The expectation here is that this predicate is meaningful
206+
/// independent of the actual form of serialization: the types
207+
/// that we can't reliably binary-serialize without an absolute
208+
/// Clang AST cross-reference are the same types that won't
209+
/// reliably round-trip through a textual format. At the very
210+
/// least, it's probably best to use conservative predicates
211+
/// that work both ways so that language behavior doesn't differ
212+
/// based on subtleties like the target module interface format.
213+
virtual bool isSerializable(const clang::Type *type,
214+
bool checkCanonical) const = 0;
114215
};
115216

116217
} // namespace swift

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2660,6 +2660,10 @@ ERROR(assoc_conformance_from_implementation_only_module,none,
26602660
"cannot use conformance of %0 to %1 in associated type %3 (inferred as "
26612661
"%4); %2 has been imported as implementation-only",
26622662
(Type, DeclName, Identifier, Type, Type))
2663+
ERROR(unexportable_clang_function_type,none,
2664+
"cannot export the underlying C type of the function type %0; "
2665+
"it may use anonymous types or types defined outside of a module",
2666+
(Type))
26632667

26642668
WARNING(warn_implementation_only_conflict,none,
26652669
"%0 inconsistently imported as implementation-only",

include/swift/ClangImporter/ClangImporter.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,16 @@ class ClangImporter final : public ClangModuleLoader {
422422
SourceLoc loc) const override;
423423
void printClangType(const clang::Type *type,
424424
llvm::raw_ostream &os) const override;
425+
426+
StableSerializationPath
427+
findStableSerializationPath(const clang::Decl *decl) const override;
428+
429+
const clang::Decl *
430+
resolveStableSerializationPath(
431+
const StableSerializationPath &path) const override;
432+
433+
bool isSerializable(const clang::Type *type,
434+
bool checkCanonical) const override;
425435
};
426436

427437
ImportDecl *createImportDecl(ASTContext &Ctx, DeclContext *DC, ClangNode ClangN,
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//===- SwiftAbstractBasicReader.h - Clang serialization adapter -*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file provides an intermediate CRTP class which implements most of
14+
// Clang's AbstractBasicReader interface, paralleling the behavior defined
15+
// in SwiftAbstractBasicWriter.
16+
//
17+
//===----------------------------------------------------------------------===//
18+
19+
#ifndef SWIFT_CLANGIMPORTER_SWIFTABSTRACTBASICREADER_H
20+
#define SWIFT_CLANGIMPORTER_SWIFTABSTRACTBASICREADER_H
21+
22+
#include "clang/AST/AbstractTypeReader.h"
23+
24+
// This include is required to instantiate the template code in
25+
// AbstractBasicReader.h, i.e. it's a workaround to an include-what-you-use
26+
// violation.
27+
#include "clang/AST/DeclObjC.h"
28+
29+
namespace swift {
30+
31+
/// An implementation of Clang's AbstractBasicReader interface for a Swift
32+
/// datastream-based reader. This is paired with the AbstractBasicWriter
33+
/// implementation in SwiftAbstractBasicWriter.h. Note that the general
34+
/// expectation is that the types and declarations involved will have passed
35+
/// a serializability check when this is used for actual deserialization.
36+
///
37+
/// The subclass must implement:
38+
/// uint64_t readUInt64();
39+
/// clang::IdentifierInfo *readIdentifier();
40+
/// clang::Stmt *readStmtRef();
41+
/// clang::Decl *readDeclRef();
42+
template <class Impl>
43+
class DataStreamBasicReader
44+
: public clang::serialization::DataStreamBasicReader<Impl> {
45+
using super = clang::serialization::DataStreamBasicReader<Impl>;
46+
public:
47+
using super::asImpl;
48+
using super::getASTContext;
49+
50+
DataStreamBasicReader(clang::ASTContext &ctx) : super(ctx) {}
51+
52+
/// Perform all the calls necessary to write out the given type.
53+
clang::QualType readTypeRef() {
54+
auto kind = clang::Type::TypeClass(asImpl().readUInt64());
55+
return clang::serialization::AbstractTypeReader<Impl>(asImpl()).read(kind);
56+
}
57+
58+
bool readBool() {
59+
return asImpl().readUInt64() != 0;
60+
}
61+
62+
uint32_t readUInt32() {
63+
return uint32_t(asImpl().readUInt64());
64+
}
65+
66+
clang::Selector readSelector() {
67+
uint64_t numArgsPlusOne = asImpl().readUInt64();
68+
69+
// The null case.
70+
if (numArgsPlusOne == 0)
71+
return clang::Selector();
72+
73+
unsigned numArgs = unsigned(numArgsPlusOne - 1);
74+
SmallVector<clang::IdentifierInfo *, 4> chunks;
75+
for (unsigned i = 0, e = std::max(numArgs, 1U); i != e; ++i)
76+
chunks.push_back(asImpl().readIdentifier());
77+
78+
return getASTContext().Selectors.getSelector(numArgs, chunks.data());
79+
}
80+
81+
clang::SourceLocation readSourceLocation() {
82+
// Always read null.
83+
return clang::SourceLocation();
84+
}
85+
86+
clang::QualType readQualType() {
87+
clang::Qualifiers quals = asImpl().readQualifiers();
88+
clang::QualType type = asImpl().readTypeRef();
89+
return getASTContext().getQualifiedType(type, quals);
90+
}
91+
};
92+
93+
}
94+
95+
#endif
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//===- SwiftAbstractBasicWriter.h - Clang serialization adapter -*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file provides an intermediate CRTP class which implements most of
14+
// Clang's AbstractBasicWriter interface, allowing largely the same logic
15+
// to be used for both the importer's "can this be serialized" checks and
16+
// the serializer's actual serialization logic.
17+
//
18+
//===----------------------------------------------------------------------===//
19+
20+
#ifndef SWIFT_CLANGIMPORTER_SWIFTABSTRACTBASICWRITER_H
21+
#define SWIFT_CLANGIMPORTER_SWIFTABSTRACTBASICWRITER_H
22+
23+
#include "clang/AST/AbstractTypeWriter.h"
24+
25+
namespace swift {
26+
27+
/// An implementation of Clang's AbstractBasicWriter interface for a Swift
28+
/// datastream-based reader. This is paired with the AbstractBasicReader
29+
/// implementation in SwiftAbstractBasicReader.h. Note that the general
30+
/// expectation is that the types and declarations involved will have passed
31+
/// a serializability check when this is used for actual serialization.
32+
/// The code in this class is also used when implementing that
33+
/// serializability check and so must be a little more cautious.
34+
///
35+
/// The subclass must implement:
36+
/// void writeUInt64(uint64_t value);
37+
/// void writeIdentifier(const clang::IdentifierInfo *ident);
38+
/// void writeStmtRef(const clang::Stmt *stmt);
39+
/// void writeDeclRef(const clang::Decl *decl);
40+
template <class Impl>
41+
class DataStreamBasicWriter
42+
: public clang::serialization::DataStreamBasicWriter<Impl> {
43+
using super = clang::serialization::DataStreamBasicWriter<Impl>;
44+
public:
45+
using super::asImpl;
46+
47+
/// Perform all the calls necessary to write out the given type.
48+
void writeTypeRef(const clang::Type *type) {
49+
asImpl().writeUInt64(uint64_t(type->getTypeClass()));
50+
clang::serialization::AbstractTypeWriter<Impl>(asImpl()).write(type);
51+
}
52+
53+
void writeBool(bool value) {
54+
asImpl().writeUInt64(uint64_t(value));
55+
}
56+
57+
void writeUInt32(uint32_t value) {
58+
asImpl().writeUInt64(uint64_t(value));
59+
}
60+
61+
void writeSelector(clang::Selector selector) {
62+
if (selector.isNull()) {
63+
asImpl().writeUInt64(0);
64+
return;
65+
}
66+
67+
asImpl().writeUInt64(selector.getNumArgs() + 1);
68+
for (unsigned i = 0, e = std::max(selector.getNumArgs(), 1U); i != e; ++i)
69+
asImpl().writeIdentifier(selector.getIdentifierInfoForSlot(i));
70+
}
71+
72+
void writeSourceLocation(clang::SourceLocation loc) {
73+
// DataStreamBasicReader will always read null; the serializability
74+
// check overrides this to complain about non-null source locations.
75+
}
76+
77+
void writeQualType(clang::QualType type) {
78+
assert(!type.isNull());
79+
80+
auto split = type.split();
81+
asImpl().writeQualifiers(split.Quals);
82+
83+
// Just recursively visit the given type.
84+
asImpl().writeTypeRef(split.Ty);
85+
}
86+
};
87+
88+
}
89+
90+
#endif

lib/AST/ASTContext.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4391,6 +4391,18 @@ ASTContext::getClangFunctionType(ArrayRef<AnyFunctionType::Param> params,
43914391
return impl.Converter.getValue().getFunctionType(params, resultTy, trueRep);
43924392
}
43934393

4394+
const Decl *
4395+
ASTContext::getSwiftDeclForExportedClangDecl(const clang::Decl *decl) {
4396+
auto &impl = getImpl();
4397+
4398+
// If we haven't exported anything yet, this must not be how we found
4399+
// this declaration.
4400+
if (!impl.Converter) return nullptr;
4401+
4402+
return impl.Converter->getSwiftDeclForExportedClangDecl(decl);
4403+
}
4404+
4405+
43944406
CanGenericSignature ASTContext::getSingleGenericParameterSignature() const {
43954407
if (auto theSig = getImpl().SingleGenericParameterSignature)
43964408
return theSig;

0 commit comments

Comments
 (0)