Skip to content

Commit 49febcd

Browse files
authored
Merge pull request #83252 from DougGregor/import-conforms-to-newtype
[Clang importer] Handle Swift conformances on typedefs and C types
2 parents d3b4817 + 477c2b5 commit 49febcd

File tree

3 files changed

+154
-96
lines changed

3 files changed

+154
-96
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 130 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -3152,98 +3152,9 @@ namespace {
31523152
}
31533153
}
31543154

3155-
if (auto *ntd = dyn_cast<NominalTypeDecl>(result))
3156-
addExplicitProtocolConformances(ntd, decl);
3157-
31583155
return result;
31593156
}
31603157

3161-
using ProtocolSet = llvm::SmallSet<ProtocolDecl *, 4>;
3162-
3163-
void
3164-
addExplicitProtocolConformances(NominalTypeDecl *decl,
3165-
const clang::CXXRecordDecl *clangDecl) {
3166-
if (clangDecl->isCompleteDefinition()) {
3167-
// Propagate conforms_to attribute from public base classes.
3168-
for (auto base : clangDecl->bases()) {
3169-
if (base.getAccessSpecifier() != clang::AccessSpecifier::AS_public)
3170-
continue;
3171-
if (auto *baseClangDecl = base.getType()->getAsCXXRecordDecl())
3172-
addExplicitProtocolConformances(decl, baseClangDecl);
3173-
}
3174-
}
3175-
3176-
if (!clangDecl->hasAttrs())
3177-
return;
3178-
3179-
ProtocolSet alreadyAdded;
3180-
llvm::for_each(clangDecl->getAttrs(), [&](auto *attr) {
3181-
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
3182-
if (swiftAttr->getAttribute().starts_with("conforms_to:"))
3183-
addExplicitProtocolConformance(decl, swiftAttr, alreadyAdded);
3184-
}
3185-
});
3186-
}
3187-
3188-
void addExplicitProtocolConformance(NominalTypeDecl *decl,
3189-
clang::SwiftAttrAttr *conformsToAttr,
3190-
ProtocolSet &alreadyAdded) {
3191-
auto conformsToValue = conformsToAttr->getAttribute()
3192-
.drop_front(StringRef("conforms_to:").size())
3193-
.str();
3194-
auto names = StringRef(conformsToValue).split('.');
3195-
auto moduleName = names.first;
3196-
auto protocolName = names.second;
3197-
if (protocolName.empty()) {
3198-
HeaderLoc attrLoc(conformsToAttr->getLocation());
3199-
Impl.diagnose(attrLoc, diag::conforms_to_missing_dot, conformsToValue);
3200-
return;
3201-
}
3202-
3203-
auto *mod = Impl.SwiftContext.getModuleByIdentifier(
3204-
Impl.SwiftContext.getIdentifier(moduleName));
3205-
if (!mod) {
3206-
HeaderLoc attrLoc(conformsToAttr->getLocation());
3207-
Impl.diagnose(attrLoc, diag::cannot_find_conforms_to_module,
3208-
conformsToValue, moduleName);
3209-
return;
3210-
}
3211-
3212-
SmallVector<ValueDecl *, 1> results;
3213-
mod->lookupValue(Impl.SwiftContext.getIdentifier(protocolName),
3214-
NLKind::UnqualifiedLookup, results);
3215-
if (results.empty()) {
3216-
HeaderLoc attrLoc(conformsToAttr->getLocation());
3217-
Impl.diagnose(attrLoc, diag::cannot_find_conforms_to, protocolName,
3218-
moduleName);
3219-
return;
3220-
}
3221-
3222-
if (results.size() != 1) {
3223-
HeaderLoc attrLoc(conformsToAttr->getLocation());
3224-
Impl.diagnose(attrLoc, diag::conforms_to_ambiguous, protocolName,
3225-
moduleName);
3226-
return;
3227-
}
3228-
3229-
auto result = results.front();
3230-
if (auto protocol = dyn_cast<ProtocolDecl>(result)) {
3231-
auto [_, inserted] = alreadyAdded.insert(protocol);
3232-
if (!inserted) {
3233-
HeaderLoc attrLoc(conformsToAttr->getLocation());
3234-
Impl.diagnose(attrLoc, diag::redundant_conformance_protocol,
3235-
decl->getDeclaredInterfaceType(), conformsToValue);
3236-
}
3237-
3238-
decl->getAttrs().add(
3239-
new (Impl.SwiftContext) SynthesizedProtocolAttr(protocol, &Impl, false));
3240-
} else {
3241-
HeaderLoc attrLoc((conformsToAttr)->getLocation());
3242-
Impl.diagnose(attrLoc, diag::conforms_to_not_protocol, result,
3243-
conformsToValue);
3244-
}
3245-
}
3246-
32473158
bool isSpecializationDepthGreaterThan(
32483159
const clang::ClassTemplateSpecializationDecl *decl, unsigned maxDepth) {
32493160
for (auto arg : decl->getTemplateArgs().asArray()) {
@@ -6608,6 +6519,20 @@ static bool conformsToProtocolInOriginalModule(NominalTypeDecl *nominal,
66086519
return false;
66096520
}
66106521

6522+
/// Determine whether the given nominal type was imported with an OptionSet
6523+
/// conformance.
6524+
static bool isImportedOptionSet(NominalTypeDecl *nominal) {
6525+
for (auto attr : nominal->getAttrs()) {
6526+
if (auto synthesizedAttr = dyn_cast<SynthesizedProtocolAttr>(attr)) {
6527+
if (synthesizedAttr->getProtocol()->isSpecificProtocol(
6528+
KnownProtocolKind::OptionSet))
6529+
return true;
6530+
}
6531+
}
6532+
6533+
return false;
6534+
}
6535+
66116536
Decl *
66126537
SwiftDeclConverter::importSwiftNewtype(const clang::TypedefNameDecl *decl,
66136538
clang::SwiftNewTypeAttr *newtypeAttr,
@@ -6682,6 +6607,11 @@ SwiftDeclConverter::importSwiftNewtype(const clang::TypedefNameDecl *decl,
66826607
addKnown(KnownProtocolKind::RawRepresentable);
66836608
addKnown(KnownProtocolKind::SwiftNewtypeWrapper);
66846609

6610+
// If this type was also imported as an OptionSet, include those typealiases.
6611+
if (isImportedOptionSet(structDecl)) {
6612+
Impl.addOptionSetTypealiases(structDecl);
6613+
}
6614+
66856615
// Local function to add a known protocol only when the
66866616
// underlying type conforms to it.
66876617
auto computedNominal = computedPropertyUnderlyingType->getAnyNominal();
@@ -6936,8 +6866,6 @@ Decl *SwiftDeclConverter::importEnumCaseAlias(
69366866
NominalTypeDecl *
69376867
SwiftDeclConverter::importAsOptionSetType(DeclContext *dc, Identifier name,
69386868
const clang::EnumDecl *decl) {
6939-
ASTContext &ctx = Impl.SwiftContext;
6940-
69416869
auto Loc = Impl.importSourceLoc(decl->getLocation());
69426870

69436871
// Create a struct with the underlying type as a field.
@@ -6956,10 +6884,7 @@ SwiftDeclConverter::importAsOptionSetType(DeclContext *dc, Identifier name,
69566884

69576885
synthesizer.makeStructRawValued(structDecl, underlyingType,
69586886
{KnownProtocolKind::OptionSet});
6959-
auto selfType = structDecl->getDeclaredInterfaceType();
6960-
Impl.addSynthesizedTypealias(structDecl, ctx.Id_Element, selfType);
6961-
Impl.addSynthesizedTypealias(structDecl, ctx.Id_ArrayLiteralElement,
6962-
selfType);
6887+
Impl.addOptionSetTypealiases(structDecl);
69636888
return structDecl;
69646889
}
69656890

@@ -8857,6 +8782,7 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
88578782

88588783
std::optional<const clang::SwiftAttrAttr *> seenMainActorAttr;
88598784
const clang::SwiftAttrAttr *seenMutabilityAttr = nullptr;
8785+
llvm::SmallSet<ProtocolDecl *, 4> conformancesSeen;
88608786

88618787
auto importAttrsFromDecl = [&](const clang::NamedDecl *ClangDecl) {
88628788
//
@@ -8972,6 +8898,11 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
89728898
continue;
89738899
}
89748900

8901+
if (swiftAttr->getAttribute().starts_with("conforms_to:")) {
8902+
if (auto nominal = dyn_cast<NominalTypeDecl>(MappedDecl))
8903+
addExplicitProtocolConformance(nominal, swiftAttr, conformancesSeen);
8904+
}
8905+
89758906
importNontrivialAttribute(MappedDecl, swiftAttr->getAttribute());
89768907
}
89778908

@@ -9034,6 +8965,14 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
90348965
}
90358966
}
90368967

8968+
// Import explicit conformances from C++ base classes.
8969+
if (auto nominal = dyn_cast<NominalTypeDecl>(MappedDecl)) {
8970+
if (auto cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(ClangDecl)) {
8971+
addExplicitProtocolConformancesFromBases(
8972+
nominal, cxxRecordDecl, /*isBase=*/false);
8973+
}
8974+
}
8975+
90378976
// Now that we've collected all @Sendable and @_nonSendable attributes, we
90388977
// can see if we should synthesize a Sendable conformance.
90398978
if (auto nominal = dyn_cast<NominalTypeDecl>(MappedDecl)) {
@@ -9302,6 +9241,102 @@ struct UnaliasedInstantiationVisitor
93029241
};
93039242
} // namespace
93049243

9244+
9245+
9246+
void ClangImporter::Implementation::addExplicitProtocolConformance(
9247+
NominalTypeDecl *decl,
9248+
clang::SwiftAttrAttr *conformsToAttr,
9249+
llvm::SmallSet<ProtocolDecl *, 4> &alreadyAdded) {
9250+
auto conformsToValue = conformsToAttr->getAttribute()
9251+
.drop_front(StringRef("conforms_to:").size())
9252+
.str();
9253+
auto names = StringRef(conformsToValue).split('.');
9254+
auto moduleName = names.first;
9255+
auto protocolName = names.second;
9256+
if (protocolName.empty()) {
9257+
HeaderLoc attrLoc(conformsToAttr->getLocation());
9258+
diagnose(attrLoc, diag::conforms_to_missing_dot, conformsToValue);
9259+
return;
9260+
}
9261+
9262+
auto *mod = SwiftContext.getModuleByIdentifier(
9263+
SwiftContext.getIdentifier(moduleName));
9264+
if (!mod) {
9265+
HeaderLoc attrLoc(conformsToAttr->getLocation());
9266+
diagnose(attrLoc, diag::cannot_find_conforms_to_module,
9267+
conformsToValue, moduleName);
9268+
return;
9269+
}
9270+
9271+
SmallVector<ValueDecl *, 1> results;
9272+
mod->lookupValue(SwiftContext.getIdentifier(protocolName),
9273+
NLKind::UnqualifiedLookup, results);
9274+
if (results.empty()) {
9275+
HeaderLoc attrLoc(conformsToAttr->getLocation());
9276+
diagnose(attrLoc, diag::cannot_find_conforms_to, protocolName,
9277+
moduleName);
9278+
return;
9279+
}
9280+
9281+
if (results.size() != 1) {
9282+
HeaderLoc attrLoc(conformsToAttr->getLocation());
9283+
diagnose(attrLoc, diag::conforms_to_ambiguous, protocolName,
9284+
moduleName);
9285+
return;
9286+
}
9287+
9288+
auto result = results.front();
9289+
if (auto protocol = dyn_cast<ProtocolDecl>(result)) {
9290+
auto [_, inserted] = alreadyAdded.insert(protocol);
9291+
if (!inserted) {
9292+
HeaderLoc attrLoc(conformsToAttr->getLocation());
9293+
diagnose(attrLoc, diag::redundant_conformance_protocol,
9294+
decl->getDeclaredInterfaceType(), conformsToValue);
9295+
}
9296+
9297+
decl->getAttrs().add(
9298+
new (SwiftContext) SynthesizedProtocolAttr(protocol, this, false));
9299+
} else {
9300+
HeaderLoc attrLoc((conformsToAttr)->getLocation());
9301+
diagnose(attrLoc, diag::conforms_to_not_protocol, result,
9302+
conformsToValue);
9303+
}
9304+
}
9305+
9306+
void ClangImporter::Implementation::addExplicitProtocolConformancesFromBases(
9307+
NominalTypeDecl *nominal,
9308+
const clang::CXXRecordDecl *cxxRecordDecl,
9309+
bool isBase) {
9310+
if (cxxRecordDecl->isCompleteDefinition()) {
9311+
// Propagate conforms_to attribute from public base classes.
9312+
for (auto base : cxxRecordDecl->bases()) {
9313+
if (base.getAccessSpecifier() != clang::AccessSpecifier::AS_public)
9314+
continue;
9315+
if (auto *baseClangDecl = base.getType()->getAsCXXRecordDecl())
9316+
addExplicitProtocolConformancesFromBases(nominal, baseClangDecl,
9317+
/*isBase=*/true);
9318+
}
9319+
}
9320+
9321+
if (isBase && cxxRecordDecl->hasAttrs()) {
9322+
llvm::SmallSet<ProtocolDecl *, 4> alreadyAdded;
9323+
llvm::for_each(cxxRecordDecl->getAttrs(), [&](auto *attr) {
9324+
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
9325+
if (swiftAttr->getAttribute().starts_with("conforms_to:"))
9326+
addExplicitProtocolConformance(nominal, swiftAttr, alreadyAdded);
9327+
}
9328+
});
9329+
}
9330+
}
9331+
9332+
void ClangImporter::Implementation::addOptionSetTypealiases(
9333+
NominalTypeDecl *nominal) {
9334+
auto selfType = nominal->getDeclaredInterfaceType();
9335+
addSynthesizedTypealias(nominal, SwiftContext.Id_Element, selfType);
9336+
addSynthesizedTypealias(nominal, SwiftContext.Id_ArrayLiteralElement,
9337+
selfType);
9338+
}
9339+
93059340
void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
93069341
if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers))
93079342
return;

lib/ClangImporter/ImporterImpl.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1794,6 +1794,25 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
17941794
}
17951795

17961796
void importSwiftAttrAttributes(Decl *decl);
1797+
1798+
/// Add the explicit protocol conformance specified by the given swift_attr
1799+
/// attribute to the given nominal type.
1800+
void addExplicitProtocolConformance(
1801+
NominalTypeDecl *decl,
1802+
clang::SwiftAttrAttr *conformsToAttr,
1803+
llvm::SmallSet<ProtocolDecl *, 4> &alreadyAdded);
1804+
1805+
/// Add explicit protocol conformances from the bases of the given C++ class
1806+
/// declaration's swift_attr attributes to the given nominal type.
1807+
void addExplicitProtocolConformancesFromBases(
1808+
NominalTypeDecl *nominal,
1809+
const clang::CXXRecordDecl *cxxRecordDecl,
1810+
bool isBase);
1811+
1812+
/// Add implicit typealiases required when turning the given nominal type
1813+
/// into an option set.
1814+
void addOptionSetTypealiases(NominalTypeDecl *nominal);
1815+
17971816
void swiftify(AbstractFunctionDecl *MappedDecl);
17981817

17991818
/// Find the lookup table that corresponds to the given Clang module.

test/Interop/C/struct/Inputs/module.modulemap

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,8 @@ module StructDeclContext {
55

66
module ForeignReference {
77
header "foreign-reference.h"
8-
}
8+
}
9+
10+
module StructAsOptionSet {
11+
header "struct-as-option-set.h"
12+
}

0 commit comments

Comments
 (0)