Skip to content
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
17 changes: 9 additions & 8 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,9 @@ struct ASTContext::Implementation {

/// The builtin initializer witness for a literal. Used when building
/// LiteralExprs in fully-checked AST.
llvm::DenseMap<const NominalTypeDecl *, ConcreteDeclRef> BuiltinInitWitness;
llvm::DenseMap<std::pair<const NominalTypeDecl *, KnownProtocolKind>,
ConcreteDeclRef>
BuiltinInitWitness;

/// Mapping from the function decl to its original body's source range. This
/// is populated if the body is reparsed from other source buffers.
Expand Down Expand Up @@ -1686,27 +1688,26 @@ ASTContext::getStringBuiltinInitDecl(NominalTypeDecl *stringDecl) const {
return getBuiltinInitDecl(stringDecl, builtinProtocolKind, fn);
}

ConcreteDeclRef
ASTContext::getBuiltinInitDecl(NominalTypeDecl *decl,
KnownProtocolKind builtinProtocolKind,
llvm::function_ref<DeclName (ASTContext &ctx)> initName) const {
auto &witness = getImpl().BuiltinInitWitness[decl];
ConcreteDeclRef ASTContext::getBuiltinInitDecl(
NominalTypeDecl *decl, KnownProtocolKind builtinProtocolKind,
llvm::function_ref<DeclName(ASTContext &ctx)> initName) const {
// Note the initializer name is expected to be unique for each protocol kind
// so we don't need it to be part of the key.
auto &witness = getImpl().BuiltinInitWitness[{decl, builtinProtocolKind}];
if (witness)
return witness;

auto type = decl->getDeclaredInterfaceType();
auto builtinProtocol = getProtocol(builtinProtocolKind);
auto builtinConformance = lookupConformance(type, builtinProtocol);
if (builtinConformance.isInvalid()) {
assert(false && "Missing required conformance");
witness = ConcreteDeclRef();
return witness;
}

auto *ctx = const_cast<ASTContext *>(this);
witness = builtinConformance.getWitnessByName(initName(*ctx));
if (!witness) {
assert(false && "Missing required witness");
witness = ConcreteDeclRef();
return witness;
}
Expand Down
25 changes: 25 additions & 0 deletions lib/ClangImporter/ImportMacro.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ static ValueDecl *importNumericLiteral(ClangImporter::Implementation &Impl,
return nullptr;
}

auto &ctx = DC->getASTContext();
auto *constantTyNominal = constantType->getAnyNominal();
if (!constantTyNominal)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add an assert at the other call sites too then, to avoid propagating nulls?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm not sure I understand what you mean by this

return nullptr;

if (auto *integer = dyn_cast<clang::IntegerLiteral>(parsed)) {
// Determine the value.
llvm::APSInt value{integer->getValue(), clangTy->isUnsignedIntegerType()};
Expand All @@ -140,6 +145,16 @@ static ValueDecl *importNumericLiteral(ClangImporter::Implementation &Impl,
}
}

// Make sure the destination type actually conforms to the builtin literal
// protocol before attempting to import, otherwise we'll crash since
// `createConstant` expects it to.
// FIXME: We ought to be careful checking conformance here since it can
// result in cycles. Additionally we ought to consider checking for the
// non-builtin literal protocol to allow any ExpressibleByIntegerLiteral
// type to be supported.
if (!ctx.getIntBuiltinInitDecl(constantTyNominal))
return nullptr;

return createMacroConstant(Impl, MI, name, DC, constantType,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to pass down the result of the lookup to createMacroConstant() instead of re-doing the lookup inside?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cache the result on success, so it shouldn't matter much. There are also a bunch of different clients of createConstant, which would make it a bit awkward (although looking at them it's not entirely clear to me that we have the correct checks for them either)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interestingly the caching logic in getBuiltinInitDecl only uses the decl as the key, I guess we never ever conform a decl to more than 1 builtin literal kind? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm nope, Float conforms to both _ExpressibleByBuiltinIntegerLiteral & _ExpressibleByBuiltinFloatLiteral

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to also include the kind in the key

clang::APValue(value),
ConstantConvertKind::None,
Expand All @@ -158,6 +173,16 @@ static ValueDecl *importNumericLiteral(ClangImporter::Implementation &Impl,
value.changeSign();
}

// Make sure the destination type actually conforms to the builtin literal
// protocol before attempting to import, otherwise we'll crash since
// `createConstant` expects it to.
// FIXME: We ought to be careful checking conformance here since it can
// result in cycles. Additionally we ought to consider checking for the
// non-builtin literal protocol to allow any ExpressibleByFloatLiteral
// type to be supported.
if (!ctx.getFloatBuiltinInitDecl(constantTyNominal))
return nullptr;

return createMacroConstant(Impl, MI, name, DC, constantType,
clang::APValue(value),
ConstantConvertKind::None,
Expand Down
28 changes: 28 additions & 0 deletions test/ClangImporter/rdar156524292.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// RUN: %empty-directory(%t)
// RUN: split-file %s %t
// RUN: %target-swift-frontend -typecheck -verify %t/main.swift -I %t -verify-additional-file %t/cmodule.h

// REQUIRES: objc_interop
// REQUIRES: OS=macosx

//--- cmodule.h
#import <CoreGraphics/CoreGraphics.h>
#define intLiteralCGFloat ((CGFloat)0)
// expected-note@-1 {{invalid numeric literal}}
// expected-note@-2 {{macro 'intLiteralCGFloat' unavailable (cannot import)}}
#define floatLiteralCGFloat ((CGFloat)0.0)
// expected-note@-1 {{invalid numeric literal}}
// expected-note@-2 {{macro 'floatLiteralCGFloat' unavailable (cannot import)}}

//--- module.modulemap
module CModule [system] {
header "cmodule.h"
export *
}

//--- main.swift
import CModule

// Make sure we don't crash when attempting to import these.
_ = intLiteralCGFloat // expected-error {{cannot find 'intLiteralCGFloat' in scope}}
_ = floatLiteralCGFloat // expected-error {{cannot find 'floatLiteralCGFloat' in scope}}