Skip to content

Commit 04c75b5

Browse files
committed
[Macros] Fix existential diagnostics for declaration macro expansions
`TypeChecker::checkExistentialTypes` has decl-specific logic and backs out without invoking `ExistentialTypeVisitor` on `TypeDecl`s, but `MacroExpansionDecl` isn't a type decl and would fall into `ExistentialTypeVisitor` which will visit its expansion. The caller of `checkExistentialTypes` already visits auxiliary decls, so here we should only visit arguments and generic arguments of a macro. Fixes a case where a declaration macro produces a type that conforms to a PAT. We now also run existential diagnostics on generic arguments on a `MacroExpansionDecl` that was previously not handled. Note: I tried bailing out in `walkToDeclPre` but we should really visit macro arguments and generic arguments. Not walking expansions in `ExistentialTypeVisitor` is also not the right fix because we need to be able to visit macro expansions in arguments. rdar://109779928
1 parent 38786ba commit 04c75b5

File tree

5 files changed

+80
-7
lines changed

5 files changed

+80
-7
lines changed

lib/Sema/TypeCheckType.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5335,9 +5335,15 @@ void TypeChecker::checkExistentialTypes(Decl *decl) {
53355335
} else if (auto *macroDecl = dyn_cast<MacroDecl>(decl)) {
53365336
checkExistentialTypes(ctx, macroDecl->getGenericParams());
53375337
checkExistentialTypes(ctx, macroDecl->getTrailingWhereClause());
5338+
} else if (auto *macroExpansionDecl = dyn_cast<MacroExpansionDecl>(decl)) {
5339+
ExistentialTypeVisitor visitor(ctx, /*checkStatements=*/false);
5340+
macroExpansionDecl->getArgs()->walk(visitor);
5341+
for (auto *genArg : macroExpansionDecl->getGenericArgs())
5342+
genArg->walk(visitor);
53385343
}
53395344

5340-
if (isa<TypeDecl>(decl) || isa<ExtensionDecl>(decl))
5345+
if (isa<TypeDecl>(decl) || isa<ExtensionDecl>(decl) ||
5346+
isa<MacroExpansionDecl>(decl))
53415347
return;
53425348

53435349
ExistentialTypeVisitor visitor(ctx, /*checkStatements=*/false);

test/Macros/Inputs/freestanding_macro_library.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
public macro structWithUnqualifiedLookup() = #externalMacro(module: "MacroDefinition", type: "DefineStructWithUnqualifiedLookupMacro")
33

44
@freestanding(declaration)
5-
public macro anonymousTypes(public: Bool = false, _: () -> String) = #externalMacro(module: "MacroDefinition", type: "DefineAnonymousTypesMacro")
5+
public macro anonymousTypes(public: Bool = false, causeErrors: Bool = false, _: () -> String) = #externalMacro(module: "MacroDefinition", type: "DefineAnonymousTypesMacro")
6+
7+
@freestanding(declaration)
8+
public macro introduceTypeCheckingErrors() = #externalMacro(module: "MacroDefinition", type: "IntroduceTypeCheckingErrorsMacro")
69

710
@freestanding(declaration)
811
public macro freestandingWithClosure<T>(_ value: T, body: (T) -> T) = #externalMacro(module: "MacroDefinition", type: "EmptyDeclarationMacro")

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1339,7 +1339,7 @@ public struct DefineAnonymousTypesMacro: DeclarationMacro {
13391339
} else {
13401340
accessSpecifier = ""
13411341
}
1342-
return [
1342+
var results: [DeclSyntax] = [
13431343
"""
13441344
13451345
\(raw:accessSpecifier)class \(context.makeUniqueName("name")) {
@@ -1360,6 +1360,41 @@ public struct DefineAnonymousTypesMacro: DeclarationMacro {
13601360
\(body.statements)
13611361
}
13621362
}
1363+
""",
1364+
"""
1365+
1366+
struct \(context.makeUniqueName("name")): Equatable {
1367+
static func == (lhs: Self, rhs: Self) -> Bool { false }
1368+
}
1369+
"""
1370+
]
1371+
1372+
if let _ = node.argumentList.first(labeled: "causeErrors") {
1373+
1374+
results += ["""
1375+
1376+
struct \(context.makeUniqueName("name"))<T> where T == Equatable { // expect error: need 'any'
1377+
#introduceTypeCheckingErrors // make sure we get nested errors
1378+
}
1379+
"""]
1380+
}
1381+
1382+
return results
1383+
}
1384+
}
1385+
1386+
public struct IntroduceTypeCheckingErrorsMacro: DeclarationMacro {
1387+
public static func expansion(
1388+
of node: some FreestandingMacroExpansionSyntax,
1389+
in context: some MacroExpansionContext
1390+
) -> [DeclSyntax] {
1391+
return [
1392+
"""
1393+
1394+
struct \(context.makeUniqueName("name")) {
1395+
struct \(context.makeUniqueName("name"))<T> where T == Hashable { // expect error: need 'any'
1396+
}
1397+
}
13631398
"""
13641399
]
13651400
}

test/Macros/macros_diagnostics.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,14 @@ struct MyStruct<T: MyProto> {
189189
#undefinedMacro { definitelyNotDefined }
190190
// expected-error@-1{{cannot find 'definitelyNotDefined' in scope}}
191191
// expected-error@-2{{no macro named 'undefinedMacro'}}
192+
193+
@freestanding(declaration) macro genericUnary<T>(_: T) = #externalMacro(module: "A", type: "B")
194+
// expected-warning@-1{{external macro implementation type}}
195+
// expected-note@-2{{'genericUnary' declared here}}
196+
197+
struct SomeType {
198+
#genericUnary<Equatable>(0 as Hashable)
199+
// expected-error@-1{{use of protocol 'Equatable' as a type must be written 'any Equatable'}}
200+
// expected-error@-2{{use of protocol 'Hashable' as a type must be written 'any Hashable'}}
201+
// expected-error@-3{{external macro implementation type}}
202+
}

test/Macros/top_level_freestanding.swift

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// RUN: %target-typecheck-verify-swift -swift-version 5 -parse-as-library -load-plugin-library %t/%target-library-name(MacroDefinition) -module-name MacroUser -DTEST_DIAGNOSTICS -DIMPORT_MACRO_LIBRARY -swift-version 5 %S/Inputs/top_level_freestanding_other.swift -I %t
1414

1515
// Check diagnostic buffer names
16-
// RUN: %target-swift-frontend -typecheck -swift-version 5 -parse-as-library -load-plugin-library %t/%target-library-name(MacroDefinition) -module-name MacroUser -DTEST_DIAGNOSTICS -swift-version 5 %s %S/Inputs/top_level_freestanding_other.swift 2> %t.diags
16+
// RUN: not %target-swift-frontend -typecheck -swift-version 5 -parse-as-library -load-plugin-library %t/%target-library-name(MacroDefinition) -module-name MacroUser -DTEST_DIAGNOSTICS -swift-version 5 %s %S/Inputs/top_level_freestanding_other.swift 2> %t.diags
1717
// RUN: %FileCheck -check-prefix DIAG_BUFFERS %s < %t.diags
1818

1919
// Execution testing
@@ -27,7 +27,9 @@ import freestanding_macro_library
2727
@freestanding(declaration, names: named(StructWithUnqualifiedLookup))
2828
macro structWithUnqualifiedLookup() = #externalMacro(module: "MacroDefinition", type: "DefineStructWithUnqualifiedLookupMacro")
2929
@freestanding(declaration)
30-
macro anonymousTypes(public: Bool = false, _: () -> String) = #externalMacro(module: "MacroDefinition", type: "DefineAnonymousTypesMacro")
30+
macro anonymousTypes(public: Bool = false, causeErrors: Bool = false, _: () -> String) = #externalMacro(module: "MacroDefinition", type: "DefineAnonymousTypesMacro")
31+
@freestanding(declaration)
32+
macro introduceTypeCheckingErrors() = #externalMacro(module: "MacroDefinition", type: "IntroduceTypeCheckingErrorsMacro")
3133
@freestanding(declaration)
3234
macro freestandingWithClosure<T>(_ value: T, body: (T) -> T) = #externalMacro(module: "MacroDefinition", type: "EmptyDeclarationMacro")
3335
@freestanding(declaration, names: arbitrary) macro bitwidthNumberedStructs(_ baseName: String) = #externalMacro(module: "MacroDefinition", type: "DefineBitwidthNumberedStructsMacro")
@@ -78,11 +80,27 @@ func testArbitraryAtGlobal() {
7880
_ = MyIntGlobal16()
7981
}
8082

81-
// DIAG_BUFFERS: @__swiftmacro_9MacroUser33_{{.*}}9stringifyfMf1_{{.*}}warning: 'deprecated()' is deprecated
82-
// DIAG_BUFFERS: @__swiftmacro_9MacroUser33_{{.*}}9stringifyfMf2_{{.*}}warning: 'deprecated()' is deprecated
83+
// DIAG_BUFFERS-DAG: @__swiftmacro_9MacroUser33_{{.*}}9stringifyfMf1_{{.*}}warning: 'deprecated()' is deprecated
84+
// DIAG_BUFFERS-DAG: @__swiftmacro_9MacroUser33_{{.*}}9stringifyfMf2_{{.*}}warning: 'deprecated()' is deprecated
8385

8486
#varValue
8587

8688
func testGlobalVariable() {
8789
_ = value
8890
}
91+
92+
#if TEST_DIAGNOSTICS
93+
94+
// expected-note @+1 6 {{in expansion of macro 'anonymousTypes' here}}
95+
#anonymousTypes(causeErrors: true) { "foo" }
96+
// DIAG_BUFFERS-DAG: @__swiftmacro_9MacroUser33{{.*}}anonymousTypesfMf2_{{.*}}error: use of protocol 'Equatable' as a type must be written 'any Equatable'
97+
// DIAG_BUFFERS-DAG: @__swiftmacro_9MacroUser03{{.*}}anonymousTypes{{.*}}introduceTypeCheckingErrorsfMf0_{{.*}}error: use of protocol 'Hashable' as a type must be written 'any Hashable'
98+
99+
// expected-note @+1 2 {{in expansion of macro 'anonymousTypes' here}}
100+
#anonymousTypes { () -> String in
101+
// expected-error @+1 {{use of protocol 'Equatable' as a type must be written 'any Equatable'}}
102+
_ = 0 as Equatable
103+
return "foo"
104+
}
105+
106+
#endif

0 commit comments

Comments
 (0)