Skip to content

Commit 30429a3

Browse files
committed
Sema: Fix the insertion location for conformances attributes
1 parent 9488df1 commit 30429a3

18 files changed

+317
-86
lines changed

include/swift/AST/ASTContext.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,10 +1333,10 @@ class ASTContext final {
13331333
getNormalConformance(Type conformingType,
13341334
ProtocolDecl *protocol,
13351335
SourceLoc loc,
1336+
TypeRepr *inheritedTypeRepr,
13361337
DeclContext *dc,
13371338
ProtocolConformanceState state,
1338-
ProtocolConformanceOptions options,
1339-
SourceLoc preconcurrencyLoc = SourceLoc());
1339+
ProtocolConformanceOptions options);
13401340

13411341
/// Produce a self-conformance for the given protocol.
13421342
SelfProtocolConformance *

include/swift/AST/NameLookup.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,11 @@ void forEachPotentialAttachedMacro(
604604

605605
/// Describes an inherited nominal entry.
606606
struct InheritedNominalEntry : Located<NominalTypeDecl *> {
607+
/// The `TypeRepr` of the inheritance clause entry from which this nominal was
608+
/// sourced, if any. For example, if this is a conformance to `Y` declared as
609+
/// `struct S: X, Y & Z {}`, this is the `TypeRepr` for `Y & Z`.
610+
TypeRepr *inheritedTypeRepr;
611+
607612
ConformanceAttributes attributes;
608613

609614
/// Whether this inherited entry was suppressed via "~".
@@ -612,10 +617,10 @@ struct InheritedNominalEntry : Located<NominalTypeDecl *> {
612617
InheritedNominalEntry() { }
613618

614619
InheritedNominalEntry(NominalTypeDecl *item, SourceLoc loc,
615-
ConformanceAttributes attributes,
616-
bool isSuppressed)
617-
: Located(item, loc), attributes(attributes),
618-
isSuppressed(isSuppressed) {}
620+
TypeRepr *inheritedTypeRepr,
621+
ConformanceAttributes attributes, bool isSuppressed)
622+
: Located(item, loc), inheritedTypeRepr(inheritedTypeRepr),
623+
attributes(attributes), isSuppressed(isSuppressed) {}
619624
};
620625

621626
/// Retrieve the set of nominal type declarations that are directly

include/swift/AST/ProtocolConformance.h

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -582,10 +582,17 @@ class NormalProtocolConformance : public RootProtocolConformance,
582582
SourceLoc Loc;
583583

584584
/// The location of the protocol name within the conformance.
585+
///
586+
/// - Important: This is not a valid insertion location for an attribute.
587+
/// Use `applyConformanceAttribute` instead.
585588
SourceLoc ProtocolNameLoc;
586589

587-
/// The location of the `@preconcurrency` attribute, if any.
588-
SourceLoc PreconcurrencyLoc;
590+
/// The `TypeRepr` of the inheritance clause entry that declares this
591+
/// conformance, if any. For example, if this is a conformance to `Y`
592+
/// declared as `struct S: X, Y & Z {}`, this is the `TypeRepr` for `Y & Z`.
593+
///
594+
/// - Important: The value can be valid only for an explicit conformance.
595+
TypeRepr *inheritedTypeRepr;
589596

590597
/// The declaration context containing the ExtensionDecl or
591598
/// NominalTypeDecl that declared the conformance.
@@ -621,22 +628,26 @@ class NormalProtocolConformance : public RootProtocolConformance,
621628
// Record the explicitly-specified global actor isolation.
622629
void setExplicitGlobalActorIsolation(TypeExpr *typeExpr);
623630

631+
/// Return the `TypeRepr` of the inheritance clause entry that declares this
632+
/// conformance, if any. For example, if this is a conformance to `Y`
633+
/// declared as `struct S: X, Y & Z {}`, this is the `TypeRepr` for `Y & Z`.
634+
///
635+
/// - Important: The value can be valid only for an explicit conformance.
636+
TypeRepr *getInheritedTypeRepr() const { return inheritedTypeRepr; }
637+
624638
public:
625639
NormalProtocolConformance(Type conformingType, ProtocolDecl *protocol,
626-
SourceLoc loc, DeclContext *dc,
627-
ProtocolConformanceState state,
628-
ProtocolConformanceOptions options,
629-
SourceLoc preconcurrencyLoc)
640+
SourceLoc loc, TypeRepr *inheritedTypeRepr,
641+
DeclContext *dc, ProtocolConformanceState state,
642+
ProtocolConformanceOptions options)
630643
: RootProtocolConformance(ProtocolConformanceKind::Normal,
631644
conformingType),
632645
Protocol(protocol), Loc(extractNearestSourceLoc(dc)),
633-
ProtocolNameLoc(loc), PreconcurrencyLoc(preconcurrencyLoc),
646+
ProtocolNameLoc(loc), inheritedTypeRepr(inheritedTypeRepr),
634647
Context(dc) {
635648
assert(!conformingType->hasArchetype() &&
636649
"ProtocolConformances should store interface types");
637-
assert((preconcurrencyLoc.isInvalid() ||
638-
options.contains(ProtocolConformanceFlags::Preconcurrency)) &&
639-
"Cannot have a @preconcurrency location without isPreconcurrency");
650+
640651
setState(state);
641652
Bits.NormalProtocolConformance.IsInvalid = false;
642653
Bits.NormalProtocolConformance.IsPreconcurrencyEffectful = false;
@@ -646,6 +657,9 @@ class NormalProtocolConformance : public RootProtocolConformance,
646657
unsigned(ConformanceEntryKind::Explicit);
647658
Bits.NormalProtocolConformance.HasExplicitGlobalActor = false;
648659
setExplicitGlobalActorIsolation(options.getGlobalActorIsolationType());
660+
661+
assert((!getPreconcurrencyLoc() || isPreconcurrency()) &&
662+
"Cannot have a @preconcurrency location without isPreconcurrency");
649663
}
650664

651665
/// Get the protocol being conformed to.
@@ -655,6 +669,9 @@ class NormalProtocolConformance : public RootProtocolConformance,
655669
SourceLoc getLoc() const { return Loc; }
656670

657671
/// Retrieve the name of the protocol location.
672+
///
673+
/// - Important: This is not a valid insertion location for an attribute.
674+
/// Use `applyConformanceAttribute` instead.
658675
SourceLoc getProtocolNameLoc() const { return ProtocolNameLoc; }
659676

660677
/// Get the declaration context that contains the conforming extension or
@@ -725,7 +742,9 @@ class NormalProtocolConformance : public RootProtocolConformance,
725742

726743
/// Retrieve the location of `@preconcurrency`, if there is one and it is
727744
/// known.
728-
SourceLoc getPreconcurrencyLoc() const { return PreconcurrencyLoc; }
745+
///
746+
/// - Important: The value can be valid only for an explicit conformance.
747+
SourceLoc getPreconcurrencyLoc() const;
729748

730749
/// Query whether this conformance was explicitly declared to be safe or
731750
/// unsafe.
@@ -849,6 +868,15 @@ class NormalProtocolConformance : public RootProtocolConformance,
849868
/// Triggers a request that resolves all of the conformance's value witnesses.
850869
void resolveValueWitnesses() const;
851870

871+
/// If this conformance is explicit and carries the necessary source location
872+
/// information, attaches a fix-it to the given diagnostic for applying the
873+
/// given conformance attribute to the conformance.
874+
///
875+
/// \param attrStr A conformance attribute as a string, e.g. "@unsafe" or
876+
/// "nonisolated".
877+
void applyConformanceAttribute(InFlightDiagnostic &diag,
878+
std::string attrStr) const;
879+
852880
/// Determine whether the witness for the given type requirement
853881
/// is the default definition.
854882
bool usesDefaultDefinition(AssociatedTypeDecl *requirement) const {

lib/AST/ASTContext.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2913,10 +2913,10 @@ NormalProtocolConformance *
29132913
ASTContext::getNormalConformance(Type conformingType,
29142914
ProtocolDecl *protocol,
29152915
SourceLoc loc,
2916+
TypeRepr *inheritedTypeRepr,
29162917
DeclContext *dc,
29172918
ProtocolConformanceState state,
2918-
ProtocolConformanceOptions options,
2919-
SourceLoc preconcurrencyLoc) {
2919+
ProtocolConformanceOptions options) {
29202920
assert(dc->isTypeContext());
29212921

29222922
llvm::FoldingSetNodeID id;
@@ -2930,8 +2930,7 @@ ASTContext::getNormalConformance(Type conformingType,
29302930

29312931
// Build a new normal protocol conformance.
29322932
auto result = new (*this) NormalProtocolConformance(
2933-
conformingType, protocol, loc, dc, state,
2934-
options, preconcurrencyLoc);
2933+
conformingType, protocol, loc, inheritedTypeRepr, dc, state, options);
29352934
normalConformances.InsertNode(result, insertPos);
29362935

29372936
return result;

lib/AST/ConformanceLookupTable.cpp

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -149,13 +149,20 @@ void ConformanceLookupTable::destroy() {
149149

150150
namespace {
151151
struct ConformanceConstructionInfo : public Located<ProtocolDecl *> {
152+
/// The `TypeRepr` of the inheritance clause entry from which this nominal
153+
/// was sourced, if any. For example, if this is a conformance to `Y`
154+
/// declared as `struct S: X, Y & Z {}`, this is the `TypeRepr` for `Y & Z`.
155+
TypeRepr *inheritedTypeRepr;
156+
152157
ConformanceAttributes attributes;
153158

154159
ConformanceConstructionInfo() { }
155160

156161
ConformanceConstructionInfo(ProtocolDecl *item, SourceLoc loc,
162+
TypeRepr *inheritedTypeRepr,
157163
ConformanceAttributes attributes)
158-
: Located(item, loc), attributes(attributes) {}
164+
: Located(item, loc), inheritedTypeRepr(inheritedTypeRepr),
165+
attributes(attributes) {}
159166
};
160167
}
161168

@@ -210,8 +217,9 @@ void ConformanceLookupTable::forEachInStage(ConformanceStage stage,
210217
loader.first->loadAllConformances(next, loader.second, conformances);
211218
registerProtocolConformances(next, conformances);
212219
for (auto conf : conformances) {
213-
protocols.push_back(
214-
{conf->getProtocol(), SourceLoc(), ConformanceAttributes()});
220+
protocols.push_back({conf->getProtocol(), SourceLoc(),
221+
/*inheritedTypeRepr=*/nullptr,
222+
ConformanceAttributes()});
215223
}
216224
} else if (next->getParentSourceFile() ||
217225
next->getParentModule()->isBuiltinModule()) {
@@ -220,7 +228,8 @@ void ConformanceLookupTable::forEachInStage(ConformanceStage stage,
220228
for (const auto &found :
221229
getDirectlyInheritedNominalTypeDecls(next, inverses, anyObject)) {
222230
if (auto proto = dyn_cast<ProtocolDecl>(found.Item))
223-
protocols.push_back({proto, found.Loc, found.attributes});
231+
protocols.push_back(
232+
{proto, found.Loc, found.inheritedTypeRepr, found.attributes});
224233
}
225234
}
226235

@@ -292,8 +301,6 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal,
292301
forEachInStage(
293302
stage, nominal,
294303
[&](NominalTypeDecl *nominal) {
295-
auto source = ConformanceSource::forExplicit(nominal);
296-
297304
// Get all of the protocols in the inheritance clause.
298305
InvertibleProtocolSet inverses;
299306
bool anyObject = false;
@@ -303,10 +310,12 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal,
303310
if (!proto)
304311
continue;
305312
auto kp = proto->getKnownProtocolKind();
306-
assert(!found.isSuppressed ||
307-
kp.has_value() &&
308-
"suppressed conformance for non-known protocol!?");
313+
assert(!found.isSuppressed ||
314+
kp.has_value() &&
315+
"suppressed conformance for non-known protocol!?");
309316
if (!found.isSuppressed) {
317+
auto source = ConformanceSource::forExplicit(
318+
nominal, found.inheritedTypeRepr);
310319
addProtocol(
311320
proto, found.Loc, source.withAttributes(found.attributes));
312321
}
@@ -318,11 +327,12 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal,
318327
[&](ExtensionDecl *ext, ArrayRef<ConformanceConstructionInfo> protos) {
319328
// The extension decl may not be validated, so we can't use
320329
// its inherited protocols directly.
321-
auto source = ConformanceSource::forExplicit(ext);
322-
for (auto locAndProto : protos)
323-
addProtocol(
324-
locAndProto.Item, locAndProto.Loc,
325-
source.withAttributes(locAndProto.attributes));
330+
for (auto locAndProto : protos) {
331+
auto source = ConformanceSource::forExplicit(
332+
ext, locAndProto.inheritedTypeRepr);
333+
addProtocol(locAndProto.Item, locAndProto.Loc,
334+
source.withAttributes(locAndProto.attributes));
335+
}
326336
});
327337
break;
328338

@@ -972,12 +982,17 @@ ConformanceLookupTable::getConformance(NominalTypeDecl *nominal,
972982
implyingConf = origImplyingConf->getRootNormalConformance();
973983
}
974984

985+
TypeRepr *inheritedTypeRepr = nullptr;
986+
if (entry->Source.getKind() == ConformanceEntryKind::Explicit) {
987+
inheritedTypeRepr = entry->Source.getInheritedTypeRepr();
988+
}
989+
975990
// Create or find the normal conformance.
976991
auto normalConf = ctx.getNormalConformance(
977-
conformingType, protocol, conformanceLoc, conformingDC,
978-
ProtocolConformanceState::Incomplete,
979-
entry->Source.getOptions(),
980-
entry->Source.getPreconcurrencyLoc());
992+
conformingType, protocol, conformanceLoc, inheritedTypeRepr,
993+
conformingDC, ProtocolConformanceState::Incomplete,
994+
entry->Source.getOptions());
995+
981996
// Invalid code may cause the getConformance call below to loop, so break
982997
// the infinite recursion by setting this eagerly to shortcircuit with the
983998
// early return at the start of this function.
@@ -1045,10 +1060,11 @@ void ConformanceLookupTable::registerProtocolConformance(
10451060

10461061
// Otherwise, add a new entry.
10471062
auto inherited = dyn_cast<InheritedProtocolConformance>(conformance);
1048-
ConformanceSource source
1049-
= inherited ? ConformanceSource::forInherited(cast<ClassDecl>(nominal)) :
1050-
synthesized ? ConformanceSource::forSynthesized(dc) :
1051-
ConformanceSource::forExplicit(dc);
1063+
ConformanceSource source =
1064+
inherited ? ConformanceSource::forInherited(cast<ClassDecl>(nominal))
1065+
: synthesized
1066+
? ConformanceSource::forSynthesized(dc)
1067+
: ConformanceSource::forExplicit(dc, /*inheritedEntry=*/nullptr);
10521068

10531069
ASTContext &ctx = nominal->getASTContext();
10541070
ConformanceEntry *entry = new (ctx) ConformanceEntry(SourceLoc(),

lib/AST/ConformanceLookupTable.h

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,20 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
8888
class ConformanceSource {
8989
void *Storage;
9090

91+
/// The `TypeRepr` of the inheritance clause entry that declares this
92+
/// conformance, if any. For example, if this is a conformance to `Y`
93+
/// declared as `struct S: X, Y & Z {}`, this is the `TypeRepr` for `Y & Z`.
94+
///
95+
/// - Important: The value can be valid only for an explicit conformance.
96+
TypeRepr *inheritedTypeRepr;
97+
9198
ConformanceEntryKind Kind;
9299

93100
ConformanceAttributes attributes;
94101

95-
ConformanceSource(void *ptr, ConformanceEntryKind kind)
96-
: Storage(ptr), Kind(kind) { }
102+
ConformanceSource(void *ptr, ConformanceEntryKind kind,
103+
TypeRepr *inheritedTypeRepr = nullptr)
104+
: Storage(ptr), inheritedTypeRepr(inheritedTypeRepr), Kind(kind) {}
97105

98106
public:
99107
/// Create an inherited conformance.
@@ -109,8 +117,10 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
109117
/// The given declaration context (nominal type declaration or
110118
/// extension thereof) explicitly specifies conformance to the
111119
/// protocol.
112-
static ConformanceSource forExplicit(DeclContext *dc) {
113-
return ConformanceSource(dc, ConformanceEntryKind::Explicit);
120+
static ConformanceSource forExplicit(DeclContext *dc,
121+
TypeRepr *inheritedEntry) {
122+
return ConformanceSource(dc, ConformanceEntryKind::Explicit,
123+
inheritedEntry);
114124
}
115125

116126
/// Create an implied conformance.
@@ -134,6 +144,13 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
134144
return ConformanceSource(dc, ConformanceEntryKind::PreMacroExpansion);
135145
}
136146

147+
/// Return the `TypeRepr` of the inheritance clause entry that declares this
148+
/// conformance, if any. For example, if this is a conformance to `Y`
149+
/// declared as `struct S: X, Y & Z {}`, this is the `TypeRepr` for `Y & Z`.
150+
///
151+
/// - Important: The value can be valid only for an explicit conformance.
152+
TypeRepr *getInheritedTypeRepr() const { return inheritedTypeRepr; }
153+
137154
/// Return a new conformance source with the given conformance
138155
/// attributes.
139156
ConformanceSource withAttributes(ConformanceAttributes attributes) {

lib/AST/NameLookup.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4090,7 +4090,8 @@ void swift::getDirectlyInheritedNominalTypeDecls(
40904090

40914091
// Form the result.
40924092
for (auto nominal : nominalTypes) {
4093-
result.push_back({nominal, loc, attributes, isSuppressed});
4093+
result.push_back({nominal, loc, inheritedTypes.getTypeRepr(i), attributes,
4094+
isSuppressed});
40944095
}
40954096
}
40964097

@@ -4123,8 +4124,8 @@ swift::getDirectlyInheritedNominalTypeDecls(
41234124
ConformanceAttributes attributes;
41244125
if (attr->isUnchecked())
41254126
attributes.uncheckedLoc = loc;
4126-
result.push_back(
4127-
{attr->getProtocol(), loc, attributes, /*isSuppressed=*/false});
4127+
result.push_back({attr->getProtocol(), loc, /*inheritedTypeRepr=*/nullptr,
4128+
attributes, /*isSuppressed=*/false});
41284129
}
41294130

41304131
// Else we have access to this information on the where clause.
@@ -4135,7 +4136,8 @@ swift::getDirectlyInheritedNominalTypeDecls(
41354136
// FIXME: Refactor SelfBoundsFromWhereClauseRequest to dig out
41364137
// the source location.
41374138
for (auto inheritedNominal : selfBounds.decls)
4138-
result.emplace_back(inheritedNominal, SourceLoc(), ConformanceAttributes(),
4139+
result.emplace_back(inheritedNominal, SourceLoc(),
4140+
/*inheritedTypeRepr=*/nullptr, ConformanceAttributes(),
41394141
/*isSuppressed=*/false);
41404142

41414143
return result;

0 commit comments

Comments
 (0)