From 0e56809834458671b9d0492942d2b0eb7a0ca60f Mon Sep 17 00:00:00 2001 From: Gabor Horvath Date: Fri, 15 Aug 2025 14:10:50 +0100 Subject: [PATCH] [cxx-interop] Support calling functions in inline namespaces The overload resolution generated a constraint that tried to bind outer.inline_inner to outer. This constraint failed. This PR attempts to recognize this scenario and make the constraint succeed. rdar://158401346 --- include/swift/ClangImporter/ClangModule.h | 4 ++ lib/ClangImporter/ClangImporter.cpp | 17 ++++++- lib/ClangImporter/ImportDecl.cpp | 2 +- lib/SILGen/SILGenExpr.cpp | 2 +- lib/Sema/TypeOfReference.cpp | 41 ++++++++++++--- ...nline-namespace-function-call-broken.swift | 26 ---------- .../inline-namespace-function-call.swift | 50 +++++++++++++++++++ 7 files changed, 106 insertions(+), 36 deletions(-) delete mode 100644 test/Interop/Cxx/namespace/inline-namespace-function-call-broken.swift create mode 100644 test/Interop/Cxx/namespace/inline-namespace-function-call.swift diff --git a/include/swift/ClangImporter/ClangModule.h b/include/swift/ClangImporter/ClangModule.h index c27cc2ecf2a2a..85536be1ec5a1 100644 --- a/include/swift/ClangImporter/ClangModule.h +++ b/include/swift/ClangImporter/ClangModule.h @@ -141,6 +141,10 @@ class ClangModuleUnit final : public LoadedFile { } }; +// Strips the inline namespaces from inner until we reach outer or a +// non-inline namespace. Returns the stripped nominal type or null when +// something unexpected happened during stripping. +NominalType *stripInlineNamespaces(NominalType *outer, NominalType *inner); } #endif diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 8f5648145d350..6c01b772e27d3 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3433,7 +3433,7 @@ class DarwinLegacyFilterDeclConsumer : public swift::VisibleDeclConsumer { /// Translate a MacroDefinition to a ClangNode, either a ModuleMacro for /// a definition imported from a module or a MacroInfo for a macro defined /// locally. -ClangNode getClangNodeForMacroDefinition(clang::MacroDefinition &M) { +static ClangNode getClangNodeForMacroDefinition(clang::MacroDefinition &M) { if (!M.getModuleMacros().empty()) return ClangNode(M.getModuleMacros().back()->getMacroInfo()); if (auto *MD = M.getLocalDirective()) @@ -9044,3 +9044,18 @@ swift::importer::getCxxRefConventionWithAttrs(const clang::Decl *decl) { return std::nullopt; } + +NominalType *swift::stripInlineNamespaces(NominalType *outer, + NominalType *inner) { + if (!outer || !inner || inner == outer) + return nullptr; + auto CDInner = inner->getDecl()->getClangDecl(); + while (inner && isa_and_nonnull(CDInner) && + cast(CDInner)->isInline() && + inner->getCanonicalType() != outer->getCanonicalType()) { + CDInner = cast(CDInner->getDeclContext()); + inner = dyn_cast(inner->getParent().getPointer()); + } + + return inner; +} diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index e08e84653f2ed..ec9fcbe9e282c 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -10610,7 +10610,7 @@ ClangImporter::Implementation::importDeclContextOf( // Treat friend decls like top-level decls. if (functionDecl->getFriendObjectKind()) { // Find the top-level decl context. - while (isa(dc)) + while (!dc->isFileContext()) dc = dc->getParent(); } diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 9f20526949ef0..b4ef831adbdcb 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -1495,7 +1495,7 @@ RValue RValueEmitter::visitDerivedToBaseExpr(DerivedToBaseExpr *E, RValue RValueEmitter::visitMetatypeConversionExpr(MetatypeConversionExpr *E, SGFContext C) { SILValue metaBase = - SGF.emitRValueAsSingleValue(E->getSubExpr()).getUnmanagedValue(); + SGF.emitRValueAsSingleValue(E->getSubExpr()).getUnmanagedValue(); // Metatype conversion casts in the AST might not be reflected as // such in the SIL type system, for example, a cast from DynamicSelf.Type diff --git a/lib/Sema/TypeOfReference.cpp b/lib/Sema/TypeOfReference.cpp index f21f5c970e3c0..bec20874a37fa 100644 --- a/lib/Sema/TypeOfReference.cpp +++ b/lib/Sema/TypeOfReference.cpp @@ -22,17 +22,20 @@ #include "TypeCheckType.h" #include "TypeChecker.h" #include "swift/AST/ConformanceLookup.h" -#include "swift/AST/GenericEnvironment.h" #include "swift/AST/Effects.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/MacroDefinition.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/Type.h" #include "swift/AST/TypeTransform.h" -#include "swift/Sema/ConstraintSystem.h" -#include "swift/Sema/PreparedOverload.h" +#include "swift/AST/Types.h" #include "swift/Basic/Assertions.h" -#include "swift/Basic/Statistic.h" #include "swift/Basic/Defer.h" +#include "swift/Basic/Statistic.h" +#include "swift/ClangImporter/ClangModule.h" +#include "swift/Sema/ConstraintSystem.h" +#include "swift/Sema/PreparedOverload.h" using namespace swift; using namespace constraints; @@ -1646,11 +1649,12 @@ static void addSelfConstraint(ConstraintSystem &cs, Type objectTy, Type selfTy, PreparedOverloadBuilder *preparedOverload) { assert(!selfTy->is()); - // Otherwise, use a subtype constraint for classes to cope with inheritance. + // Otherwise, use a subtype constraint for classes to cope with + // inheritance. if (selfTy->getClassOrBoundGenericClass()) { cs.addConstraint(ConstraintKind::Subtype, objectTy, selfTy, - cs.getConstraintLocator(locator), /*isFavored=*/false, - preparedOverload); + cs.getConstraintLocator(locator), + /*isFavored=*/false, preparedOverload); return; } @@ -1950,6 +1954,29 @@ ConstraintSystem::getTypeOfMemberReferencePre( value, /*isCurriedInstanceReference*/ !hasAppliedSelf, functionRefInfo); openedType = openedType->removeArgumentLabels(numRemovedArgumentLabels); + // Adjust for C++ inline namespaces. + if (const auto *FT = openedType->getAs()) { + auto openedParams = FT->getParams(); + assert(openedParams.size() == 1); + auto param = openedParams.front(); + + Type selfObjTy = param.getPlainType(); + bool wasMetaType = false; + if (param.getPlainType()->is()) { + selfObjTy = param.getPlainType()->getMetatypeInstanceType(); + wasMetaType = true; + } + if (auto *objectTyNominal = baseObjTy->getAs()) { + if (auto *selfTyNominal = selfObjTy->getAs()) + if (auto newSelfTy = + swift::stripInlineNamespaces(objectTyNominal, selfTyNominal)) + openedType = FunctionType::get( + param.withType(wasMetaType ? Type(MetatypeType::get(newSelfTy)) + : Type(newSelfTy)), + FT->getResult(), FT->getExtInfo()); + } + } + Type baseOpenedTy = baseObjTy; // If we are looking at a member of an existential, open the existential. diff --git a/test/Interop/Cxx/namespace/inline-namespace-function-call-broken.swift b/test/Interop/Cxx/namespace/inline-namespace-function-call-broken.swift deleted file mode 100644 index 0debaeed63c4a..0000000000000 --- a/test/Interop/Cxx/namespace/inline-namespace-function-call-broken.swift +++ /dev/null @@ -1,26 +0,0 @@ -// RUN: rm -rf %t -// RUN: split-file %s %t -// RUN: %target-swift-frontend -typecheck -verify -verify-ignore-unknown -I %t/Inputs %t/test.swift -enable-experimental-cxx-interop - -//--- Inputs/module.modulemap -module namespaces { - header "test.h" - requires cplusplus -} -//--- Inputs/test.h -namespace Parent { -inline namespace InlineChild { - -void functionInInlineChild(); - -} // namespace InlineChild -} // namespace Parent - -//--- test.swift - -import namespaces; - -// Swift's typechecker currently doesn't allow calling a function from inline namespace when it's referenced through the parent namespace. -func test() { - Parent.functionInInlineChild() // expected-error {{failed to produce diagnostic for expression}} -} diff --git a/test/Interop/Cxx/namespace/inline-namespace-function-call.swift b/test/Interop/Cxx/namespace/inline-namespace-function-call.swift new file mode 100644 index 0000000000000..d338299479b17 --- /dev/null +++ b/test/Interop/Cxx/namespace/inline-namespace-function-call.swift @@ -0,0 +1,50 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: %target-swift-frontend -c -verify -verify-ignore-unknown -I %t/Inputs %t/test.swift -enable-experimental-cxx-interop -verify + +//--- Inputs/module.modulemap +module namespaces { + header "test.h" + requires cplusplus +} +//--- Inputs/test.h +namespace Parent { +inline namespace InlineChild { +inline namespace InlineChild2 { +void functionInInlineChild2(); +int GlobalVariableInInlineChild2; +struct GlobalTypeInInlineChild2 {}; +} // namespace InlineChild2 +void functionInInlineChild(); +int GlobalVariableInInlineChild; +struct GlobalTypeInInlineChild {}; +namespace Child { +void functionInInlineChildsNonInlineNamespace(); +} // namespace Child +} // namespace InlineChild +} // namespace Parent + +//--- test.swift + +import namespaces; + +func test() { + Parent.functionInInlineChild() + Parent.GlobalVariableInInlineChild = 1 + Parent.functionInInlineChild2() + Parent.GlobalVariableInInlineChild2 = 2 + Parent.InlineChild.functionInInlineChild() + Parent.InlineChild.GlobalVariableInInlineChild = 3 + Parent.InlineChild.functionInInlineChild2() + Parent.InlineChild.GlobalVariableInInlineChild2 = 4 + Parent.InlineChild.InlineChild2.functionInInlineChild2() + Parent.InlineChild.InlineChild2.GlobalVariableInInlineChild2 = 5 + // FIXME: fix the below case. + Parent.Child.functionInInlineChildsNonInlineNamespace() // expected-error {{failed to produce diagnostic for expression}} + Parent.InlineChild.Child.functionInInlineChildsNonInlineNamespace() +} + +func testTypes(x: Parent.GlobalTypeInInlineChild, y: Parent.GlobalTypeInInlineChild2, + x2: Parent.InlineChild.GlobalTypeInInlineChild, + y2: Parent.InlineChild.GlobalTypeInInlineChild2, + y3: Parent.InlineChild.InlineChild2.GlobalTypeInInlineChild2) {}