Skip to content

Commit ab576b3

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.
1 parent b7bfaf3 commit ab576b3

File tree

4 files changed

+52
-10
lines changed

4 files changed

+52
-10
lines changed

include/swift/AST/DeclContext.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,15 @@ enum class ConformanceEntryKind : unsigned {
153153
/// Explicitly specified.
154154
Explicit,
155155

156+
/// The conformance is generated by a macro that has not been
157+
/// expanded yet.
158+
PreMacroExpansion,
159+
156160
/// Implicitly synthesized.
157161
Synthesized,
158162

159163
/// Implied by an explicitly-specified conformance.
160164
Implied,
161-
162-
/// The conformance is generated by a macro that has not been
163-
/// expanded yet.
164-
PreMacroExpansion,
165165
};
166166

167167
/// 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: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,19 @@ public struct HashableMacro: ExtensionMacro {
13601360
}
13611361
}
13621362

1363+
public struct ImpliesHashableMacro: ExtensionMacro {
1364+
public static func expansion(
1365+
of node: AttributeSyntax,
1366+
attachedTo: some DeclGroupSyntax,
1367+
providingExtensionsOf type: some TypeSyntaxProtocol,
1368+
conformingTo protocols: [TypeSyntax],
1369+
in context: some MacroExpansionContext
1370+
) throws -> [ExtensionDeclSyntax] {
1371+
let ext: DeclSyntax = "extension \(type.trimmed): ImpliesHashable {}"
1372+
return [ext.cast(ExtensionDeclSyntax.self)]
1373+
}
1374+
}
1375+
13631376
public struct DelegatedConformanceMacro: ExtensionMacro, MemberMacro {
13641377
public static func expansion(
13651378
of node: AttributeSyntax,

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)