Skip to content

Commit 9ba60f3

Browse files
committed
[cxx-interop] Import using decls that expose methods from private base classes
If a C++ type `Derived` inherits from `Base` privately, the public methods from `Base` should not be callable on an instance of `Derived`. However, C++ supports exposing such methods via a using declaration: `using MyPrivateBase::myPublicMethod;`. MSVC started using this feature for `std::optional` which means Swift doesn't correctly import `var pointee: Pointee` for instantiations of `std::optional` on Windows. This prevents the automatic conformance to `CxxOptional` from being synthesized. rdar://114282353 / resolves #68068
1 parent 6dafae8 commit 9ba60f3

16 files changed

+364
-83
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7075,6 +7075,11 @@ static bool isSufficientlyTrivial(const clang::CXXRecordDecl *decl) {
70757075
/// Checks if a record provides the required value type lifetime operations
70767076
/// (copy and destroy).
70777077
static bool hasCopyTypeOperations(const clang::CXXRecordDecl *decl) {
7078+
// Hack for a base type of std::optional from the Microsoft standard library.
7079+
if (decl->isInStdNamespace() && decl->getIdentifier() &&
7080+
decl->getName() == "_Optional_construct_base")
7081+
return true;
7082+
70787083
// If we have no way of copying the type we can't import the class
70797084
// at all because we cannot express the correct semantics as a swift
70807085
// struct.

lib/ClangImporter/ImportDecl.cpp

Lines changed: 157 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -2697,6 +2697,30 @@ namespace {
26972697
// SemaLookup.cpp).
26982698
if (!decl->isBeingDefined() && !decl->isDependentContext() &&
26992699
areRecordFieldsComplete(decl)) {
2700+
if (decl->hasInheritedConstructor() &&
2701+
Impl.isCxxInteropCompatVersionAtLeast(5, 11)) {
2702+
for (auto member : decl->decls()) {
2703+
if (auto usingDecl = dyn_cast<clang::UsingDecl>(member)) {
2704+
for (auto usingShadowDecl : usingDecl->shadows()) {
2705+
if (auto ctorUsingShadowDecl =
2706+
dyn_cast<clang::ConstructorUsingShadowDecl>(
2707+
usingShadowDecl)) {
2708+
auto baseCtorDecl = dyn_cast<clang::CXXConstructorDecl>(
2709+
ctorUsingShadowDecl->getTargetDecl());
2710+
if (!baseCtorDecl || baseCtorDecl->isDeleted())
2711+
continue;
2712+
auto derivedCtorDecl = clangSema.findInheritingConstructor(
2713+
clang::SourceLocation(), baseCtorDecl,
2714+
ctorUsingShadowDecl);
2715+
if (!derivedCtorDecl->isDefined() &&
2716+
!derivedCtorDecl->isDeleted())
2717+
clangSema.DefineInheritingConstructor(
2718+
clang::SourceLocation(), derivedCtorDecl);
2719+
}
2720+
}
2721+
}
2722+
}
2723+
}
27002724
if (decl->needsImplicitDefaultConstructor()) {
27012725
clang::CXXConstructorDecl *ctor =
27022726
clangSema.DeclareImplicitDefaultConstructor(
@@ -3237,6 +3261,58 @@ namespace {
32373261
llvm::None);
32383262
}
32393263

3264+
/// Handles special functions such as subscripts and dereference operators.
3265+
bool processSpecialImportedFunc(FuncDecl *func, ImportedName importedName) {
3266+
auto dc = func->getDeclContext();
3267+
3268+
if (importedName.isSubscriptAccessor()) {
3269+
assert(func->getParameters()->size() == 1);
3270+
auto typeDecl = dc->getSelfNominalTypeDecl();
3271+
auto parameter = func->getParameters()->get(0);
3272+
auto parameterType = parameter->getTypeInContext();
3273+
if (!typeDecl || !parameterType)
3274+
return false;
3275+
if (parameter->isInOut())
3276+
// Subscripts with inout parameters are not allowed in Swift.
3277+
return false;
3278+
3279+
auto &getterAndSetter = Impl.cxxSubscripts[{typeDecl, parameterType}];
3280+
3281+
switch (importedName.getAccessorKind()) {
3282+
case ImportedAccessorKind::SubscriptGetter:
3283+
getterAndSetter.first = func;
3284+
break;
3285+
case ImportedAccessorKind::SubscriptSetter:
3286+
getterAndSetter.second = func;
3287+
break;
3288+
default:
3289+
llvm_unreachable("invalid subscript kind");
3290+
}
3291+
3292+
Impl.markUnavailable(func, "use subscript");
3293+
}
3294+
3295+
if (importedName.isDereferenceAccessor()) {
3296+
auto typeDecl = dc->getSelfNominalTypeDecl();
3297+
auto &getterAndSetter = Impl.cxxDereferenceOperators[typeDecl];
3298+
3299+
switch (importedName.getAccessorKind()) {
3300+
case ImportedAccessorKind::DereferenceGetter:
3301+
getterAndSetter.first = func;
3302+
break;
3303+
case ImportedAccessorKind::DereferenceSetter:
3304+
getterAndSetter.second = func;
3305+
break;
3306+
default:
3307+
llvm_unreachable("invalid dereference operator kind");
3308+
}
3309+
3310+
Impl.markUnavailable(func, "use .pointee property");
3311+
}
3312+
3313+
return true;
3314+
}
3315+
32403316
Decl *importFunctionDecl(
32413317
const clang::FunctionDecl *decl, ImportedName importedName,
32423318
llvm::Optional<ImportedName> correctSwiftName,
@@ -3556,62 +3632,14 @@ namespace {
35563632
func->setImportAsStaticMember();
35573633
}
35583634
}
3635+
// Someday, maybe this will need to be 'open' for C++ virtual methods.
3636+
func->setAccess(AccessLevel::Public);
35593637

3560-
bool makePrivate = false;
3561-
3562-
if (importedName.isSubscriptAccessor() && !importFuncWithoutSignature) {
3563-
assert(func->getParameters()->size() == 1);
3564-
auto typeDecl = dc->getSelfNominalTypeDecl();
3565-
auto parameter = func->getParameters()->get(0);
3566-
auto parameterType = parameter->getTypeInContext();
3567-
if (!typeDecl || !parameterType)
3568-
return nullptr;
3569-
if (parameter->isInOut())
3570-
// Subscripts with inout parameters are not allowed in Swift.
3638+
if (!importFuncWithoutSignature) {
3639+
bool success = processSpecialImportedFunc(func, importedName);
3640+
if (!success)
35713641
return nullptr;
3572-
3573-
auto &getterAndSetter = Impl.cxxSubscripts[{ typeDecl,
3574-
parameterType }];
3575-
3576-
switch (importedName.getAccessorKind()) {
3577-
case ImportedAccessorKind::SubscriptGetter:
3578-
getterAndSetter.first = func;
3579-
break;
3580-
case ImportedAccessorKind::SubscriptSetter:
3581-
getterAndSetter.second = func;
3582-
break;
3583-
default:
3584-
llvm_unreachable("invalid subscript kind");
3585-
}
3586-
3587-
Impl.markUnavailable(func, "use subscript");
35883642
}
3589-
3590-
if (importedName.isDereferenceAccessor() &&
3591-
!importFuncWithoutSignature) {
3592-
auto typeDecl = dc->getSelfNominalTypeDecl();
3593-
auto &getterAndSetter = Impl.cxxDereferenceOperators[typeDecl];
3594-
3595-
switch (importedName.getAccessorKind()) {
3596-
case ImportedAccessorKind::DereferenceGetter:
3597-
getterAndSetter.first = func;
3598-
break;
3599-
case ImportedAccessorKind::DereferenceSetter:
3600-
getterAndSetter.second = func;
3601-
break;
3602-
default:
3603-
llvm_unreachable("invalid dereference operator kind");
3604-
}
3605-
3606-
Impl.markUnavailable(func, "use .pointee property");
3607-
makePrivate = true;
3608-
}
3609-
3610-
if (makePrivate)
3611-
func->setAccess(AccessLevel::Private);
3612-
else
3613-
// Someday, maybe this will need to be 'open' for C++ virtual methods.
3614-
func->setAccess(AccessLevel::Public);
36153643
}
36163644

36173645
result->setIsObjC(false);
@@ -3924,18 +3952,30 @@ namespace {
39243952
}
39253953

39263954
Decl *VisitUsingDecl(const clang::UsingDecl *decl) {
3927-
// Using declarations are not imported.
3955+
// See VisitUsingShadowDecl below.
39283956
return nullptr;
39293957
}
39303958

39313959
Decl *VisitUsingShadowDecl(const clang::UsingShadowDecl *decl) {
3932-
// Only import types for now.
3933-
if (!isa<clang::TypeDecl>(decl->getUnderlyingDecl()))
3960+
// Only import:
3961+
// 1. Types
3962+
// 2. C++ methods from privately inherited base classes
3963+
if (!isa<clang::TypeDecl>(decl->getTargetDecl()) &&
3964+
!(isa<clang::CXXMethodDecl>(decl->getTargetDecl()) &&
3965+
Impl.isCxxInteropCompatVersionAtLeast(5, 11)))
3966+
return nullptr;
3967+
// Constructors (e.g. `using BaseClass::BaseClass`) are handled in
3968+
// VisitCXXRecordDecl, since we need them to determine whether a struct
3969+
// can be imported into Swift.
3970+
if (isa<clang::CXXConstructorDecl>(decl->getTargetDecl()))
39343971
return nullptr;
39353972

39363973
ImportedName importedName;
39373974
llvm::Optional<ImportedName> correctSwiftName;
39383975
std::tie(importedName, correctSwiftName) = importFullName(decl);
3976+
// Don't import something that doesn't have a name.
3977+
if (importedName.getDeclName().isSpecial())
3978+
return nullptr;
39393979
auto Name = importedName.getDeclName().getBaseIdentifier();
39403980
if (Name.empty())
39413981
return nullptr;
@@ -3946,30 +3986,66 @@ namespace {
39463986
return importCompatibilityTypeAlias(decl, importedName,
39473987
*correctSwiftName);
39483988

3949-
auto DC =
3989+
auto importedDC =
39503990
Impl.importDeclContextOf(decl, importedName.getEffectiveContext());
3951-
if (!DC)
3991+
if (!importedDC)
39523992
return nullptr;
39533993

3954-
Decl *SwiftDecl = Impl.importDecl(decl->getUnderlyingDecl(), getActiveSwiftVersion());
3955-
if (!SwiftDecl)
3956-
return nullptr;
3994+
if (isa<clang::TypeDecl>(decl->getTargetDecl())) {
3995+
Decl *SwiftDecl = Impl.importDecl(decl->getUnderlyingDecl(), getActiveSwiftVersion());
3996+
if (!SwiftDecl)
3997+
return nullptr;
39573998

3958-
const TypeDecl *SwiftTypeDecl = dyn_cast<TypeDecl>(SwiftDecl);
3959-
if (!SwiftTypeDecl)
3960-
return nullptr;
3999+
const TypeDecl *SwiftTypeDecl = dyn_cast<TypeDecl>(SwiftDecl);
4000+
if (!SwiftTypeDecl)
4001+
return nullptr;
39614002

3962-
auto Loc = Impl.importSourceLoc(decl->getLocation());
3963-
auto Result = Impl.createDeclWithClangNode<TypeAliasDecl>(
3964-
decl,
3965-
AccessLevel::Public,
3966-
Impl.importSourceLoc(decl->getBeginLoc()),
3967-
SourceLoc(), Name,
3968-
Loc,
3969-
/*genericparams*/nullptr, DC);
3970-
Result->setUnderlyingType(SwiftTypeDecl->getDeclaredInterfaceType());
4003+
auto Loc = Impl.importSourceLoc(decl->getLocation());
4004+
auto Result = Impl.createDeclWithClangNode<TypeAliasDecl>(
4005+
decl,
4006+
AccessLevel::Public,
4007+
Impl.importSourceLoc(decl->getBeginLoc()),
4008+
SourceLoc(), Name,
4009+
Loc,
4010+
/*genericparams*/nullptr, importedDC);
4011+
Result->setUnderlyingType(SwiftTypeDecl->getDeclaredInterfaceType());
4012+
4013+
return Result;
4014+
}
4015+
if (auto targetMethod =
4016+
dyn_cast<clang::CXXMethodDecl>(decl->getTargetDecl())) {
4017+
auto dc = dyn_cast<clang::CXXRecordDecl>(decl->getDeclContext());
4018+
4019+
auto targetDC = targetMethod->getDeclContext();
4020+
auto targetRecord = dyn_cast<clang::CXXRecordDecl>(targetDC);
4021+
if (!targetRecord)
4022+
return nullptr;
39714023

3972-
return Result;
4024+
// If this struct is not inherited from the struct where the method is
4025+
// defined, bail.
4026+
if (!dc->isDerivedFrom(targetRecord))
4027+
return nullptr;
4028+
4029+
auto importedBaseMethod = dyn_cast_or_null<FuncDecl>(
4030+
Impl.importDecl(targetMethod, getActiveSwiftVersion()));
4031+
// This will be nullptr for a protected method of base class that is
4032+
// made public with a using declaration in a derived class. This is
4033+
// valid in C++ but we do not import such using declarations now.
4034+
// TODO: make this work for protected base methods.
4035+
if (!importedBaseMethod)
4036+
return nullptr;
4037+
auto clonedMethod = dyn_cast_or_null<FuncDecl>(
4038+
Impl.importBaseMemberDecl(importedBaseMethod, importedDC));
4039+
if (!clonedMethod)
4040+
return nullptr;
4041+
4042+
bool success = processSpecialImportedFunc(clonedMethod, importedName);
4043+
if (!success)
4044+
return nullptr;
4045+
4046+
return clonedMethod;
4047+
}
4048+
return nullptr;
39734049
}
39744050

39754051
/// Add an @objc(name) attribute with the given, optional name expressed as
@@ -8353,11 +8429,17 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
83538429
if (Result &&
83548430
(!Result->getDeclContext()->isModuleScopeContext() ||
83558431
isa<ClangModuleUnit>(Result->getDeclContext()))) {
8432+
// For using declarations that expose a method of a base class, the Clang
8433+
// decl is synthesized lazily when the method is actually used from Swift.
8434+
bool hasSynthesizedClangNode =
8435+
isa<clang::UsingShadowDecl>(ClangDecl) && isa<FuncDecl>(Result);
8436+
83568437
// Either the Swift declaration was from stdlib,
83578438
// or we imported the underlying decl of the typedef,
83588439
// or we imported the decl itself.
83598440
bool ImportedCorrectly =
83608441
!Result->getClangDecl() || SkippedOverTypedef ||
8442+
hasSynthesizedClangNode ||
83618443
Result->getClangDecl()->getCanonicalDecl() == Canon;
83628444

83638445
// Or the other type is a typedef,
@@ -8380,7 +8462,7 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
83808462
}
83818463
assert(ImportedCorrectly);
83828464
}
8383-
assert(Result->hasClangNode());
8465+
assert(Result->hasClangNode() || hasSynthesizedClangNode);
83848466
}
83858467
#else
83868468
(void)SkippedOverTypedef;

lib/ClangImporter/ImportName.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,6 +1522,17 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
15221522
return ImportedName();
15231523
result.effectiveContext = effectiveCtx;
15241524

1525+
// If this is a using declaration, import the name of the shadowed decl and
1526+
// adjust the context.
1527+
if (auto usingShadowDecl = dyn_cast<clang::UsingShadowDecl>(D)) {
1528+
auto targetDecl = usingShadowDecl->getTargetDecl();
1529+
if (isa<clang::CXXMethodDecl>(targetDecl)) {
1530+
ImportedName baseName = importName(targetDecl, version, givenName);
1531+
baseName.effectiveContext = effectiveCtx;
1532+
return baseName;
1533+
}
1534+
}
1535+
15251536
// Gather information from the swift_async attribute, if there is one.
15261537
llvm::Optional<unsigned> completionHandlerParamIndex;
15271538
bool completionHandlerFlagIsZeroOnError = false;

lib/ClangImporter/ImporterImpl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,9 +663,9 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
663663
llvm::DenseMap<std::pair<ValueDecl *, DeclContext *>, ValueDecl *>
664664
clonedBaseMembers;
665665

666+
public:
666667
ValueDecl *importBaseMemberDecl(ValueDecl *decl, DeclContext *newContext);
667668

668-
public:
669669
static size_t getImportedBaseMemberDeclArity(const ValueDecl *valueDecl);
670670

671671
// Cache for already-specialized function templates and any thunks they may

lib/ClangImporter/SwiftLookupTable.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1991,6 +1991,12 @@ void importer::addEntryToLookupTable(SwiftLookupTable &table,
19911991
}
19921992
}
19931993
}
1994+
if (auto usingDecl = dyn_cast<clang::UsingDecl>(named)) {
1995+
for (auto usingShadowDecl : usingDecl->shadows()) {
1996+
if (isa<clang::CXXMethodDecl>(usingShadowDecl->getTargetDecl()))
1997+
addEntryToLookupTable(table, usingShadowDecl, nameImporter);
1998+
}
1999+
}
19942000
}
19952001

19962002
/// Returns the nearest parent of \p module that is marked \c explicit in its

test/Interop/Cxx/class/Inputs/constructors.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,4 @@ struct DeletedCopyConstructor {
8080
DeletedCopyConstructor(const DeletedCopyConstructor &) = delete;
8181
};
8282

83-
// TODO: we should be able to import this constructor correctly. Until we can,
84-
// make sure not to crash.
85-
struct UsingBaseConstructor : ConstructorWithParam {
86-
using ConstructorWithParam::ConstructorWithParam;
87-
};
88-
8983
#endif // TEST_INTEROP_CXX_CLASS_INPUTS_CONSTRUCTORS_H

test/Interop/Cxx/class/inheritance/Inputs/module.modulemap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ module TypeAliases {
2828
header "type-aliases.h"
2929
}
3030

31+
module UsingBaseMembers {
32+
header "using-base-members.h"
33+
requires cplusplus
34+
}
35+
3136
module VirtualMethods {
3237
header "virtual-methods.h"
3338
requires cplusplus

0 commit comments

Comments
 (0)