Skip to content

Implement Swift serialization and deserialization of Clang types #29670

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
101 changes: 101 additions & 0 deletions include/swift/AST/ClangModuleLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -26,6 +28,7 @@ class Type;

namespace swift {

class Decl;
class DeclContext;
class VisibleDeclConsumer;

Expand All @@ -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<std::pair<ComponentKind, Identifier>, 2> Path;

void add(ComponentKind kind, Identifier name) {
Path.push_back({kind, name});
}
};
private:
TaggedUnion<void, const Decl *, ExternalPath> 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*>(); }
const Decl *getSwiftDecl() const {
assert(isSwiftDecl());
return Union.get<const Decl*>();
}

bool isExternalPath() const { return Union.isa<ExternalPath>(); }
const ExternalPath &getExternalPath() const {
assert(isExternalPath());
return Union.get<ExternalPath>();
}

SWIFT_DEBUG_DUMP;
void dump(raw_ostream &os) const;
};

class ClangModuleLoader : public ModuleLoader {
private:
virtual void anchor();
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
15 changes: 15 additions & 0 deletions include/swift/AST/PrettyStackTrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
#include "swift/AST/Identifier.h"
#include "swift/AST/Type.h"

namespace clang {
class Type;
}

namespace swift {
class ASTContext;
class Decl;
Expand Down Expand Up @@ -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;
Expand Down
10 changes: 10 additions & 0 deletions include/swift/ClangImporter/ClangImporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
95 changes: 95 additions & 0 deletions include/swift/ClangImporter/SwiftAbstractBasicReader.h
Original file line number Diff line number Diff line change
@@ -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 Impl>
class DataStreamBasicReader
: public clang::serialization::DataStreamBasicReader<Impl> {
using super = clang::serialization::DataStreamBasicReader<Impl>;
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<Impl>(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<clang::IdentifierInfo *, 4> 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
90 changes: 90 additions & 0 deletions include/swift/ClangImporter/SwiftAbstractBasicWriter.h
Original file line number Diff line number Diff line change
@@ -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 Impl>
class DataStreamBasicWriter
: public clang::serialization::DataStreamBasicWriter<Impl> {
using super = clang::serialization::DataStreamBasicWriter<Impl>;
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<Impl>(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
Loading