Skip to content

Commit 190d965

Browse files
committed
[Property wrappers] Fix source compatibility issue with attribute lookup.
Swift 5.1's lookup for custom attributes skipped associated type members, which allowed code like the given example to compile. To maintain source compatibility, identify the narrow case that happens in practice---the property wrapper is at module scope but is now shadowed by an associated type---warn about it, and accept it. Fixes rdar://problem/56213175.
1 parent 7b9f30b commit 190d965

File tree

3 files changed

+84
-2
lines changed

3 files changed

+84
-2
lines changed

include/swift/AST/DiagnosticsCommon.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ ERROR(circular_protocol_def,none,
163163
NOTE(kind_declname_declared_here,none,
164164
"%0 %1 declared here", (DescriptiveDeclKind, DeclName))
165165

166+
WARNING(warn_property_wrapper_module_scope,none,
167+
"ignoring associated type %0 in favor of module-scoped property "
168+
"wrapper %0; please qualify the reference with %1",
169+
(DeclName, Identifier))
170+
166171
#ifndef DIAG_NO_UNDEF
167172
# if defined(DIAG)
168173
# undef DIAG

lib/AST/NameLookup.cpp

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,13 @@ void namelookup::recordLookupOfTopLevelName(DeclContext *topLevelContext,
648648
nameTracker->addTopLevelName(name.getBaseName(), isCascading);
649649
}
650650

651+
namespace {
652+
/// Whether we're looking up outer results or not.
653+
enum class LookupOuterResults {
654+
Excluded,
655+
Included
656+
};
657+
}
651658

652659
/// Retrieve the set of type declarations that are directly referenced from
653660
/// the given parsed type representation.
@@ -1878,11 +1885,15 @@ resolveTypeDeclsToNominal(Evaluator &evaluator,
18781885
/// Perform unqualified name lookup for types at the given location.
18791886
static DirectlyReferencedTypeDecls
18801887
directReferencesForUnqualifiedTypeLookup(DeclName name,
1881-
SourceLoc loc, DeclContext *dc) {
1888+
SourceLoc loc, DeclContext *dc,
1889+
LookupOuterResults lookupOuter) {
18821890
DirectlyReferencedTypeDecls results;
18831891
UnqualifiedLookup::Options options =
18841892
UnqualifiedLookup::Flags::TypeLookup |
18851893
UnqualifiedLookup::Flags::AllowProtocolMembers;
1894+
if (lookupOuter == LookupOuterResults::Included)
1895+
options |= UnqualifiedLookup::Flags::IncludeOuterResults;
1896+
18861897
UnqualifiedLookup lookup(name, dc, loc, options);
18871898
for (const auto &result : lookup.Results) {
18881899
if (auto typeDecl = dyn_cast<TypeDecl>(result.getValueDecl()))
@@ -1957,7 +1968,8 @@ directReferencesForIdentTypeRepr(Evaluator &evaluator,
19571968
current =
19581969
directReferencesForUnqualifiedTypeLookup(component->getIdentifier(),
19591970
component->getIdLoc(),
1960-
dc);
1971+
dc,
1972+
LookupOuterResults::Excluded);
19611973

19621974
// If we didn't find anything, fail now.
19631975
if (current.empty())
@@ -2195,6 +2207,19 @@ ExtendedNominalRequest::evaluate(Evaluator &evaluator,
21952207
return nominalTypes.empty() ? nullptr : nominalTypes[0];
21962208
}
21972209

2210+
/// Whether there are only associated types in the set of declarations.
2211+
static bool declsAreAssociatedTypes(ArrayRef<TypeDecl *> decls) {
2212+
if (decls.empty())
2213+
return false;
2214+
2215+
for (auto decl : decls) {
2216+
if (!isa<AssociatedTypeDecl>(decl))
2217+
return false;
2218+
}
2219+
2220+
return true;
2221+
}
2222+
21982223
llvm::Expected<NominalTypeDecl *>
21992224
CustomAttrNominalRequest::evaluate(Evaluator &evaluator,
22002225
CustomAttr *attr, DeclContext *dc) const {
@@ -2217,6 +2242,48 @@ CustomAttrNominalRequest::evaluate(Evaluator &evaluator,
22172242
if (nominals.size() == 1 && !isa<ProtocolDecl>(nominals.front()))
22182243
return nominals.front();
22192244

2245+
// If we found declarations that are associated types, look outside of
2246+
// the current context to see if we can recover.
2247+
if (declsAreAssociatedTypes(decls)) {
2248+
if (auto typeRepr = typeLoc.getTypeRepr()) {
2249+
if (auto identTypeRepr = dyn_cast<SimpleIdentTypeRepr>(typeRepr)) {
2250+
auto assocType = cast<AssociatedTypeDecl>(decls.front());
2251+
2252+
modulesFound.clear();
2253+
anyObject = false;
2254+
decls = directReferencesForUnqualifiedTypeLookup(
2255+
identTypeRepr->getIdentifier(), identTypeRepr->getIdLoc(), dc,
2256+
LookupOuterResults::Included);
2257+
nominals = resolveTypeDeclsToNominal(evaluator, ctx, decls,
2258+
modulesFound, anyObject);
2259+
if (nominals.size() == 1 && !isa<ProtocolDecl>(nominals.front())) {
2260+
auto nominal = nominals.front();
2261+
if (nominal->getDeclContext()->isModuleScopeContext()) {
2262+
// Complain, producing module qualification in a Fix-It.
2263+
auto moduleName = nominal->getParentModule()->getName();
2264+
ctx.Diags.diagnose(typeRepr->getLoc(),
2265+
diag::warn_property_wrapper_module_scope,
2266+
identTypeRepr->getIdentifier(),
2267+
moduleName)
2268+
.fixItInsert(typeRepr->getLoc(),
2269+
moduleName.str().str() + ".");
2270+
ctx.Diags.diagnose(assocType, diag::kind_declname_declared_here,
2271+
assocType->getDescriptiveKind(),
2272+
assocType->getFullName());
2273+
2274+
ComponentIdentTypeRepr *components[2] = {
2275+
new (ctx) SimpleIdentTypeRepr(typeRepr->getLoc(), moduleName),
2276+
identTypeRepr
2277+
};
2278+
2279+
typeLoc = TypeLoc(IdentTypeRepr::create(ctx, components));
2280+
return nominal;
2281+
}
2282+
}
2283+
}
2284+
}
2285+
}
2286+
22202287
return nullptr;
22212288
}
22222289

test/decl/var/property_wrappers.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1779,3 +1779,13 @@ struct SR_11477_W3 { // Okay
17791779
get { return 0 }
17801780
}
17811781
}
1782+
1783+
// rdar://problem/56213175 - backward compatibility issue with Swift 5.1,
1784+
// which unconditionally skipped protocol members.
1785+
protocol ProtocolWithWrapper {
1786+
associatedtype Wrapper = Float // expected-note{{associated type 'Wrapper' declared here}}
1787+
}
1788+
1789+
struct UsesProtocolWithWrapper: ProtocolWithWrapper {
1790+
@Wrapper var foo: Int // expected-warning{{ignoring associated type 'Wrapper' in favor of module-scoped property wrapper 'Wrapper'; please qualify the reference with 'property_wrappers'}}{{4-4=property_wrappers.}}
1791+
}

0 commit comments

Comments
 (0)