Skip to content

Commit b393a92

Browse files
committed
Always supersede conformances implied by pre-macro-expansion conformances
Pre-macro-expansion conformances are introduced at the point where an attached extension macro is attached to a particular nominal type, and can imply other conformances. Once the macro is expanded, they are expected to be replaced by the real conformance from the extension produced by the macro. This includes any other conformances that are implied by that conformances. Ensure that the real conformance---and every conformances it implies---are considered "better" than the pre-expansion conformances. Fixes a bug where we would pick the wrong (pre-expansion) conformances, which would then fail to get fully type-checked prior to serialization. This could accept invalid code that then crashed the compiler, as in rdar://112916159. (cherry picked from commit ab576b3)
1 parent ba7628c commit b393a92

File tree

4 files changed

+53
-11
lines changed

4 files changed

+53
-11
lines changed

include/swift/AST/DeclContext.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,15 +150,15 @@ enum class ConformanceEntryKind : unsigned {
150150
/// Explicitly specified.
151151
Explicit,
152152

153+
/// The conformance is generated by a macro that has not been
154+
/// expanded yet.
155+
PreMacroExpansion,
156+
153157
/// Implicitly synthesized.
154158
Synthesized,
155159

156160
/// Implied by an explicitly-specified conformance.
157161
Implied,
158-
159-
/// The conformance is generated by a macro that has not been
160-
/// expanded yet.
161-
PreMacroExpansion,
162162
};
163163

164164
/// Describes the kind of conformance lookup desired.

lib/AST/ConformanceLookupTable.h

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
147147
///
148148
/// The only difference between the ranking kind and the kind is
149149
/// that implied conformances originating from a synthesized
150-
/// conformance are considered to be synthesized (which has a
150+
/// or pre-macro-expansion conformance are considered to be synthesized (which has a
151151
/// lower ranking).
152152
ConformanceEntryKind getRankingKind() const {
153153
switch (auto kind = getKind()) {
@@ -157,11 +157,22 @@ class ConformanceLookupTable : public ASTAllocated<ConformanceLookupTable> {
157157
case ConformanceEntryKind::PreMacroExpansion:
158158
return kind;
159159

160-
case ConformanceEntryKind::Implied:
161-
return (getImpliedSource()->getDeclaredConformance()->getKind()
162-
== ConformanceEntryKind::Synthesized)
163-
? ConformanceEntryKind::Synthesized
164-
: ConformanceEntryKind::Implied;
160+
case ConformanceEntryKind::Implied: {
161+
auto impliedSourceKind =
162+
getImpliedSource()->getDeclaredConformance()->getKind();
163+
switch (impliedSourceKind) {
164+
case ConformanceEntryKind::Synthesized:
165+
case ConformanceEntryKind::PreMacroExpansion:
166+
return impliedSourceKind;
167+
168+
case ConformanceEntryKind::Explicit:
169+
case ConformanceEntryKind::Inherited:
170+
return ConformanceEntryKind::Implied;
171+
172+
case ConformanceEntryKind::Implied:
173+
return getImpliedSource()->getRankingKind();
174+
}
175+
}
165176
}
166177

167178
llvm_unreachable("Unhandled ConformanceEntryKind in switch.");

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1423,7 +1423,20 @@ public struct HashableMacro: ConformanceMacro {
14231423
}
14241424
}
14251425

1426-
public struct DelegatedConformanceMacro: ConformanceMacro, MemberMacro {
1426+
public struct ImpliesHashableMacro: ExtensionMacro {
1427+
public static func expansion(
1428+
of node: AttributeSyntax,
1429+
attachedTo: some DeclGroupSyntax,
1430+
providingExtensionsOf type: some TypeSyntaxProtocol,
1431+
conformingTo protocols: [TypeSyntax],
1432+
in context: some MacroExpansionContext
1433+
) throws -> [ExtensionDeclSyntax] {
1434+
let ext: DeclSyntax = "extension \(type.trimmed): ImpliesHashable {}"
1435+
return [ext.cast(ExtensionDeclSyntax.self)]
1436+
}
1437+
}
1438+
1439+
public struct DelegatedConformanceMacro: ExtensionMacro, MemberMacro {
14271440
public static func expansion(
14281441
of node: AttributeSyntax,
14291442
providingConformancesOf decl: some DeclGroupSyntax,

test/Macros/macro_expand_extensions.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
// RUN: not %target-swift-frontend -enable-experimental-feature ExtensionMacros -swift-version 5 -typecheck -load-plugin-library %t/%target-library-name(MacroDefinition) -module-name MacroUser -DTEST_DIAGNOSTICS -serialize-diagnostics-path %t/macro_expand.dia %s -emit-macro-expansion-files no-diagnostics
1111
// RUN: c-index-test -read-diagnostics %t/macro_expand.dia 2>&1 | %FileCheck -check-prefix CHECK-DIAGS %s
1212

13+
// Ensure that we can serialize this file as a module.
14+
// RUN: %target-swift-frontend -swift-version 5 -load-plugin-library %t/%target-library-name(MacroDefinition) %s -I %t -disable-availability-checking -emit-module -o %t/MyModule.swiftmodule -enable-testing
15+
1316
// RUN: %target-build-swift -enable-experimental-feature ExtensionMacros -swift-version 5 -load-plugin-library %t/%target-library-name(MacroDefinition) %s -o %t/main -module-name MacroUser -swift-version 5 -emit-tbd -emit-tbd-path %t/MacroUser.tbd -I %t
1417
// RUN: %target-codesign %t/main
1518
// RUN: %target-run %t/main | %FileCheck %s
@@ -198,3 +201,18 @@ func requiresEquatable<T: Equatable>(_: T) { }
198201
func testHasPropertyWrappers(hpw: HasPropertyWrappers) {
199202
requiresEquatable(hpw)
200203
}
204+
205+
// Check that conformances implied by a macro-defined conformance are serialized
206+
// without issue.
207+
public protocol ImpliesHashable: Hashable { }
208+
209+
@attached(extension, conformances: ImpliesHashable)
210+
macro ImpliesHashable() = #externalMacro(module: "MacroDefinition", type: "ImpliesHashableMacro")
211+
212+
func requiresHashable<T: Hashable>(_: T) { }
213+
func testMakeMeHashable(mmh: MakeMeHashable, dict: [MakeMeHashable: Int]) {
214+
requiresHashable(mmh)
215+
}
216+
217+
@ImpliesHashable
218+
public struct MakeMeHashable { }

0 commit comments

Comments
 (0)