Skip to content

[6.2] [Sema] Requestify PatternBindingDecl capture computation #80599

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
Apr 8, 2025
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
28 changes: 19 additions & 9 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2287,6 +2287,7 @@ class PatternBindingEntry {
// Flags::Checked.
friend class PatternBindingEntryRequest;
friend class PatternBindingCheckedAndContextualizedInitRequest;
friend class PatternBindingCaptureInfoRequest;

bool isFullyValidated() const {
return InitContextAndFlags.getInt().contains(
Expand Down Expand Up @@ -2435,8 +2436,20 @@ class PatternBindingEntry {
/// from the source range.
SourceRange getSourceRange(bool omitAccessors = false) const;

CaptureInfo getCaptureInfo() const { return Captures; }
void setCaptureInfo(CaptureInfo captures) { Captures = captures; }
/// Retrieve the computed capture info, or \c nullopt if it hasn't been
/// computed yet.
std::optional<CaptureInfo> getCachedCaptureInfo() const {
if (!Captures.hasBeenComputed())
return std::nullopt;

return Captures;
}

void setCaptureInfo(CaptureInfo captures) {
ASSERT(!Captures.hasBeenComputed());
ASSERT(captures.hasBeenComputed());
Captures = captures;
}

private:
SourceLoc getLastAccessorEndLoc() const;
Expand All @@ -2460,6 +2473,7 @@ class PatternBindingDecl final : public Decl,
friend class Decl;
friend class PatternBindingEntryRequest;
friend class PatternBindingCheckedAndContextualizedInitRequest;
friend class PatternBindingCaptureInfoRequest;

SourceLoc StaticLoc; ///< Location of the 'static/class' keyword, if present.
SourceLoc VarLoc; ///< Location of the 'var' keyword.
Expand Down Expand Up @@ -2639,13 +2653,9 @@ class PatternBindingDecl final : public Decl,
getMutablePatternList()[i].setInitContext(init);
}

CaptureInfo getCaptureInfo(unsigned i) const {
return getPatternList()[i].getCaptureInfo();
}

void setCaptureInfo(unsigned i, CaptureInfo captures) {
getMutablePatternList()[i].setCaptureInfo(captures);
}
/// Retrieve the capture info for the initializer at the given index,
/// computing if needed.
CaptureInfo getCaptureInfo(unsigned i) const;

/// Given that this PBD is the parent pattern for the specified VarDecl,
/// return the entry of the VarDecl in our PatternList. For example, in:
Expand Down
20 changes: 20 additions & 0 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -5134,6 +5134,26 @@ class ParamCaptureInfoRequest :
void cacheResult(CaptureInfo value) const;
};

class PatternBindingCaptureInfoRequest
: public SimpleRequest<PatternBindingCaptureInfoRequest,
CaptureInfo(PatternBindingDecl *, unsigned),
RequestFlags::SeparatelyCached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

CaptureInfo evaluate(Evaluator &evaluator, PatternBindingDecl *PBD,
unsigned idx) const;

public:
// Separate caching.
bool isCached() const { return true; }
std::optional<CaptureInfo> getCachedResult() const;
void cacheResult(CaptureInfo info) const;
};

class SuppressesConformanceRequest
: public SimpleRequest<SuppressesConformanceRequest,
bool(NominalTypeDecl *decl, KnownProtocolKind kp),
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,9 @@ SWIFT_REQUEST(TypeChecker, CaptureInfoRequest,
SWIFT_REQUEST(TypeChecker, ParamCaptureInfoRequest,
CaptureInfo(ParamDecl *),
SeparatelyCached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, PatternBindingCaptureInfoRequest,
CaptureInfo(PatternBindingDecl *, unsigned),
SeparatelyCached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, CustomDerivativesRequest,
CustomDerivativesResult(SourceFile *),
Cached, NoLocationInfo)
Expand Down
7 changes: 7 additions & 0 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2287,6 +2287,13 @@ PatternBindingDecl *PatternBindingDecl::createDeserialized(
return PBD;
}

CaptureInfo PatternBindingDecl::getCaptureInfo(unsigned i) const {
auto *mutableThis = const_cast<PatternBindingDecl *>(this);
PatternBindingCaptureInfoRequest req(mutableThis, i);
return evaluateOrDefault(getASTContext().evaluator, req,
CaptureInfo::empty());
}

PatternBindingInitializer *
PatternBindingInitializer::createDeserialized(PatternBindingDecl *PBD,
unsigned index) {
Expand Down
17 changes: 17 additions & 0 deletions lib/AST/TypeCheckRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2751,6 +2751,23 @@ void ParamCaptureInfoRequest::cacheResult(CaptureInfo info) const {
param->setDefaultArgumentCaptureInfo(info);
}

//----------------------------------------------------------------------------//
// PatternBindingCaptureInfoRequest caching.
//----------------------------------------------------------------------------//

std::optional<CaptureInfo>
PatternBindingCaptureInfoRequest::getCachedResult() const {
auto *PBD = std::get<0>(getStorage());
auto idx = std::get<1>(getStorage());
return PBD->getPatternList()[idx].getCachedCaptureInfo();
}

void PatternBindingCaptureInfoRequest::cacheResult(CaptureInfo info) const {
auto *PBD = std::get<0>(getStorage());
auto idx = std::get<1>(getStorage());
PBD->getMutablePatternList()[idx].setCaptureInfo(info);
}

//----------------------------------------------------------------------------//
// SemanticAvailableAttrRequest computation.
//----------------------------------------------------------------------------//
Expand Down
57 changes: 22 additions & 35 deletions lib/Sema/TypeCheckCaptures.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -857,43 +857,30 @@ CaptureInfo ParamCaptureInfoRequest::evaluate(Evaluator &evaluator,
return finder.getCaptureInfo();
}

static bool isLazy(PatternBindingDecl *PBD) {
if (auto var = PBD->getSingleVar())
return var->getAttrs().hasAttribute<LazyAttr>();
return false;
}

void TypeChecker::checkPatternBindingCaptures(IterableDeclContext *DC) {
for (auto member : DC->getMembers()) {
// Ignore everything other than PBDs.
auto *PBD = dyn_cast<PatternBindingDecl>(member);
if (!PBD) continue;
// Walk the initializers for all properties declared in the type with
// an initializer.
for (unsigned i : range(PBD->getNumPatternEntries())) {
if (PBD->isInitializerSubsumed(i))
continue;
CaptureInfo PatternBindingCaptureInfoRequest::evaluate(Evaluator &evaluator,
PatternBindingDecl *PBD,
unsigned int idx) const {
auto *init = PBD->getExecutableInit(idx);
if (!init)
return CaptureInfo::empty();

auto *init = PBD->getInit(i);
if (init == nullptr)
continue;
// Only have captures when we have a PatternBindingInitializer context, i.e
// local variables don't have captures.
auto *DC = PBD->getInitContext(idx);
if (!DC)
return CaptureInfo::empty();

auto *DC = PBD->getInitContext(i);
FindCapturedVars finder(init->getLoc(),
DC,
/*NoEscape=*/false,
/*ObjC=*/false,
/*IsGenericFunction*/false);
init->walk(finder);

auto &ctx = DC->getASTContext();
if (finder.getDynamicSelfCaptureLoc().isValid() && !isLazy(PBD)) {
ctx.Diags.diagnose(finder.getDynamicSelfCaptureLoc(),
diag::dynamic_self_stored_property_init);
}
FindCapturedVars finder(init->getLoc(), DC,
/*NoEscape=*/false,
/*ObjC=*/false,
/*IsGenericFunction*/ false);
init->walk(finder);

auto captures = finder.getCaptureInfo();
PBD->setCaptureInfo(i, captures);
}
auto &ctx = DC->getASTContext();
if (finder.getDynamicSelfCaptureLoc().isValid()) {
ctx.Diags.diagnose(finder.getDynamicSelfCaptureLoc(),
diag::dynamic_self_stored_property_init);
}

return finder.getCaptureInfo();
}
8 changes: 0 additions & 8 deletions lib/Sema/TypeCheckDeclPrimary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3236,8 +3236,6 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
diagnoseMissingExplicitSendable(ED);
checkAccessControl(ED);

TypeChecker::checkPatternBindingCaptures(ED);

auto &DE = Ctx.Diags;
if (auto rawTy = ED->getRawType()) {
// The raw type must be one of the blessed literal convertible types.
Expand Down Expand Up @@ -3307,8 +3305,6 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
visit(Member);
}

TypeChecker::checkPatternBindingCaptures(SD);

checkInheritanceClause(SD);
diagnoseMissingExplicitSendable(SD);

Expand Down Expand Up @@ -3490,8 +3486,6 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
for (Decl *Member : CD->getABIMembers())
visit(Member);

TypeChecker::checkPatternBindingCaptures(CD);

// If this class requires all of its stored properties to have
// in-class initializers, diagnose this now.
if (CD->requiresStoredPropertyInits())
Expand Down Expand Up @@ -4074,8 +4068,6 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
for (Decl *Member : ED->getMembers())
visit(Member);

TypeChecker::checkPatternBindingCaptures(ED);

TypeChecker::checkConformancesInContext(ED);

checkAccessControl(ED);
Expand Down
3 changes: 3 additions & 0 deletions lib/Sema/TypeCheckStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,9 @@ static void checkAndContextualizePatternBindingInit(PatternBindingDecl *binding,
auto *init = binding->getInit(i);
TypeChecker::contextualizeExpr(init, initContext);
}

// Compute captures in case diagnostics are emitted.
(void)binding->getCaptureInfo(i);
}

Expr *PatternBindingCheckedAndContextualizedInitRequest::evaluate(
Expand Down
3 changes: 0 additions & 3 deletions lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -798,9 +798,6 @@ bool typeCheckForEachPreamble(DeclContext *dc, ForEachStmt *stmt);
/// Compute the set of captures for the given closure.
void computeCaptures(AbstractClosureExpr *ACE);

/// Check for invalid captures from stored property initializers.
void checkPatternBindingCaptures(IterableDeclContext *DC);

/// Update the DeclContexts for AST nodes in a given DeclContext. This is
/// necessary after type-checking since autoclosures may have been introduced.
void contextualizeExpr(Expr *E, DeclContext *DC);
Expand Down
14 changes: 14 additions & 0 deletions test/Macros/Inputs/syntax_macro_definitions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2134,6 +2134,20 @@ public struct VarValueMacro: DeclarationMacro, PeerMacro {
}
}

struct StoredPropertyMacro: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let argument = node.arguments.first?.expression else {
fatalError("boom")
}
return [
"var storedProperty = \(argument)"
]
}
}

public struct GenericToVoidMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
Expand Down
20 changes: 18 additions & 2 deletions test/Macros/macro_expand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ macro freestandingWithClosure<T>(_ value: T, body: (T) -> T) = #externalMacro(mo

#endif

@freestanding(declaration, names: named(storedProperty))
public macro AddStoredProperty<T>(_ x: T) = #externalMacro(module: "MacroDefinition", type: "StoredPropertyMacro")

#if TEST_DIAGNOSTICS
@freestanding(declaration)
macro NotCovered() = #externalMacro(module: "MacroDefinition", type: "InvalidMacro")
Expand All @@ -72,7 +75,7 @@ struct MemberNotCovered {
// expected-note@-1 {{in expansion of macro 'NotCovered' here}}

// CHECK-DIAGS: error: declaration name 'value' is not covered by macro 'NotCovered'
// CHECK-DIAGS: CONTENTS OF FILE @__swiftmacro_9MacroUser0023macro_expandswift_elFCffMX70_2_33_4361AD9339943F52AE6186DD51E04E91Ll10NotCoveredfMf_.swift
// CHECK-DIAGS: CONTENTS OF FILE @__swiftmacro_9MacroUser0023macro_expandswift_elFCffMX[[@LINE-5]]_2_33_4361AD9339943F52AE6186DD51E04E91Ll10NotCoveredfMf_.swift
// CHECK-DIAGS: var value: Int
// CHECK-DIAGS: END CONTENTS OF FILE
}
Expand Down Expand Up @@ -126,6 +129,11 @@ struct Bad {}
// CHECK-DIAGS: typealias _ImageLiteralType = Void
// CHECK-DIAGS: typealias _FileReferenceLiteralType = Void
// CHECK-DIAGS: END CONTENTS OF FILE

class HasStoredPropertyClassInvalid {
#AddStoredProperty((Self.self, 0).1) // expected-note {{in expansion of macro 'AddStoredProperty' here}}
// CHECK-DIAGS: @__swiftmacro_9MacroUser0023macro_expandswift_elFCffMX[[@LINE-2]]_2_33_{{.*}}AddStoredPropertyfMf_.swift:1:22: error: covariant 'Self' type cannot be referenced from a stored property initializer
}
#endif

@freestanding(declaration)
Expand All @@ -138,7 +146,7 @@ macro AccidentalCodeItem() = #externalMacro(module: "MacroDefinition", type: "Fa
func invalidDeclarationMacro() {
#accidentalCodeItem
// expected-note@-1 {{in expansion of macro 'accidentalCodeItem' here}}
// CHECK-DIAGS: @__swiftmacro_9MacroUser0023macro_expandswift_elFCffMX138_2_18accidentalCodeItemfMf_.swift:1:1: error: expected macro expansion to produce a declaration
// CHECK-DIAGS: @__swiftmacro_9MacroUser0023macro_expandswift_elFCffMX[[@LINE-3]]_2_18accidentalCodeItemfMf_.swift:1:1: error: expected macro expansion to produce a declaration

@AccidentalCodeItem struct S {}
// expected-note@-1 {{in expansion of macro 'AccidentalCodeItem' on struct 'S' here}}
Expand Down Expand Up @@ -656,3 +664,11 @@ macro missingMacro() = #externalMacro(module: "MacroDefinition", type: "BluhBlah
macro notMacro() = #externalMacro(module: "MacroDefinition", type: "NotMacroStruct")
// FIXME: xpected-warning@-1 {{macro implementation type 'MacroDefinition.NotMacroStruct' could not be found for macro 'notMacro()'; 'MacroDefinition.NotMacroStruct' is not a valid macro implementation type in library plugin '}}
#endif

// Make sure we compute captures for the introduced stored property here.
struct HasStoredProperty {
#AddStoredProperty(0)
}
class HasStoredPropertyClass {
#AddStoredProperty(0)
}