Skip to content

Commit c5db5cc

Browse files
committed
[Clang importer] Handle Swift conformances on typedefs and C types
The code for handling Swift conforms_to attributes was specific to C++ record types. Generalize it to work on typedefs imported as nominal types, also in C. Fixes rdar://156290361.
1 parent 0e755d4 commit c5db5cc

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
@@ -3150,98 +3150,9 @@ namespace {
31503150
}
31513151
}
31523152

3153-
if (auto *ntd = dyn_cast<NominalTypeDecl>(result))
3154-
addExplicitProtocolConformances(ntd, decl);
3155-
31563153
return result;
31573154
}
31583155

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

6547+
/// Determine whether the given nominal type was imported with an OptionSet
6548+
/// conformance.
6549+
static bool isImportedOptionSet(NominalTypeDecl *nominal) {
6550+
for (auto attr : nominal->getAttrs()) {
6551+
if (auto synthesizedAttr = dyn_cast<SynthesizedProtocolAttr>(attr)) {
6552+
if (synthesizedAttr->getProtocol()->isSpecificProtocol(
6553+
KnownProtocolKind::OptionSet))
6554+
return true;
6555+
}
6556+
}
6557+
6558+
return false;
6559+
}
6560+
66366561
Decl *
66376562
SwiftDeclConverter::importSwiftNewtype(const clang::TypedefNameDecl *decl,
66386563
clang::SwiftNewTypeAttr *newtypeAttr,
@@ -6707,6 +6632,11 @@ SwiftDeclConverter::importSwiftNewtype(const clang::TypedefNameDecl *decl,
67076632
addKnown(KnownProtocolKind::RawRepresentable);
67086633
addKnown(KnownProtocolKind::SwiftNewtypeWrapper);
67096634

6635+
// If this type was also imported as an OptionSet, include those typealiases.
6636+
if (isImportedOptionSet(structDecl)) {
6637+
Impl.addOptionSetTypealiases(structDecl);
6638+
}
6639+
67106640
// Local function to add a known protocol only when the
67116641
// underlying type conforms to it.
67126642
auto computedNominal = computedPropertyUnderlyingType->getAnyNominal();
@@ -6961,8 +6891,6 @@ Decl *SwiftDeclConverter::importEnumCaseAlias(
69616891
NominalTypeDecl *
69626892
SwiftDeclConverter::importAsOptionSetType(DeclContext *dc, Identifier name,
69636893
const clang::EnumDecl *decl) {
6964-
ASTContext &ctx = Impl.SwiftContext;
6965-
69666894
auto Loc = Impl.importSourceLoc(decl->getLocation());
69676895

69686896
// Create a struct with the underlying type as a field.
@@ -6981,10 +6909,7 @@ SwiftDeclConverter::importAsOptionSetType(DeclContext *dc, Identifier name,
69816909

69826910
synthesizer.makeStructRawValued(structDecl, underlyingType,
69836911
{KnownProtocolKind::OptionSet});
6984-
auto selfType = structDecl->getDeclaredInterfaceType();
6985-
Impl.addSynthesizedTypealias(structDecl, ctx.Id_Element, selfType);
6986-
Impl.addSynthesizedTypealias(structDecl, ctx.Id_ArrayLiteralElement,
6987-
selfType);
6912+
Impl.addOptionSetTypealiases(structDecl);
69886913
return structDecl;
69896914
}
69906915

@@ -8882,6 +8807,7 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
88828807

88838808
std::optional<const clang::SwiftAttrAttr *> seenMainActorAttr;
88848809
const clang::SwiftAttrAttr *seenMutabilityAttr = nullptr;
8810+
llvm::SmallSet<ProtocolDecl *, 4> conformancesSeen;
88858811

88868812
auto importAttrsFromDecl = [&](const clang::NamedDecl *ClangDecl) {
88878813
//
@@ -8997,6 +8923,11 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
89978923
continue;
89988924
}
89998925

8926+
if (swiftAttr->getAttribute().starts_with("conforms_to:")) {
8927+
if (auto nominal = dyn_cast<NominalTypeDecl>(MappedDecl))
8928+
addExplicitProtocolConformance(nominal, swiftAttr, conformancesSeen);
8929+
}
8930+
90008931
importNontrivialAttribute(MappedDecl, swiftAttr->getAttribute());
90018932
}
90028933

@@ -9059,6 +8990,14 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
90598990
}
90608991
}
90618992

8993+
// Import explicit conformances from C++ base classes.
8994+
if (auto nominal = dyn_cast<NominalTypeDecl>(MappedDecl)) {
8995+
if (auto cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(ClangDecl)) {
8996+
addExplicitProtocolConformancesFromBases(
8997+
nominal, cxxRecordDecl, /*isBase=*/false);
8998+
}
8999+
}
9000+
90629001
// Now that we've collected all @Sendable and @_nonSendable attributes, we
90639002
// can see if we should synthesize a Sendable conformance.
90649003
if (auto nominal = dyn_cast<NominalTypeDecl>(MappedDecl)) {
@@ -9327,6 +9266,102 @@ struct UnaliasedInstantiationVisitor
93279266
};
93289267
} // namespace
93299268

9269+
9270+
9271+
void ClangImporter::Implementation::addExplicitProtocolConformance(
9272+
NominalTypeDecl *decl,
9273+
clang::SwiftAttrAttr *conformsToAttr,
9274+
llvm::SmallSet<ProtocolDecl *, 4> &alreadyAdded) {
9275+
auto conformsToValue = conformsToAttr->getAttribute()
9276+
.drop_front(StringRef("conforms_to:").size())
9277+
.str();
9278+
auto names = StringRef(conformsToValue).split('.');
9279+
auto moduleName = names.first;
9280+
auto protocolName = names.second;
9281+
if (protocolName.empty()) {
9282+
HeaderLoc attrLoc(conformsToAttr->getLocation());
9283+
diagnose(attrLoc, diag::conforms_to_missing_dot, conformsToValue);
9284+
return;
9285+
}
9286+
9287+
auto *mod = SwiftContext.getModuleByIdentifier(
9288+
SwiftContext.getIdentifier(moduleName));
9289+
if (!mod) {
9290+
HeaderLoc attrLoc(conformsToAttr->getLocation());
9291+
diagnose(attrLoc, diag::cannot_find_conforms_to_module,
9292+
conformsToValue, moduleName);
9293+
return;
9294+
}
9295+
9296+
SmallVector<ValueDecl *, 1> results;
9297+
mod->lookupValue(SwiftContext.getIdentifier(protocolName),
9298+
NLKind::UnqualifiedLookup, results);
9299+
if (results.empty()) {
9300+
HeaderLoc attrLoc(conformsToAttr->getLocation());
9301+
diagnose(attrLoc, diag::cannot_find_conforms_to, protocolName,
9302+
moduleName);
9303+
return;
9304+
}
9305+
9306+
if (results.size() != 1) {
9307+
HeaderLoc attrLoc(conformsToAttr->getLocation());
9308+
diagnose(attrLoc, diag::conforms_to_ambiguous, protocolName,
9309+
moduleName);
9310+
return;
9311+
}
9312+
9313+
auto result = results.front();
9314+
if (auto protocol = dyn_cast<ProtocolDecl>(result)) {
9315+
auto [_, inserted] = alreadyAdded.insert(protocol);
9316+
if (!inserted) {
9317+
HeaderLoc attrLoc(conformsToAttr->getLocation());
9318+
diagnose(attrLoc, diag::redundant_conformance_protocol,
9319+
decl->getDeclaredInterfaceType(), conformsToValue);
9320+
}
9321+
9322+
decl->getAttrs().add(
9323+
new (SwiftContext) SynthesizedProtocolAttr(protocol, this, false));
9324+
} else {
9325+
HeaderLoc attrLoc((conformsToAttr)->getLocation());
9326+
diagnose(attrLoc, diag::conforms_to_not_protocol, result,
9327+
conformsToValue);
9328+
}
9329+
}
9330+
9331+
void ClangImporter::Implementation::addExplicitProtocolConformancesFromBases(
9332+
NominalTypeDecl *nominal,
9333+
const clang::CXXRecordDecl *cxxRecordDecl,
9334+
bool isBase) {
9335+
if (cxxRecordDecl->isCompleteDefinition()) {
9336+
// Propagate conforms_to attribute from public base classes.
9337+
for (auto base : cxxRecordDecl->bases()) {
9338+
if (base.getAccessSpecifier() != clang::AccessSpecifier::AS_public)
9339+
continue;
9340+
if (auto *baseClangDecl = base.getType()->getAsCXXRecordDecl())
9341+
addExplicitProtocolConformancesFromBases(nominal, baseClangDecl,
9342+
/*isBase=*/true);
9343+
}
9344+
}
9345+
9346+
if (isBase && cxxRecordDecl->hasAttrs()) {
9347+
llvm::SmallSet<ProtocolDecl *, 4> alreadyAdded;
9348+
llvm::for_each(cxxRecordDecl->getAttrs(), [&](auto *attr) {
9349+
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
9350+
if (swiftAttr->getAttribute().starts_with("conforms_to:"))
9351+
addExplicitProtocolConformance(nominal, swiftAttr, alreadyAdded);
9352+
}
9353+
});
9354+
}
9355+
}
9356+
9357+
void ClangImporter::Implementation::addOptionSetTypealiases(
9358+
NominalTypeDecl *nominal) {
9359+
auto selfType = nominal->getDeclaredInterfaceType();
9360+
addSynthesizedTypealias(nominal, SwiftContext.Id_Element, selfType);
9361+
addSynthesizedTypealias(nominal, SwiftContext.Id_ArrayLiteralElement,
9362+
selfType);
9363+
}
9364+
93309365
void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
93319366
if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers))
93329367
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)