Skip to content

Commit 4b41fe1

Browse files
committed
[NameLookup] Prefer property wrapper over attached macro on local var
We don't properly support peer declarations in local contexts, as such if we have both a property wrapper and macro with a given name on a local var, let's prefer the property wrapper. rdar://148782639
1 parent 2b8a1cc commit 4b41fe1

File tree

3 files changed

+120
-3
lines changed

3 files changed

+120
-3
lines changed

lib/AST/NameLookup.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3971,20 +3971,34 @@ GenericParamListRequest::evaluate(Evaluator &evaluator, GenericContext *value) c
39713971
parsedGenericParams->getRAngleLoc());
39723972
}
39733973

3974+
static bool shouldPreferPropertyWrapperOverMacro(CustomAttrOwner owner) {
3975+
// If we have a VarDecl in a local context, prefer to use a property wrapper
3976+
// if one exists. This is necessary since we don't properly support peer
3977+
// declarations in local contexts, so want to use a property wrapper if one
3978+
// exists.
3979+
if (auto *D = dyn_cast_or_null<VarDecl>(owner.getAsDecl())) {
3980+
if (D->getDeclContext()->isLocalContext())
3981+
return true;
3982+
}
3983+
return false;
3984+
}
3985+
39743986
NominalTypeDecl *CustomAttrNominalRequest::evaluate(Evaluator &evaluator,
39753987
CustomAttr *attr) const {
39763988
auto owner = attr->getOwner();
39773989
auto *dc = owner.getDeclContext();
39783990

39793991
// Look for names at module scope, so we don't trigger name lookup for
39803992
// nested scopes. At this point, we're looking to see whether there are
3981-
// any suitable macros.
3993+
// any suitable macros. If we're preferring property wrappers we wait to see
3994+
// if any property wrappers are in scope before returning.
39823995
auto [module, macro] = attr->destructureMacroRef();
39833996
auto moduleName = (module) ? module->getNameRef() : DeclNameRef();
39843997
auto macroName = (macro) ? macro->getNameRef() : DeclNameRef();
39853998
auto macros = namelookup::lookupMacros(dc, moduleName, macroName,
39863999
getAttachedMacroRoles());
3987-
if (!macros.empty())
4000+
auto shouldPreferPropWrapper = shouldPreferPropertyWrapperOverMacro(owner);
4001+
if (!macros.empty() && !shouldPreferPropWrapper)
39884002
return nullptr;
39894003

39904004
// Find the types referenced by the custom attribute.
@@ -4004,6 +4018,15 @@ NominalTypeDecl *CustomAttrNominalRequest::evaluate(Evaluator &evaluator,
40044018
auto nominals = resolveTypeDeclsToNominal(evaluator, ctx, decls.first,
40054019
ResolveToNominalOptions(),
40064020
modulesFound, anyObject);
4021+
// If we're preferring property wrappers and found a suitable match, continue.
4022+
// Otherwise we can bail and resolve as a macro.
4023+
if (shouldPreferPropWrapper) {
4024+
auto hasPropWrapper = llvm::any_of(nominals, [](NominalTypeDecl *NTD) {
4025+
return NTD->getAttrs().hasAttribute<PropertyWrapperAttr>();
4026+
});
4027+
if (!macros.empty() && !hasPropWrapper)
4028+
return nullptr;
4029+
}
40074030
if (nominals.size() == 1 && !isa<ProtocolDecl>(nominals.front()))
40084031
return nominals.front();
40094032

lib/Sema/TypeCheckMacros.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2241,7 +2241,12 @@ ConcreteDeclRef ResolveMacroRequest::evaluate(Evaluator &evaluator,
22412241

22422242
// When a macro is not found for a custom attribute, it may be a non-macro.
22432243
// So bail out to prevent diagnostics from the contraint system.
2244-
if (macroRef.getAttr()) {
2244+
if (auto *attr = macroRef.getAttr()) {
2245+
// If we already resolved this CustomAttr to a nominal, this isn't for a
2246+
// macro.
2247+
if (attr->getNominalDecl())
2248+
return ConcreteDeclRef();
2249+
22452250
auto foundMacros = namelookup::lookupMacros(dc, macroRef.getModuleName(),
22462251
macroRef.getMacroName(), roles);
22472252
if (foundMacros.empty())
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
// RUN: %target-swift-frontend -emit-ir -primary-file %t/valid.swift %t/definitions.swift
4+
// RUN: %target-swift-frontend -typecheck -verify -verify-additional-prefix main- -primary-file %t/main.swift %t/definitions.swift
5+
// RUN: %target-swift-frontend -typecheck -verify -verify-additional-prefix other- -parse-as-library -primary-file %t/other.swift %t/definitions.swift
6+
7+
//--- definitions.swift
8+
@attached(peer)
9+
macro SomeAttr() = #externalMacro(module: "", type: "") // expected-note {{declared here}}
10+
// expected-main-note@-1 2{{declared here}}
11+
12+
@propertyWrapper
13+
struct SomeAttr<T> {
14+
var wrappedValue: T
15+
init(wrappedValue: T) { self.wrappedValue = wrappedValue }
16+
init(projectedValue: Self) { self = projectedValue}
17+
var projectedValue: Self { self }
18+
}
19+
20+
struct Err: Error {}
21+
22+
//--- valid.swift
23+
func foo() throws {
24+
// Make sure we prefer the property wrapper over the macro here.
25+
@SomeAttr var x = 0
26+
let _: Int = x
27+
let _: SomeAttr<Int> = _x
28+
let _: SomeAttr<Int> = $x
29+
30+
_ = {
31+
@SomeAttr var y = 0
32+
let _: Int = y
33+
let _: SomeAttr<Int> = _y
34+
let _: SomeAttr<Int> = $y
35+
}
36+
37+
func bar(@SomeAttr x: Int) {
38+
let _: Int = x
39+
let _: SomeAttr<Int> = _x
40+
let _: SomeAttr<Int> = $x
41+
}
42+
43+
bar($x: SomeAttr(wrappedValue: 0))
44+
45+
func baz(_ fn: (SomeAttr<Int>) -> Void) {}
46+
baz { x in }
47+
baz { $x in }
48+
49+
_ = if .random() {
50+
@SomeAttr var z = 0
51+
let _: SomeAttr<Int> = _z
52+
let _: SomeAttr<Int> = $z
53+
throw Err()
54+
} else {
55+
0
56+
}
57+
}
58+
59+
//--- main.swift
60+
struct S1 {
61+
var x = if .random() {
62+
@SomeAttr var y = 0
63+
let _: SomeAttr<Int> = _y
64+
let _: SomeAttr<Int> = $y
65+
throw Err() // expected-error {{errors cannot be thrown out of a property initializer}}
66+
} else {
67+
0
68+
}
69+
}
70+
71+
// Here we prefer the macro.
72+
struct S2 {
73+
@SomeAttr var x = 0 // expected-error {{could not be found for macro}}
74+
}
75+
76+
func bar() {
77+
@SomeAttr func baz() {} // expected-error {{could not be found for macro}}
78+
}
79+
80+
@SomeAttr var x = 0 // expected-error {{could not be found for macro}}
81+
82+
_ = { @SomeAttr<Int> in }
83+
// expected-error@-1 {{attribute @SomeAttr<Int> is not supported on a closure}}
84+
// expected-warning@-2 {{cannot specialize a non-generic external macro 'SomeAttr()'}}
85+
86+
//--- other.swift
87+
88+
// Also check for a library file
89+
@SomeAttr var x = 0 // expected-error {{could not be found for macro}}

0 commit comments

Comments
 (0)