Skip to content

Macro operators without global operator lookup 5.9 #66325

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
8 changes: 5 additions & 3 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ void SourceLookupCache::addToUnqualifiedLookupCache(Range decls,
// Cache the value under both its compound name and its full name.
TopLevelValues.add(VD);

if (VD->getAttrs().hasAttribute<CustomAttr>()) {
if (!onlyOperators && VD->getAttrs().hasAttribute<CustomAttr>()) {
MayHaveAuxiliaryDecls.push_back(VD);
}
}
Expand Down Expand Up @@ -279,8 +279,10 @@ void SourceLookupCache::addToUnqualifiedLookupCache(Range decls,
else if (auto *PG = dyn_cast<PrecedenceGroupDecl>(D))
PrecedenceGroups[PG->getName()].push_back(PG);

else if (auto *MED = dyn_cast<MacroExpansionDecl>(D))
MayHaveAuxiliaryDecls.push_back(MED);
else if (auto *MED = dyn_cast<MacroExpansionDecl>(D)) {
if (!onlyOperators)
MayHaveAuxiliaryDecls.push_back(MED);
}
}
}

Expand Down
27 changes: 25 additions & 2 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/GenericSignature.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/NameLookupRequests.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/PrettyStackTrace.h"
#include "swift/AST/PotentialMacroExpansions.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/AST/TypeDeclFinder.h"
Expand Down Expand Up @@ -1314,6 +1316,25 @@ WitnessChecker::lookupValueWitnessesViaImplementsAttr(
removeShadowedDecls(witnesses, DC);
}

/// Determine whether the given context may expand an operator with the given name.
static bool contextMayExpandOperator(
DeclContext *dc, DeclBaseName operatorName
) {
TypeOrExtensionDecl decl;
if (auto nominal = dyn_cast<NominalTypeDecl>(dc))
decl = nominal;
else if (auto ext = dyn_cast<ExtensionDecl>(dc))
decl = ext;
else
return false;

ASTContext &ctx = dc->getASTContext();
auto potentialExpansions = evaluateOrDefault(
ctx.evaluator, PotentialMacroExpansionsInContextRequest{decl},
PotentialMacroExpansions());
return potentialExpansions.shouldExpandForName(operatorName);
}

SmallVector<ValueDecl *, 4>
WitnessChecker::lookupValueWitnesses(ValueDecl *req, bool *ignoringNames) {
assert(!isa<AssociatedTypeDecl>(req) && "Not for lookup for type witnesses*");
Expand All @@ -1331,10 +1352,12 @@ WitnessChecker::lookupValueWitnesses(ValueDecl *req, bool *ignoringNames) {
// An operator function is the only kind of witness that requires global
// lookup. However, because global lookup doesn't enter local contexts,
// an additional, qualified lookup is warranted when the conforming type
// is declared in a local context.
// is declared in a local context or when the operator could come from a
// macro expansion.
const bool doUnqualifiedLookup = req->isOperator();
const bool doQualifiedLookup =
!req->isOperator() || DC->getParent()->getLocalContext();
!req->isOperator() || DC->getParent()->getLocalContext() ||
contextMayExpandOperator(DC, req->getName().getBaseName());

if (doUnqualifiedLookup) {
auto lookup = TypeChecker::lookupUnqualified(DC->getModuleScopeContext(),
Expand Down
60 changes: 60 additions & 0 deletions test/Macros/Inputs/syntax_macro_definitions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1505,3 +1505,63 @@ public struct SingleMemberMacro: MemberMacro {
]
}
}

public struct UseIdentifierMacro: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let argument = node.argumentList.first?.expression.as(StringLiteralExprSyntax.self) else {
fatalError("boom")
}
return [
"""
func \(context.makeUniqueName("name"))() {
_ = \(raw: argument.segments.first!)
}
""",
"""
struct Foo {
var \(context.makeUniqueName("name")): Int {
_ = \(raw: argument.segments.first!)
return 0
}
}
"""
]
}
}

public struct StaticFooFuncMacro: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return [
"static func foo() {}",
]
}
}

public struct SelfAlwaysEqualOperator: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return [
"static func ==(lhs: Self, rhs: Bool) -> Bool { true }",
]
}
}

extension SelfAlwaysEqualOperator: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf decl: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return [
"static func ==(lhs: Self, rhs: Bool) -> Bool { true }",
]
}
}
88 changes: 87 additions & 1 deletion test/Macros/macro_expand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
// RUN: %target-swift-frontend -swift-version 5 -emit-ir -load-plugin-library %t/%target-library-name(MacroDefinition) %s -module-name MacroUser -o - -g | %FileCheck --check-prefix CHECK-IR %s

// Execution testing
// RUN: %target-build-swift -swift-version 5 -g -load-plugin-library %t/%target-library-name(MacroDefinition) %s -o %t/main -module-name MacroUser
// RUN: %target-build-swift -swift-version 5 -g -load-plugin-library %t/%target-library-name(MacroDefinition) %s -o %t/main -module-name MacroUser -Xfrontend -emit-dependencies-path -Xfrontend %t/main.d -Xfrontend -emit-reference-dependencies-path -Xfrontend %t/main.swiftdeps
// RUN: %target-codesign %t/main
// RUN: %target-run %t/main | %FileCheck %s

Expand Down Expand Up @@ -417,3 +417,89 @@ func testFreestandingClosureNesting() {
func testLocalVarsFromDeclarationMacros() {
#varValue
}

// Variadic macro
@freestanding(declaration, names: arbitrary) macro emptyDecl(_: String...) = #externalMacro(module: "MacroDefinition", type: "EmptyDeclarationMacro")

struct TakesVariadic {
#emptyDecl("foo", "bar")
}

// Funkiness with static functions introduced via macro expansions.
@freestanding(declaration, names: named(foo())) public macro staticFooFunc() = #externalMacro(module: "MacroDefinition", type: "StaticFooFuncMacro")
@freestanding(declaration, names: arbitrary) public macro staticFooFuncArbitrary() = #externalMacro(module: "MacroDefinition", type: "StaticFooFuncMacro")

class HasAnExpandedStatic {
#staticFooFunc()
}

class HasAnExpandedStatic2 {
#staticFooFuncArbitrary()
}

func testHasAnExpandedStatic() {
#if TEST_DIAGNOSTICS
foo() // expected-error{{cannot find 'foo' in scope}}
#endif
}

@freestanding(declaration, names: named(==)) public macro addSelfEqualsOperator() = #externalMacro(module: "MacroDefinition", type: "SelfAlwaysEqualOperator")
@freestanding(declaration, names: arbitrary) public macro addSelfEqualsOperatorArbitrary() = #externalMacro(module: "MacroDefinition", type: "SelfAlwaysEqualOperator")
@attached(member, names: named(==)) public macro AddSelfEqualsMemberOperator() = #externalMacro(module: "MacroDefinition", type: "SelfAlwaysEqualOperator")
@attached(member, names: arbitrary) public macro AddSelfEqualsMemberOperatorArbitrary() = #externalMacro(module: "MacroDefinition", type: "SelfAlwaysEqualOperator")

struct HasEqualsSelf {
#addSelfEqualsOperator
}

struct HasEqualsSelf2 {
#addSelfEqualsOperatorArbitrary
}

@AddSelfEqualsMemberOperator
struct HasEqualsSelf3 {
}

@AddSelfEqualsMemberOperatorArbitrary
struct HasEqualsSelf4 {
}

protocol SelfEqualsBoolProto { // expected-note 4{{where 'Self' =}}
static func ==(lhs: Self, rhs: Bool) -> Bool
}

struct HasEqualsSelfP: SelfEqualsBoolProto {
#addSelfEqualsOperator
}

struct HasEqualsSelf2P: SelfEqualsBoolProto {
#addSelfEqualsOperatorArbitrary
}

@AddSelfEqualsMemberOperator
struct HasEqualsSelf3P: SelfEqualsBoolProto {
}

@AddSelfEqualsMemberOperatorArbitrary
struct HasEqualsSelf4P: SelfEqualsBoolProto {
}

func testHasEqualsSelf(
x: HasEqualsSelf, y: HasEqualsSelf2, z: HasEqualsSelf3, w: HasEqualsSelf4,
xP: HasEqualsSelfP, yP: HasEqualsSelf2P, zP: HasEqualsSelf3P,
wP: HasEqualsSelf4P
) {
#if TEST_DIAGNOSTICS
// Global operator lookup doesn't find member operators introduced by macros.
_ = (x == true) // expected-error{{referencing operator function '=='}}
_ = (y == true) // expected-error{{referencing operator function '=='}}
_ = (z == true) // expected-error{{referencing operator function '=='}}
_ = (w == true) // expected-error{{referencing operator function '=='}}
#endif

// These should be found through the protocol.
_ = (xP == true)
_ = (yP == true)
_ = (zP == true)
_ = (wP == true)
}
10 changes: 10 additions & 0 deletions test/Macros/macro_expand_primary.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,13 @@ final class Dog: Observable {
func test() {
observeDog()
}


@freestanding(declaration, names: named(Foo)) macro useIdentifier(_ value: String) = #externalMacro(module: "MacroDefinition", type: "UseIdentifierMacro")

#if false
#useIdentifier("totallyNotDefined")
#else
let hello = "Hello"
#useIdentifier("hello")
#endif