Skip to content

Commit 8a4d30c

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. (cherry picked from commit c5db5cc)
1 parent 7a67b35 commit 8a4d30c

File tree

3 files changed

+152
-80
lines changed

3 files changed

+152
-80
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 128 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -3132,82 +3132,9 @@ namespace {
31323132
}
31333133
}
31343134

3135-
if (auto *ntd = dyn_cast<NominalTypeDecl>(result))
3136-
addExplicitProtocolConformances(ntd, decl);
3137-
31383135
return result;
31393136
}
31403137

3141-
void
3142-
addExplicitProtocolConformances(NominalTypeDecl *decl,
3143-
const clang::CXXRecordDecl *clangDecl) {
3144-
// Propagate conforms_to attribute from public base classes.
3145-
for (auto base : clangDecl->bases()) {
3146-
if (base.getAccessSpecifier() != clang::AccessSpecifier::AS_public)
3147-
continue;
3148-
if (auto *baseClangDecl = base.getType()->getAsCXXRecordDecl())
3149-
addExplicitProtocolConformances(decl, baseClangDecl);
3150-
}
3151-
3152-
if (!clangDecl->hasAttrs())
3153-
return;
3154-
3155-
SmallVector<ValueDecl *, 1> results;
3156-
auto conformsToAttr =
3157-
llvm::find_if(clangDecl->getAttrs(), [](auto *attr) {
3158-
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
3159-
return swiftAttr->getAttribute().starts_with("conforms_to:");
3160-
return false;
3161-
});
3162-
if (conformsToAttr == clangDecl->getAttrs().end())
3163-
return;
3164-
3165-
auto conformsToValue = cast<clang::SwiftAttrAttr>(*conformsToAttr)
3166-
->getAttribute()
3167-
.drop_front(StringRef("conforms_to:").size())
3168-
.str();
3169-
auto names = StringRef(conformsToValue).split('.');
3170-
auto moduleName = names.first;
3171-
auto protocolName = names.second;
3172-
if (protocolName.empty()) {
3173-
HeaderLoc attrLoc((*conformsToAttr)->getLocation());
3174-
Impl.diagnose(attrLoc, diag::conforms_to_missing_dot, conformsToValue);
3175-
return;
3176-
}
3177-
3178-
auto *mod = Impl.SwiftContext.getModuleByIdentifier(
3179-
Impl.SwiftContext.getIdentifier(moduleName));
3180-
if (!mod) {
3181-
HeaderLoc attrLoc((*conformsToAttr)->getLocation());
3182-
Impl.diagnose(attrLoc, diag::cannot_find_conforms_to_module,
3183-
conformsToValue, moduleName);
3184-
return;
3185-
}
3186-
mod->lookupValue(Impl.SwiftContext.getIdentifier(protocolName),
3187-
NLKind::UnqualifiedLookup, results);
3188-
if (results.empty()) {
3189-
HeaderLoc attrLoc((*conformsToAttr)->getLocation());
3190-
Impl.diagnose(attrLoc, diag::cannot_find_conforms_to, protocolName,
3191-
moduleName);
3192-
return;
3193-
} else if (results.size() != 1) {
3194-
HeaderLoc attrLoc((*conformsToAttr)->getLocation());
3195-
Impl.diagnose(attrLoc, diag::conforms_to_ambiguous, protocolName,
3196-
moduleName);
3197-
return;
3198-
}
3199-
3200-
auto result = results.front();
3201-
if (auto protocol = dyn_cast<ProtocolDecl>(result)) {
3202-
decl->getAttrs().add(
3203-
new (Impl.SwiftContext) SynthesizedProtocolAttr(protocol, &Impl, false));
3204-
} else {
3205-
HeaderLoc attrLoc((*conformsToAttr)->getLocation());
3206-
Impl.diagnose(attrLoc, diag::conforms_to_not_protocol,
3207-
result->getDescriptiveKind(), result, conformsToValue);
3208-
}
3209-
}
3210-
32113138
bool isSpecializationDepthGreaterThan(
32123139
const clang::ClassTemplateSpecializationDecl *decl, unsigned maxDepth) {
32133140
for (auto arg : decl->getTemplateArgs().asArray()) {
@@ -6507,6 +6434,20 @@ static bool conformsToProtocolInOriginalModule(NominalTypeDecl *nominal,
65076434
return false;
65086435
}
65096436

6437+
/// Determine whether the given nominal type was imported with an OptionSet
6438+
/// conformance.
6439+
static bool isImportedOptionSet(NominalTypeDecl *nominal) {
6440+
for (auto attr : nominal->getAttrs()) {
6441+
if (auto synthesizedAttr = dyn_cast<SynthesizedProtocolAttr>(attr)) {
6442+
if (synthesizedAttr->getProtocol()->isSpecificProtocol(
6443+
KnownProtocolKind::OptionSet))
6444+
return true;
6445+
}
6446+
}
6447+
6448+
return false;
6449+
}
6450+
65106451
Decl *
65116452
SwiftDeclConverter::importSwiftNewtype(const clang::TypedefNameDecl *decl,
65126453
clang::SwiftNewTypeAttr *newtypeAttr,
@@ -6581,6 +6522,11 @@ SwiftDeclConverter::importSwiftNewtype(const clang::TypedefNameDecl *decl,
65816522
addKnown(KnownProtocolKind::RawRepresentable);
65826523
addKnown(KnownProtocolKind::SwiftNewtypeWrapper);
65836524

6525+
// If this type was also imported as an OptionSet, include those typealiases.
6526+
if (isImportedOptionSet(structDecl)) {
6527+
Impl.addOptionSetTypealiases(structDecl);
6528+
}
6529+
65846530
// Local function to add a known protocol only when the
65856531
// underlying type conforms to it.
65866532
auto computedNominal = computedPropertyUnderlyingType->getAnyNominal();
@@ -6835,8 +6781,6 @@ Decl *SwiftDeclConverter::importEnumCaseAlias(
68356781
NominalTypeDecl *
68366782
SwiftDeclConverter::importAsOptionSetType(DeclContext *dc, Identifier name,
68376783
const clang::EnumDecl *decl) {
6838-
ASTContext &ctx = Impl.SwiftContext;
6839-
68406784
auto Loc = Impl.importSourceLoc(decl->getLocation());
68416785

68426786
// Create a struct with the underlying type as a field.
@@ -6855,10 +6799,7 @@ SwiftDeclConverter::importAsOptionSetType(DeclContext *dc, Identifier name,
68556799

68566800
synthesizer.makeStructRawValued(structDecl, underlyingType,
68576801
{KnownProtocolKind::OptionSet});
6858-
auto selfType = structDecl->getDeclaredInterfaceType();
6859-
Impl.addSynthesizedTypealias(structDecl, ctx.Id_Element, selfType);
6860-
Impl.addSynthesizedTypealias(structDecl, ctx.Id_ArrayLiteralElement,
6861-
selfType);
6802+
Impl.addOptionSetTypealiases(structDecl);
68626803
return structDecl;
68636804
}
68646805

@@ -8756,6 +8697,7 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
87568697

87578698
std::optional<const clang::SwiftAttrAttr *> seenMainActorAttr;
87588699
const clang::SwiftAttrAttr *seenMutabilityAttr = nullptr;
8700+
llvm::SmallSet<ProtocolDecl *, 4> conformancesSeen;
87598701

87608702
auto importAttrsFromDecl = [&](const clang::NamedDecl *ClangDecl) {
87618703
//
@@ -8871,6 +8813,11 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
88718813
continue;
88728814
}
88738815

8816+
if (swiftAttr->getAttribute().starts_with("conforms_to:")) {
8817+
if (auto nominal = dyn_cast<NominalTypeDecl>(MappedDecl))
8818+
addExplicitProtocolConformance(nominal, swiftAttr, conformancesSeen);
8819+
}
8820+
88748821
importNontrivialAttribute(MappedDecl, swiftAttr->getAttribute());
88758822
}
88768823

@@ -8933,6 +8880,14 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
89338880
}
89348881
}
89358882

8883+
// Import explicit conformances from C++ base classes.
8884+
if (auto nominal = dyn_cast<NominalTypeDecl>(MappedDecl)) {
8885+
if (auto cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(ClangDecl)) {
8886+
addExplicitProtocolConformancesFromBases(
8887+
nominal, cxxRecordDecl, /*isBase=*/false);
8888+
}
8889+
}
8890+
89368891
// Now that we've collected all @Sendable and @_nonSendable attributes, we
89378892
// can see if we should synthesize a Sendable conformance.
89388893
if (auto nominal = dyn_cast<NominalTypeDecl>(MappedDecl)) {
@@ -9200,6 +9155,100 @@ static bool SwiftifiableCAT(const clang::ASTContext &ctx,
92009155
: SwiftifiableCountedByPointerType(swiftType));
92019156
}
92029157

9158+
void ClangImporter::Implementation::addExplicitProtocolConformance(
9159+
NominalTypeDecl *decl,
9160+
clang::SwiftAttrAttr *conformsToAttr,
9161+
llvm::SmallSet<ProtocolDecl *, 4> &alreadyAdded) {
9162+
auto conformsToValue = conformsToAttr->getAttribute()
9163+
.drop_front(StringRef("conforms_to:").size())
9164+
.str();
9165+
auto names = StringRef(conformsToValue).split('.');
9166+
auto moduleName = names.first;
9167+
auto protocolName = names.second;
9168+
if (protocolName.empty()) {
9169+
HeaderLoc attrLoc(conformsToAttr->getLocation());
9170+
diagnose(attrLoc, diag::conforms_to_missing_dot, conformsToValue);
9171+
return;
9172+
}
9173+
9174+
auto *mod = SwiftContext.getModuleByIdentifier(
9175+
SwiftContext.getIdentifier(moduleName));
9176+
if (!mod) {
9177+
HeaderLoc attrLoc(conformsToAttr->getLocation());
9178+
diagnose(attrLoc, diag::cannot_find_conforms_to_module,
9179+
conformsToValue, moduleName);
9180+
return;
9181+
}
9182+
9183+
SmallVector<ValueDecl *, 1> results;
9184+
mod->lookupValue(SwiftContext.getIdentifier(protocolName),
9185+
NLKind::UnqualifiedLookup, results);
9186+
if (results.empty()) {
9187+
HeaderLoc attrLoc(conformsToAttr->getLocation());
9188+
diagnose(attrLoc, diag::cannot_find_conforms_to, protocolName,
9189+
moduleName);
9190+
return;
9191+
}
9192+
9193+
if (results.size() != 1) {
9194+
HeaderLoc attrLoc(conformsToAttr->getLocation());
9195+
diagnose(attrLoc, diag::conforms_to_ambiguous, protocolName,
9196+
moduleName);
9197+
return;
9198+
}
9199+
9200+
auto result = results.front();
9201+
if (auto protocol = dyn_cast<ProtocolDecl>(result)) {
9202+
auto [_, inserted] = alreadyAdded.insert(protocol);
9203+
if (!inserted) {
9204+
HeaderLoc attrLoc(conformsToAttr->getLocation());
9205+
diagnose(attrLoc, diag::redundant_conformance_protocol,
9206+
decl->getDeclaredInterfaceType(), conformsToValue);
9207+
}
9208+
9209+
decl->getAttrs().add(
9210+
new (SwiftContext) SynthesizedProtocolAttr(protocol, this, false));
9211+
} else {
9212+
HeaderLoc attrLoc((conformsToAttr)->getLocation());
9213+
diagnose(attrLoc, diag::conforms_to_not_protocol, result,
9214+
conformsToValue);
9215+
}
9216+
}
9217+
9218+
void ClangImporter::Implementation::addExplicitProtocolConformancesFromBases(
9219+
NominalTypeDecl *nominal,
9220+
const clang::CXXRecordDecl *cxxRecordDecl,
9221+
bool isBase) {
9222+
if (cxxRecordDecl->isCompleteDefinition()) {
9223+
// Propagate conforms_to attribute from public base classes.
9224+
for (auto base : cxxRecordDecl->bases()) {
9225+
if (base.getAccessSpecifier() != clang::AccessSpecifier::AS_public)
9226+
continue;
9227+
if (auto *baseClangDecl = base.getType()->getAsCXXRecordDecl())
9228+
addExplicitProtocolConformancesFromBases(nominal, baseClangDecl,
9229+
/*isBase=*/true);
9230+
}
9231+
}
9232+
9233+
if (isBase && cxxRecordDecl->hasAttrs()) {
9234+
llvm::SmallSet<ProtocolDecl *, 4> alreadyAdded;
9235+
llvm::for_each(cxxRecordDecl->getAttrs(), [&](auto *attr) {
9236+
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
9237+
if (swiftAttr->getAttribute().starts_with("conforms_to:"))
9238+
addExplicitProtocolConformance(nominal, swiftAttr, alreadyAdded);
9239+
}
9240+
});
9241+
}
9242+
}
9243+
9244+
void ClangImporter::Implementation::addOptionSetTypealiases(
9245+
NominalTypeDecl *nominal) {
9246+
auto selfType = nominal->getDeclaredInterfaceType();
9247+
addSynthesizedTypealias(nominal, SwiftContext.Id_Element, selfType);
9248+
addSynthesizedTypealias(nominal, SwiftContext.Id_ArrayLiteralElement,
9249+
selfType);
9250+
}
9251+
92039252
void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
92049253
if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers))
92059254
return;

lib/ClangImporter/ImporterImpl.h

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

17621762
void importSwiftAttrAttributes(Decl *decl);
1763+
1764+
/// Add the explicit protocol conformance specified by the given swift_attr
1765+
/// attribute to the given nominal type.
1766+
void addExplicitProtocolConformance(
1767+
NominalTypeDecl *decl,
1768+
clang::SwiftAttrAttr *conformsToAttr,
1769+
llvm::SmallSet<ProtocolDecl *, 4> &alreadyAdded);
1770+
1771+
/// Add explicit protocol conformances from the bases of the given C++ class
1772+
/// declaration's swift_attr attributes to the given nominal type.
1773+
void addExplicitProtocolConformancesFromBases(
1774+
NominalTypeDecl *nominal,
1775+
const clang::CXXRecordDecl *cxxRecordDecl,
1776+
bool isBase);
1777+
1778+
/// Add implicit typealiases required when turning the given nominal type
1779+
/// into an option set.
1780+
void addOptionSetTypealiases(NominalTypeDecl *nominal);
1781+
17631782
void swiftify(AbstractFunctionDecl *MappedDecl);
17641783

17651784
/// 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)