From 92e5e7b5b703407315111c571f3600b818af31ea Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Mon, 10 Nov 2025 15:57:33 +0000 Subject: [PATCH 1/3] [AST] Remove asserts from `getBuiltinInitDecl` Allow querying for types that may not conform. Failures here should be asserted downstream (e.g in `setBuiltinInitializer`). --- lib/AST/ASTContext.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index e1c7f80493ee3..4abd7fe678063 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1698,7 +1698,6 @@ ASTContext::getBuiltinInitDecl(NominalTypeDecl *decl, auto builtinProtocol = getProtocol(builtinProtocolKind); auto builtinConformance = lookupConformance(type, builtinProtocol); if (builtinConformance.isInvalid()) { - assert(false && "Missing required conformance"); witness = ConcreteDeclRef(); return witness; } @@ -1706,7 +1705,6 @@ ASTContext::getBuiltinInitDecl(NominalTypeDecl *decl, auto *ctx = const_cast(this); witness = builtinConformance.getWitnessByName(initName(*ctx)); if (!witness) { - assert(false && "Missing required witness"); witness = ConcreteDeclRef(); return witness; } From 33e6c0fb6c13ea9df2372e7794915a31232df6de Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Mon, 10 Nov 2025 15:57:33 +0000 Subject: [PATCH 2/3] [AST] Also cache protocol kind in `getBuiltinInitDecl` Make sure we don't return the wrong witness if you query with 2 different builtin protocol kinds. --- lib/AST/ASTContext.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 4abd7fe678063..7d6bea27724f3 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -525,7 +525,9 @@ struct ASTContext::Implementation { /// The builtin initializer witness for a literal. Used when building /// LiteralExprs in fully-checked AST. - llvm::DenseMap BuiltinInitWitness; + llvm::DenseMap, + 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. @@ -1686,11 +1688,12 @@ ASTContext::getStringBuiltinInitDecl(NominalTypeDecl *stringDecl) const { return getBuiltinInitDecl(stringDecl, builtinProtocolKind, fn); } -ConcreteDeclRef -ASTContext::getBuiltinInitDecl(NominalTypeDecl *decl, - KnownProtocolKind builtinProtocolKind, - llvm::function_ref initName) const { - auto &witness = getImpl().BuiltinInitWitness[decl]; +ConcreteDeclRef ASTContext::getBuiltinInitDecl( + NominalTypeDecl *decl, KnownProtocolKind builtinProtocolKind, + llvm::function_ref 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; From cdffd55d12b42a55d31a7bde6e83808d48b670d8 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Mon, 10 Nov 2025 15:57:33 +0000 Subject: [PATCH 3/3] [ClangImporter] Check for builtin conformance in `importNumericLiteral` Make sure the destination type actually conforms to the corresponding builtin literal protocol before attempting to import it. We may want to consider allowing any type that conforms to the non-builtin literal protocol, but I want to keep this patch low risk and just ensure we at least don't crash for now. rdar://156524292 --- lib/ClangImporter/ImportMacro.cpp | 25 +++++++++++++++++++++++ test/ClangImporter/rdar156524292.swift | 28 ++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 test/ClangImporter/rdar156524292.swift diff --git a/lib/ClangImporter/ImportMacro.cpp b/lib/ClangImporter/ImportMacro.cpp index 4715e669f7ae8..717341b8bbf8a 100644 --- a/lib/ClangImporter/ImportMacro.cpp +++ b/lib/ClangImporter/ImportMacro.cpp @@ -125,6 +125,11 @@ static ValueDecl *importNumericLiteral(ClangImporter::Implementation &Impl, return nullptr; } + auto &ctx = DC->getASTContext(); + auto *constantTyNominal = constantType->getAnyNominal(); + if (!constantTyNominal) + return nullptr; + if (auto *integer = dyn_cast(parsed)) { // Determine the value. llvm::APSInt value{integer->getValue(), clangTy->isUnsignedIntegerType()}; @@ -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, clang::APValue(value), ConstantConvertKind::None, @@ -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, diff --git a/test/ClangImporter/rdar156524292.swift b/test/ClangImporter/rdar156524292.swift new file mode 100644 index 0000000000000..c6cd55289210b --- /dev/null +++ b/test/ClangImporter/rdar156524292.swift @@ -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 +#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}}