Skip to content

Conformance macro fixes #64020

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/Refactoring/Refactoring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8549,6 +8549,14 @@ getMacroExpansionBuffers(MacroDecl *macro, const CustomAttr *attr, Decl *decl) {
allBufferIDs.append(bufferIDs.begin(), bufferIDs.end());
}

if (roles.contains(MacroRole::Conformance)) {
if (auto nominal = dyn_cast<NominalTypeDecl>(decl)) {
auto bufferIDs = evaluateOrDefault(
ctx.evaluator, ExpandConformanceMacros{nominal}, { });
allBufferIDs.append(bufferIDs.begin(), bufferIDs.end());
}
}

// Drop any buffers that come from other macros. We could eliminate this
// step by adding more fine-grained requests above, which only expand for a
// single custom attribute.
Expand Down
30 changes: 5 additions & 25 deletions lib/Sema/TypeCheckMacros.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1276,34 +1276,14 @@ swift::expandConformances(CustomAttr *attr, MacroDecl *macro,
if (!extension)
continue;

auto &extensionCtx = extension->getASTContext();

// Bind the extension to the original nominal type.
extension->setExtendedNominal(nominal);
nominal->addExtension(extension);

// Resolve the protocol type.
assert(extension->getInherited().size() == 1);
auto inheritedType = evaluateOrDefault(
extensionCtx.evaluator,
InheritedTypeRequest{extension, 0, TypeResolutionStage::Interface},
Type());

if (!inheritedType || inheritedType->hasError())
continue;

auto protocolType = inheritedType->getAs<ProtocolType>();
if (!protocolType)
continue;

// Create a synthesized conformance and register it with the nominal type.
auto conformance = extensionCtx.getConformance(
nominal->getDeclaredInterfaceType(), protocolType->getDecl(),
nominal->getLoc(), extension, ProtocolConformanceState::Incomplete,
/*isUnchecked=*/false);
conformance->setSourceKindAndImplyingConformance(
ConformanceEntryKind::Synthesized, nullptr);

nominal->registerProtocolConformance(conformance, /*synthesized=*/true);
// Make it accessible to getTopLevelDecls()
if (auto file = dyn_cast<FileUnit>(
decl->getDeclContext()->getModuleScopeContext()))
file->getOrCreateSynthesizedFile().addTopLevelDecl(extension);
}

return macroSourceFile->getBufferID();
Expand Down
11 changes: 11 additions & 0 deletions test/Macros/Inputs/syntax_macro_definitions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,17 @@ public struct EquatableMacro: ConformanceMacro {
}
}

public struct HashableMacro: ConformanceMacro {
public static func expansion(
of node: AttributeSyntax,
providingConformancesOf decl: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [(TypeSyntax, GenericWhereClauseSyntax?)] {
let protocolName: TypeSyntax = "Hashable"
return [(protocolName, nil)]
}
}

public struct DelegatedConformanceMacro: ConformanceMacro, MemberMacro {
public static func expansion(
of node: AttributeSyntax,
Expand Down
13 changes: 13 additions & 0 deletions test/Macros/macro_expand_conformances.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,32 @@
@attached(conformance)
macro Equatable() = #externalMacro(module: "MacroDefinition", type: "EquatableMacro")

@attached(conformance)
macro Hashable() = #externalMacro(module: "MacroDefinition", type: "HashableMacro")

func requireEquatable(_ value: some Equatable) {
print(value == value)
}

func requireHashable(_ value: some Hashable) {
print(value.hashValue)
}

@Equatable
struct S {}

@Hashable
struct S2 {}

// CHECK-DUMP: @__swiftmacro_25macro_expand_conformances1SV9EquatablefMc_.swift
// CHECK-DUMP: extension S : Equatable {}

// CHECK: true
requireEquatable(S())

requireEquatable(S2())
requireHashable(S2())

@attached(conformance)
@attached(member)
macro DelegatedConformance() = #externalMacro(module: "MacroDefinition", type: "DelegatedConformanceMacro")
Expand Down
18 changes: 17 additions & 1 deletion test/SourceKit/Macros/macro_basic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ struct S3 {
}
}

@attached(conformance)
macro Hashable() = #externalMacro(module: "MacroDefinition", type: "HashableMacro")

@Hashable
struct S4 { }

// FIXME: Swift parser is not enabled on Linux CI yet.
// REQUIRES: OS=macosx

Expand Down Expand Up @@ -162,7 +168,7 @@ struct S3 {
// ACCESSOR2_EXPAND-NEXT: source.edit.kind.active:
// ACCESSOR2_EXPAND-NEXT: 33:3-33:20 ""

//##-- Refactoring expanding the second accessor macro
//##-- Refactoring expanding the addCompletionHandler macro.
// RUN: %sourcekitd-test -req=refactoring.expand.macro -pos=42:5 %s -- ${COMPILER_ARGS[@]} | %FileCheck -check-prefix=PEER_EXPAND %s
// PEER_EXPAND: source.edit.kind.active:
// PEER_EXPAND-NEXT: 45:4-45:4 (@__swiftmacro_9MacroUser2S3V1f1a3for_SSSi_SSSdtYaF20addCompletionHandlerfMp_.swift) "
Expand All @@ -176,6 +182,16 @@ struct S3 {
// PEER_EXPAND-NEXT: source.edit.kind.active:
// PEER_EXPAND-NEXT: 42:3-42:24 ""

//##-- Refactoring expanding a conformance macro.
// RUN: %sourcekitd-test -req=refactoring.expand.macro -pos=51:5 %s -- ${COMPILER_ARGS[@]} | %FileCheck -check-prefix=CONFORMANCE_EXPAND %s
// CONFORMANCE_EXPAND: source.edit.kind.active:
// CONFORMANCE_EXPAND-NEXT: 52:14-52:14 (@__swiftmacro_9MacroUser2S4V8HashablefMc_.swift) "
// CONFORMANCE_EXPAND-EMPTY:
// CONFORMANCE_EXPAND-NEXT: extension S4 : Hashable {}
// CONFORMANCE_EXPAND-NEXT: "
// CONFORMANCE_EXPAND-NEXT: source.edit.kind.active:
// CONFORMANCE_EXPAND-NEXT: 51:1-51:10 ""

//##-- Doc info, mostly just checking we don't crash because of the separate buffers
// RUN: %sourcekitd-test -req=doc-info %s -- ${COMPILER_ARGS_WITHOUT_SOURCE[@]} | %FileCheck -check-prefix=DOCINFO %s
// DOCINFO: key.name: "myTypeWrapper()"
Expand Down