From 8f71f5c780a982f6bb5c413bd428484322292f7f Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Wed, 4 Sep 2024 17:01:19 -0700 Subject: [PATCH 01/29] [NFC] Rename property to member to generalize for both properties and method members. --- include/swift/AST/Expr.h | 24 ++++++++++++------------ lib/AST/ASTDumper.cpp | 4 ++-- lib/AST/ASTPrinter.cpp | 2 +- lib/AST/ASTWalker.cpp | 2 +- lib/IDE/SourceEntityWalker.cpp | 2 +- lib/SILGen/SILGenExpr.cpp | 4 ++-- lib/Sema/CSApply.cpp | 22 +++++++++++----------- lib/Sema/CSGen.cpp | 4 ++-- lib/Sema/ConstraintSystem.cpp | 4 ++-- lib/Sema/MiscDiagnostics.cpp | 4 ++-- lib/Sema/TypeCheckAvailability.cpp | 2 +- lib/Sema/TypeCheckCodeCompletion.cpp | 2 +- lib/Sema/TypeCheckExprObjC.cpp | 10 +++++----- 13 files changed, 43 insertions(+), 43 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 37b58726ac8b0..642eed2140f88 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -5887,11 +5887,11 @@ class KeyPathExpr : public Expr { /// - a code completion token class Component { public: - enum class Kind: unsigned { + enum class Kind : unsigned { Invalid, UnresolvedProperty, UnresolvedSubscript, - Property, + Member, Subscript, OptionalForce, OptionalChain, @@ -5901,7 +5901,7 @@ class KeyPathExpr : public Expr { DictionaryKey, CodeCompletion, }; - + private: union DeclNameOrRef { DeclNameRef UnresolvedName; @@ -5928,7 +5928,7 @@ class KeyPathExpr : public Expr { // Private constructor for property or #keyPath dictionary key. explicit Component(DeclNameOrRef decl, Kind kind, Type type, SourceLoc loc) : Component(kind, type, loc) { - assert(kind == Kind::Property || kind == Kind::UnresolvedProperty || + assert(kind == Kind::Member || kind == Kind::UnresolvedProperty || kind == Kind::DictionaryKey); Decl = decl; } @@ -5970,7 +5970,7 @@ class KeyPathExpr : public Expr { static Component forProperty(ConcreteDeclRef property, Type propertyType, SourceLoc loc) { - return Component(property, Kind::Property, propertyType, loc); + return Component(property, Kind::Member, propertyType, loc); } /// Create a component for a dictionary key (#keyPath only). @@ -6046,7 +6046,7 @@ class KeyPathExpr : public Expr { case Kind::OptionalChain: case Kind::OptionalWrap: case Kind::OptionalForce: - case Kind::Property: + case Kind::Member: case Kind::Identity: case Kind::TupleElement: case Kind::DictionaryKey: @@ -6072,7 +6072,7 @@ class KeyPathExpr : public Expr { case Kind::OptionalWrap: case Kind::OptionalForce: case Kind::UnresolvedProperty: - case Kind::Property: + case Kind::Member: case Kind::Identity: case Kind::TupleElement: case Kind::DictionaryKey: @@ -6101,7 +6101,7 @@ class KeyPathExpr : public Expr { case Kind::OptionalWrap: case Kind::OptionalForce: case Kind::UnresolvedProperty: - case Kind::Property: + case Kind::Member: case Kind::Identity: case Kind::TupleElement: case Kind::DictionaryKey: @@ -6123,7 +6123,7 @@ class KeyPathExpr : public Expr { case Kind::OptionalChain: case Kind::OptionalWrap: case Kind::OptionalForce: - case Kind::Property: + case Kind::Member: case Kind::Identity: case Kind::TupleElement: case Kind::CodeCompletion: @@ -6134,7 +6134,7 @@ class KeyPathExpr : public Expr { bool hasDeclRef() const { switch (getKind()) { - case Kind::Property: + case Kind::Member: case Kind::Subscript: return true; @@ -6155,7 +6155,7 @@ class KeyPathExpr : public Expr { ConcreteDeclRef getDeclRef() const { switch (getKind()) { - case Kind::Property: + case Kind::Member: case Kind::Subscript: return Decl.ResolvedDecl; @@ -6186,7 +6186,7 @@ class KeyPathExpr : public Expr { case Kind::OptionalWrap: case Kind::OptionalForce: case Kind::Identity: - case Kind::Property: + case Kind::Member: case Kind::Subscript: case Kind::DictionaryKey: case Kind::CodeCompletion: diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 4f4cd4be38ab8..f2381491fa89d 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -4273,8 +4273,8 @@ class PrintExpr : public ExprVisitor, printHead("optional_wrap", ASTNodeColor, label); break; - case KeyPathExpr::Component::Kind::Property: - printHead("property", ASTNodeColor, label); + case KeyPathExpr::Component::Kind::Member: + printHead("member", ASTNodeColor, label); printDeclRefField(component.getDeclRef(), Label::always("decl")); break; diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 12b44acc3d212..2431643a9988b 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -5083,7 +5083,7 @@ void PrintAST::printKeyPathComponents(KeyPathExpr *expr, ArrayRef SemaAnnotator::walkToExprPre(Expr *E) { } else if (auto *KPE = dyn_cast(E)) { for (auto &component : KPE->getComponents()) { switch (component.getKind()) { - case KeyPathExpr::Component::Kind::Property: + case KeyPathExpr::Component::Kind::Member: case KeyPathExpr::Component::Kind::Subscript: { auto *decl = component.getDeclRef().getDecl(); auto loc = component.getLoc(); diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index b350ce4b3afee..c145b24abde16 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -4426,7 +4426,7 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { for (auto &component : E->getComponents()) { switch (auto kind = component.getKind()) { - case KeyPathExpr::Component::Kind::Property: + case KeyPathExpr::Component::Kind::Member: case KeyPathExpr::Component::Kind::Subscript: { auto decl = cast(component.getDeclRef().getDecl()); @@ -4444,7 +4444,7 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { SGF.FunctionDC, /*for descriptor*/ false)); baseTy = loweredComponents.back().getComponentType(); - if (kind == KeyPathExpr::Component::Kind::Property) + if (kind == KeyPathExpr::Component::Kind::Member) break; auto subscript = cast(decl); diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index ccd6b2febf7f5..b77cac985218a 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -333,7 +333,7 @@ static bool buildObjCKeyPathString(KeyPathExpr *E, // the only component, in which case we use @"self"). continue; - case KeyPathExpr::Component::Kind::Property: { + case KeyPathExpr::Component::Kind::Member: { // Property references must be to @objc properties. // TODO: If we added special properties matching KVC operators like '@sum', // '@count', etc. those could be mapped too. @@ -2520,8 +2520,8 @@ namespace { auto &comp = KPE->getComponents()[kpElt->getIndex()]; if (comp.getKind() == Component::Kind::UnresolvedProperty) { - buildKeyPathPropertyComponent(overload, comp.getLoc(), componentLoc, - components); + buildKeyPathMemberComponent(overload, comp.getLoc(), componentLoc, + components); } else if (comp.getKind() == Component::Kind::UnresolvedSubscript) { buildKeyPathSubscriptComponent(overload, comp.getLoc(), comp.getSubscriptArgs(), componentLoc, @@ -2533,8 +2533,8 @@ namespace { } if (auto *UDE = dyn_cast(anchor)) { - buildKeyPathPropertyComponent(overload, UDE->getLoc(), componentLoc, - components); + buildKeyPathMemberComponent(overload, UDE->getLoc(), componentLoc, + components); } else if (auto *SE = dyn_cast(anchor)) { buildKeyPathSubscriptComponent(overload, SE->getLoc(), SE->getArgs(), componentLoc, components); @@ -5150,9 +5150,9 @@ namespace { switch (kind) { case KeyPathExpr::Component::Kind::UnresolvedProperty: { - buildKeyPathPropertyComponent(solution.getOverloadChoice(calleeLoc), - origComponent.getLoc(), calleeLoc, - resolvedComponents); + buildKeyPathMemberComponent(solution.getOverloadChoice(calleeLoc), + origComponent.getLoc(), calleeLoc, + resolvedComponents); break; } case KeyPathExpr::Component::Kind::UnresolvedSubscript: { @@ -5205,7 +5205,7 @@ namespace { resolvedComponents.push_back(component); break; } - case KeyPathExpr::Component::Kind::Property: + case KeyPathExpr::Component::Kind::Member: case KeyPathExpr::Component::Kind::Subscript: case KeyPathExpr::Component::Kind::OptionalWrap: case KeyPathExpr::Component::Kind::TupleElement: @@ -5369,7 +5369,7 @@ namespace { KeyPathExpr::Component::forOptionalForce(objectTy, loc)); } - void buildKeyPathPropertyComponent( + void buildKeyPathMemberComponent( const SelectedOverload &overload, SourceLoc componentLoc, ConstraintLocator *locator, SmallVectorImpl &components) { @@ -5383,7 +5383,7 @@ namespace { // Compute the concrete reference to the member. auto ref = resolveConcreteDeclRef(property, locator); components.push_back( - KeyPathExpr::Component::forProperty(ref, resolvedTy, componentLoc)); + KeyPathExpr::Component::forMember(ref, resolvedTy, componentLoc)); } else { auto fieldIndex = overload.choice.getTupleIndex(); components.push_back(KeyPathExpr::Component::forTupleElement( diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index b91461a742937..2c7eed3caf679 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3769,7 +3769,7 @@ namespace { case KeyPathExpr::Component::Kind::UnresolvedProperty: // This should only appear in resolved ASTs, but we may need to // re-type-check the constraints during failure diagnosis. - case KeyPathExpr::Component::Kind::Property: { + case KeyPathExpr::Component::Kind::Member: { auto memberTy = CS.createTypeVariable(resultLocator, TVO_CanBindToLValue | TVO_CanBindToNoEscape); @@ -3784,7 +3784,7 @@ namespace { base = memberTy; break; } - + case KeyPathExpr::Component::Kind::UnresolvedSubscript: // Subscript should only appear in resolved ASTs, but we may need to // re-type-check the constraints during failure diagnosis. diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 50975f6662547..822eba9a7af8b 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -738,7 +738,7 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator( return getConstraintLocator( anchor, {*componentElt, ConstraintLocator::SubscriptMember}); case ComponentKind::UnresolvedProperty: - case ComponentKind::Property: + case ComponentKind::Member: // For a property, the choice is just given by the component. return getConstraintLocator(anchor, *componentElt); case ComponentKind::TupleElement: @@ -5103,7 +5103,7 @@ ConstraintSystem::inferKeyPathLiteralCapability(KeyPathExpr *keyPath) { } LLVM_FALLTHROUGH; } - case KeyPathExpr::Component::Kind::Property: + case KeyPathExpr::Component::Kind::Member: case KeyPathExpr::Component::Kind::UnresolvedProperty: { auto *componentLoc = getConstraintLocator(keyPath, LocatorPathElt::KeyPathComponent(i)); diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 4d2904234bffb..a6ff23c9bda73 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -5741,7 +5741,7 @@ static void diagnoseDeprecatedWritableKeyPath(const Expr *E, assert(keyPathExpr->getComponents().size() > 0); auto &component = keyPathExpr->getComponents().back(); - if (component.getKind() == KeyPathExpr::Component::Kind::Property) { + if (component.getKind() == KeyPathExpr::Component::Kind::Member) { auto *storage = cast(component.getDeclRef().getDecl()); if (!storage->isSettable(nullptr) || @@ -5817,7 +5817,7 @@ static void maybeDiagnoseCallToKeyValueObserveMethod(const Expr *E, } for (auto *keyPathArg : keyPathArgs) { auto lastComponent = keyPathArg->getComponents().back(); - if (lastComponent.getKind() != KeyPathExpr::Component::Kind::Property) + if (lastComponent.getKind() != KeyPathExpr::Component::Kind::Member) continue; auto property = lastComponent.getDeclRef().getDecl(); if (!property) diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index bbd07f4ef4564..72d506aa45980 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -2524,7 +2524,7 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker { for (auto &component : KP->getComponents()) { switch (component.getKind()) { - case KeyPathExpr::Component::Kind::Property: + case KeyPathExpr::Component::Kind::Member: case KeyPathExpr::Component::Kind::Subscript: { auto decl = component.getDeclRef(); auto loc = component.getLoc(); diff --git a/lib/Sema/TypeCheckCodeCompletion.cpp b/lib/Sema/TypeCheckCodeCompletion.cpp index 6a18a34f27a16..2728c88bc2187 100644 --- a/lib/Sema/TypeCheckCodeCompletion.cpp +++ b/lib/Sema/TypeCheckCodeCompletion.cpp @@ -319,7 +319,7 @@ getTypeOfCompletionContextExpr(DeclContext *DC, CompletionTypeCheckKind kind, if (!components.empty()) { auto &last = components.back(); if (last.isResolved()) { - if (last.getKind() == KeyPathExpr::Component::Kind::Property) + if (last.getKind() == KeyPathExpr::Component::Kind::Member) referencedDecl = last.getDeclRef(); Type lookupTy = last.getComponentType(); ASTContext &Ctx = DC->getASTContext(); diff --git a/lib/Sema/TypeCheckExprObjC.cpp b/lib/Sema/TypeCheckExprObjC.cpp index ab0d8a7a7b951..9b1874713d6a1 100644 --- a/lib/Sema/TypeCheckExprObjC.cpp +++ b/lib/Sema/TypeCheckExprObjC.cpp @@ -220,7 +220,7 @@ std::optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, (unsigned)kind); continue; case KeyPathExpr::Component::Kind::OptionalWrap: - case KeyPathExpr::Component::Kind::Property: + case KeyPathExpr::Component::Kind::Member: case KeyPathExpr::Component::Kind::Subscript: case KeyPathExpr::Component::Kind::DictionaryKey: llvm_unreachable("already resolved!"); @@ -331,8 +331,8 @@ std::optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, // Updates currentType updateState(/*isProperty=*/true, varTy); - auto resolved = KeyPathExpr::Component::forProperty(varRef, currentType, - componentNameLoc); + auto resolved = KeyPathExpr::Component::forMember(varRef, currentType, + componentNameLoc); resolvedComponents.push_back(resolved); // Check that the property is @objc. @@ -390,8 +390,8 @@ std::optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, // Resolve this component to the type we found. auto typeRef = ConcreteDeclRef(type); - auto resolved = KeyPathExpr::Component::forProperty(typeRef, currentType, - componentNameLoc); + auto resolved = KeyPathExpr::Component::forMember(typeRef, currentType, + componentNameLoc); resolvedComponents.push_back(resolved); continue; From 811d54901b89d3e87b55b1904111269e97ffba1a Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Wed, 4 Sep 2024 17:09:40 -0700 Subject: [PATCH 02/29] [NFC] Rename unresolvedProperty to unresolvedMember to generalize for both properties and method members. --- include/swift/AST/Expr.h | 28 ++++++++++++++-------------- lib/AST/ASTDumper.cpp | 12 ++++++------ lib/AST/ASTPrinter.cpp | 2 +- lib/AST/ASTWalker.cpp | 2 +- lib/IDE/SourceEntityWalker.cpp | 2 +- lib/Parse/ParseExpr.cpp | 4 ++-- lib/SILGen/SILGenExpr.cpp | 2 +- lib/Sema/CSApply.cpp | 8 ++++---- lib/Sema/CSGen.cpp | 11 +++++++---- lib/Sema/ConstraintSystem.cpp | 4 ++-- lib/Sema/PreCheckTarget.cpp | 2 +- lib/Sema/TypeCheckAvailability.cpp | 2 +- lib/Sema/TypeCheckExprObjC.cpp | 2 +- 13 files changed, 42 insertions(+), 39 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 642eed2140f88..2b4027e9eda64 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -5889,7 +5889,7 @@ class KeyPathExpr : public Expr { public: enum class Kind : unsigned { Invalid, - UnresolvedProperty, + UnresolvedMember, UnresolvedSubscript, Member, Subscript, @@ -5928,7 +5928,7 @@ class KeyPathExpr : public Expr { // Private constructor for property or #keyPath dictionary key. explicit Component(DeclNameOrRef decl, Kind kind, Type type, SourceLoc loc) : Component(kind, type, loc) { - assert(kind == Kind::Member || kind == Kind::UnresolvedProperty || + assert(kind == Kind::Member || kind == Kind::UnresolvedMember || kind == Kind::DictionaryKey); Decl = decl; } @@ -5946,12 +5946,12 @@ class KeyPathExpr : public Expr { public: Component() : Component(Kind::Invalid, Type(), SourceLoc()) {} - /// Create an unresolved component for a property. - static Component forUnresolvedProperty(DeclNameRef UnresolvedName, - SourceLoc Loc) { - return Component(UnresolvedName, Kind::UnresolvedProperty, Type(), Loc); + /// Create an unresolved component for a member. + static Component forUnresolvedMember(DeclNameRef UnresolvedName, + SourceLoc Loc) { + return Component(UnresolvedName, Kind::UnresolvedMember, Type(), Loc); } - + /// Create an unresolved component for a subscript. static Component forUnresolvedSubscript(ASTContext &ctx, ArgumentList *argList); @@ -6053,7 +6053,7 @@ class KeyPathExpr : public Expr { return true; case Kind::UnresolvedSubscript: - case Kind::UnresolvedProperty: + case Kind::UnresolvedMember: case Kind::Invalid: case Kind::CodeCompletion: return false; @@ -6071,7 +6071,7 @@ class KeyPathExpr : public Expr { case Kind::OptionalChain: case Kind::OptionalWrap: case Kind::OptionalForce: - case Kind::UnresolvedProperty: + case Kind::UnresolvedMember: case Kind::Member: case Kind::Identity: case Kind::TupleElement: @@ -6100,7 +6100,7 @@ class KeyPathExpr : public Expr { case Kind::OptionalChain: case Kind::OptionalWrap: case Kind::OptionalForce: - case Kind::UnresolvedProperty: + case Kind::UnresolvedMember: case Kind::Member: case Kind::Identity: case Kind::TupleElement: @@ -6113,7 +6113,7 @@ class KeyPathExpr : public Expr { DeclNameRef getUnresolvedDeclName() const { switch (getKind()) { - case Kind::UnresolvedProperty: + case Kind::UnresolvedMember: case Kind::DictionaryKey: return Decl.UnresolvedName; @@ -6139,7 +6139,7 @@ class KeyPathExpr : public Expr { return true; case Kind::Invalid: - case Kind::UnresolvedProperty: + case Kind::UnresolvedMember: case Kind::UnresolvedSubscript: case Kind::OptionalChain: case Kind::OptionalWrap: @@ -6160,7 +6160,7 @@ class KeyPathExpr : public Expr { return Decl.ResolvedDecl; case Kind::Invalid: - case Kind::UnresolvedProperty: + case Kind::UnresolvedMember: case Kind::UnresolvedSubscript: case Kind::OptionalChain: case Kind::OptionalWrap: @@ -6180,7 +6180,7 @@ class KeyPathExpr : public Expr { return TupleIndex; case Kind::Invalid: - case Kind::UnresolvedProperty: + case Kind::UnresolvedMember: case Kind::UnresolvedSubscript: case Kind::OptionalChain: case Kind::OptionalWrap: diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index f2381491fa89d..660f43c3e56a8 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -4278,17 +4278,17 @@ class PrintExpr : public ExprVisitor, printDeclRefField(component.getDeclRef(), Label::always("decl")); break; + case KeyPathExpr::Component::Kind::UnresolvedMember: + printHead("unresolved_member", ASTNodeColor, label); + printFieldQuoted(component.getUnresolvedDeclName(), + Label::always("decl_name"), IdentifierColor); + break; + case KeyPathExpr::Component::Kind::Subscript: printHead("subscript", ASTNodeColor, label); printDeclRefField(component.getDeclRef(), Label::always("decl")); break; - case KeyPathExpr::Component::Kind::UnresolvedProperty: - printHead("unresolved_property", ASTNodeColor, label); - printFieldQuoted(component.getUnresolvedDeclName(), - Label::always("decl_name"), IdentifierColor); - break; - case KeyPathExpr::Component::Kind::UnresolvedSubscript: printHead("unresolved_subscript", ASTNodeColor, label); break; diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 2431643a9988b..56b25b73888a2 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -5074,7 +5074,7 @@ void PrintAST::printKeyPathComponents(KeyPathExpr *expr, ArrayRef SemaAnnotator::walkToExprPre(Expr *E) { case KeyPathExpr::Component::Kind::TupleElement: case KeyPathExpr::Component::Kind::Invalid: - case KeyPathExpr::Component::Kind::UnresolvedProperty: + case KeyPathExpr::Component::Kind::UnresolvedMember: case KeyPathExpr::Component::Kind::UnresolvedSubscript: case KeyPathExpr::Component::Kind::OptionalChain: case KeyPathExpr::Component::Kind::OptionalWrap: diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index e8f4dd6baf42a..cae4b07d7fc59 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -850,8 +850,8 @@ ParserResult Parser::parseExprKeyPathObjC() { } // Record the name we parsed. - auto component = KeyPathExpr::Component::forUnresolvedProperty(name, - nameLoc.getBaseNameLoc()); + auto component = KeyPathExpr::Component::forUnresolvedMember( + name, nameLoc.getBaseNameLoc()); components.push_back(component); // After the first component, we can start parsing keywords. diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index c145b24abde16..f9ca08c13b62a 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -4505,7 +4505,7 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { continue; case KeyPathExpr::Component::Kind::Invalid: - case KeyPathExpr::Component::Kind::UnresolvedProperty: + case KeyPathExpr::Component::Kind::UnresolvedMember: case KeyPathExpr::Component::Kind::UnresolvedSubscript: case KeyPathExpr::Component::Kind::CodeCompletion: llvm_unreachable("not resolved"); diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index b77cac985218a..74ce390c53f91 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -355,7 +355,7 @@ static bool buildObjCKeyPathString(KeyPathExpr *E, // a mapping subscript operation to Array/Set or NSArray/NSSet. return false; case KeyPathExpr::Component::Kind::Invalid: - case KeyPathExpr::Component::Kind::UnresolvedProperty: + case KeyPathExpr::Component::Kind::UnresolvedMember: case KeyPathExpr::Component::Kind::UnresolvedSubscript: case KeyPathExpr::Component::Kind::CodeCompletion: // Don't bother building the key path string if the key path didn't even @@ -2519,7 +2519,7 @@ namespace { assert(kpElt && "no keypath component node"); auto &comp = KPE->getComponents()[kpElt->getIndex()]; - if (comp.getKind() == Component::Kind::UnresolvedProperty) { + if (comp.getKind() == Component::Kind::UnresolvedMember) { buildKeyPathMemberComponent(overload, comp.getLoc(), componentLoc, components); } else if (comp.getKind() == Component::Kind::UnresolvedSubscript) { @@ -5129,7 +5129,7 @@ namespace { bool isDynamicMember = false; // If this is an unresolved link, make sure we resolved it. - if (kind == KeyPathExpr::Component::Kind::UnresolvedProperty || + if (kind == KeyPathExpr::Component::Kind::UnresolvedMember || kind == KeyPathExpr::Component::Kind::UnresolvedSubscript) { auto foundDecl = solution.getOverloadChoiceIfAvailable(calleeLoc); if (!foundDecl) { @@ -5149,7 +5149,7 @@ namespace { } switch (kind) { - case KeyPathExpr::Component::Kind::UnresolvedProperty: { + case KeyPathExpr::Component::Kind::UnresolvedMember: { buildKeyPathMemberComponent(solution.getOverloadChoice(calleeLoc), origComponent.getLoc(), calleeLoc, resolvedComponents); diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 2c7eed3caf679..1e1097fcce388 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3766,7 +3766,7 @@ namespace { resultLocator, TVO_CanBindToLValue | TVO_CanBindToNoEscape | TVO_CanBindToHole); break; - case KeyPathExpr::Component::Kind::UnresolvedProperty: + case KeyPathExpr::Component::Kind::UnresolvedMember: // This should only appear in resolved ASTs, but we may need to // re-type-check the constraints during failure diagnosis. case KeyPathExpr::Component::Kind::Member: { @@ -3774,9 +3774,12 @@ namespace { TVO_CanBindToLValue | TVO_CanBindToNoEscape); componentTypeVars.push_back(memberTy); - auto lookupName = kind == KeyPathExpr::Component::Kind::UnresolvedProperty - ? DeclNameRef(component.getUnresolvedDeclName()) // FIXME: type change needed - : component.getDeclRef().getDecl()->createNameRef(); + auto lookupName = + kind == KeyPathExpr::Component::Kind::UnresolvedMember + ? DeclNameRef( + component.getUnresolvedDeclName()) // FIXME: type change + // needed + : component.getDeclRef().getDecl()->createNameRef(); CS.addValueMemberConstraint(base, lookupName, memberTy, CurDC, FunctionRefInfo::unapplied(lookupName), diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 822eba9a7af8b..41c8c67243086 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -737,7 +737,7 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator( // For a subscript the callee is given by 'component -> subscript member'. return getConstraintLocator( anchor, {*componentElt, ConstraintLocator::SubscriptMember}); - case ComponentKind::UnresolvedProperty: + case ComponentKind::UnresolvedMember: case ComponentKind::Member: // For a property, the choice is just given by the component. return getConstraintLocator(anchor, *componentElt); @@ -5104,7 +5104,7 @@ ConstraintSystem::inferKeyPathLiteralCapability(KeyPathExpr *keyPath) { LLVM_FALLTHROUGH; } case KeyPathExpr::Component::Kind::Member: - case KeyPathExpr::Component::Kind::UnresolvedProperty: { + case KeyPathExpr::Component::Kind::UnresolvedMember: { auto *componentLoc = getConstraintLocator(keyPath, LocatorPathElt::KeyPathComponent(i)); auto *calleeLoc = getCalleeLocator(componentLoc); diff --git a/lib/Sema/PreCheckTarget.cpp b/lib/Sema/PreCheckTarget.cpp index 70c20492a2297..30fba1a63ef8e 100644 --- a/lib/Sema/PreCheckTarget.cpp +++ b/lib/Sema/PreCheckTarget.cpp @@ -2455,7 +2455,7 @@ void PreCheckTarget::resolveKeyPathExpr(KeyPathExpr *KPE) { expr = SE->getSubExpr(); } else if (auto UDE = dyn_cast(expr)) { // .foo - components.push_back(KeyPathExpr::Component::forUnresolvedProperty( + components.push_back(KeyPathExpr::Component::forUnresolvedMember( UDE->getName(), UDE->getLoc())); expr = UDE->getBase(); diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 72d506aa45980..125cb460c3ab5 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -2539,7 +2539,7 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker { break; case KeyPathExpr::Component::Kind::Invalid: - case KeyPathExpr::Component::Kind::UnresolvedProperty: + case KeyPathExpr::Component::Kind::UnresolvedMember: case KeyPathExpr::Component::Kind::UnresolvedSubscript: case KeyPathExpr::Component::Kind::OptionalChain: case KeyPathExpr::Component::Kind::OptionalWrap: diff --git a/lib/Sema/TypeCheckExprObjC.cpp b/lib/Sema/TypeCheckExprObjC.cpp index 9b1874713d6a1..3dac9c333baf9 100644 --- a/lib/Sema/TypeCheckExprObjC.cpp +++ b/lib/Sema/TypeCheckExprObjC.cpp @@ -209,7 +209,7 @@ std::optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, case KeyPathExpr::Component::Kind::CodeCompletion: continue; - case KeyPathExpr::Component::Kind::UnresolvedProperty: + case KeyPathExpr::Component::Kind::UnresolvedMember: break; case KeyPathExpr::Component::Kind::UnresolvedSubscript: case KeyPathExpr::Component::Kind::OptionalChain: From 2583da94b29cd493913c0501a47862afaf50b394 Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Wed, 4 Sep 2024 17:56:12 -0700 Subject: [PATCH 03/29] [NFC] Generalize subscript index handling. --- include/swift/AST/Expr.h | 23 +++++++++++------------ lib/AST/ASTDumper.cpp | 4 ++-- lib/AST/ASTPrinter.cpp | 4 ++-- lib/AST/ASTWalker.cpp | 4 ++-- lib/AST/Expr.cpp | 10 +++++----- lib/SILGen/SILGenExpr.cpp | 23 ++++++++--------------- lib/Sema/CSApply.cpp | 9 ++++----- lib/Sema/CSGen.cpp | 2 +- lib/Sema/ConstraintSystem.cpp | 6 +++--- lib/Sema/TypeCheckConcurrency.cpp | 2 +- 10 files changed, 39 insertions(+), 48 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 2b4027e9eda64..9f87b66f5df06 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -5912,8 +5912,8 @@ class KeyPathExpr : public Expr { DeclNameOrRef(ConcreteDeclRef rd) : ResolvedDecl(rd) {} } Decl; - ArgumentList *SubscriptArgList; - const ProtocolConformanceRef *SubscriptHashableConformancesData; + ArgumentList *ArgList; + const ProtocolConformanceRef *HashableConformancesData; unsigned TupleIndex; Kind KindValue; @@ -6023,7 +6023,7 @@ class KeyPathExpr : public Expr { } SourceRange getSourceRange() const { - if (auto *args = getSubscriptArgs()) { + if (auto *args = getArgs()) { return args->getSourceRange(); } return Loc; @@ -6061,11 +6061,11 @@ class KeyPathExpr : public Expr { llvm_unreachable("unhandled kind"); } - ArgumentList *getSubscriptArgs() const { + ArgumentList *getArgs() const { switch (getKind()) { case Kind::Subscript: case Kind::UnresolvedSubscript: - return SubscriptArgList; + return ArgList; case Kind::Invalid: case Kind::OptionalChain: @@ -6082,18 +6082,17 @@ class KeyPathExpr : public Expr { llvm_unreachable("unhandled kind"); } - void setSubscriptArgs(ArgumentList *newArgs) { - assert(getSubscriptArgs() && "Should be replacing existing args"); - SubscriptArgList = newArgs; + void setArgs(ArgumentList *newArgs) { + assert(getArgs() && "Should be replacing existing args"); + ArgList = newArgs; } - ArrayRef - getSubscriptIndexHashableConformances() const { + ArrayRef getIndexHashableConformances() const { switch (getKind()) { case Kind::Subscript: - if (!SubscriptHashableConformancesData) + if (!HashableConformancesData) return {}; - return {SubscriptHashableConformancesData, SubscriptArgList->size()}; + return {HashableConformancesData, ArgList->size()}; case Kind::UnresolvedSubscript: case Kind::Invalid: diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 660f43c3e56a8..6b4a1a2e3f046 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -4311,8 +4311,8 @@ class PrintExpr : public ExprVisitor, break; } printTypeField(GetTypeOfKeyPathComponent(E, i), Label::always("type")); - if (auto *args = component.getSubscriptArgs()) { - printRec(args, Label::optional("subscript_args")); + if (auto *args = component.getArgs()) { + printRec(args, Label::optional("args")); } printFoot(); }, Label::optional("component")); diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 56b25b73888a2..4536b88aeb6d6 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -5079,7 +5079,7 @@ void PrintAST::printKeyPathComponents(KeyPathExpr *expr, ArrayRef KeyPathExpr::findComponentWithSubscriptArg(Expr *arg) { for (auto idx : indices(getComponents())) { - if (auto *args = getComponents()[idx].getSubscriptArgs()) { + if (auto *args = getComponents()[idx].getArgs()) { if (args->findArgumentExpr(arg)) return idx; } @@ -2554,13 +2554,13 @@ KeyPathExpr::Component::Component( DeclNameOrRef decl, ArgumentList *argList, ArrayRef indexHashables, Kind kind, Type type, SourceLoc loc) - : Decl(decl), SubscriptArgList(argList), KindValue(kind), - ComponentType(type), Loc(loc) { + : Decl(decl), ArgList(argList), KindValue(kind), ComponentType(type), + Loc(loc) { assert(kind == Kind::Subscript || kind == Kind::UnresolvedSubscript); assert(argList); assert(argList->size() == indexHashables.size() || indexHashables.empty()); - SubscriptHashableConformancesData = indexHashables.empty() - ? nullptr : indexHashables.data(); + HashableConformancesData = + indexHashables.empty() ? nullptr : indexHashables.data(); } SingleValueStmtExpr *SingleValueStmtExpr::create(ASTContext &ctx, Stmt *S, diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index f9ca08c13b62a..1d863e9b8e464 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -4431,27 +4431,20 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { auto decl = cast(component.getDeclRef().getDecl()); unsigned numOperands = operands.size(); - loweredComponents.push_back( - SGF.SGM.emitKeyPathComponentForDecl(SILLocation(E), - SGF.F.getGenericEnvironment(), - SGF.F.getResilienceExpansion(), - numOperands, - needsGenericContext, - component.getDeclRef().getSubstitutions(), - decl, - component.getSubscriptIndexHashableConformances(), - baseTy, - SGF.FunctionDC, - /*for descriptor*/ false)); + loweredComponents.push_back(SGF.SGM.emitKeyPathComponentForDecl( + SILLocation(E), SGF.F.getGenericEnvironment(), + SGF.F.getResilienceExpansion(), numOperands, needsGenericContext, + component.getDeclRef().getSubstitutions(), decl, + component.getIndexHashableConformances(), baseTy, SGF.FunctionDC, + /*for descriptor*/ false)); baseTy = loweredComponents.back().getComponentType(); if (kind == KeyPathExpr::Component::Kind::Member) break; auto subscript = cast(decl); auto loweredArgs = SGF.emitKeyPathSubscriptOperands( - E, subscript, - component.getDeclRef().getSubstitutions(), - component.getSubscriptArgs()); + E, subscript, component.getDeclRef().getSubstitutions(), + component.getArgs()); for (auto &arg : loweredArgs) { operands.push_back(arg.forward(SGF)); diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 74ce390c53f91..28db28b127bdf 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -2524,7 +2524,7 @@ namespace { components); } else if (comp.getKind() == Component::Kind::UnresolvedSubscript) { buildKeyPathSubscriptComponent(overload, comp.getLoc(), - comp.getSubscriptArgs(), componentLoc, + comp.getArgs(), componentLoc, components); } else { return nullptr; @@ -5156,10 +5156,9 @@ namespace { break; } case KeyPathExpr::Component::Kind::UnresolvedSubscript: { - buildKeyPathSubscriptComponent(solution.getOverloadChoice(calleeLoc), - origComponent.getLoc(), - origComponent.getSubscriptArgs(), - componentLocator, resolvedComponents); + buildKeyPathSubscriptComponent( + solution.getOverloadChoice(calleeLoc), origComponent.getLoc(), + origComponent.getArgs(), componentLocator, resolvedComponents); break; } case KeyPathExpr::Component::Kind::OptionalChain: { diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 1e1097fcce388..8f0d4224198f3 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3792,7 +3792,7 @@ namespace { // Subscript should only appear in resolved ASTs, but we may need to // re-type-check the constraints during failure diagnosis. case KeyPathExpr::Component::Kind::Subscript: { - auto *args = component.getSubscriptArgs(); + auto *args = component.getArgs(); base = addSubscriptConstraints(E, base, /*decl*/ nullptr, args, memberLocator, &componentTypeVars); diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 41c8c67243086..7b1e360b21de6 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -3601,7 +3601,7 @@ void constraints::simplifyLocator(ASTNode &anchor, auto argIdx = applyArgElt->getArgIdx(); if (auto *kpe = getAsExpr(anchor)) { auto component = kpe->getComponents()[elt.getIndex()]; - auto *args = component.getSubscriptArgs(); + auto *args = component.getArgs(); assert(args && "Trying to apply a component without args?"); if (argIdx < args->size()) { anchor = args->getExpr(argIdx); @@ -4290,7 +4290,7 @@ ConstraintLocator *ConstraintSystem::getArgumentLocator(Expr *expr) { if (!idx) return nullptr; loc = getConstraintLocator(KP, {LocatorPathElt::KeyPathComponent(*idx)}); - argList = KP->getComponents()[*idx].getSubscriptArgs(); + argList = KP->getComponents()[*idx].getArgs(); } else { loc = getConstraintLocator(application); } @@ -5078,7 +5078,7 @@ ConstraintSystem::inferKeyPathLiteralCapability(KeyPathExpr *keyPath) { case KeyPathExpr::Component::Kind::Subscript: { if (Context.LangOpts.hasFeature(Feature::InferSendableFromCaptures)) { // Key path is sendable only when all of its captures are sendable. - if (auto *args = component.getSubscriptArgs()) { + if (auto *args = component.getArgs()) { auto *sendable = Context.getProtocol(KnownProtocolKind::Sendable); for (const auto &arg : *args) { diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index ee3283ddca51e..265c83ebaa2a5 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -4416,7 +4416,7 @@ namespace { // Captured values in a path component must conform to Sendable. // These captured values appear in Subscript, such as \Type.dict[k] // where k is a captured dictionary key. - if (auto *args = component.getSubscriptArgs()) { + if (auto *args = component.getArgs()) { for (auto arg : *args) { auto type = getType(arg.getExpr()); if (type && shouldDiagnoseExistingDataRaces(getDeclContext()) && From 0af27a63540a6e819e5deb021d87dc2bdd3adcd1 Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Tue, 19 Nov 2024 15:06:12 -0800 Subject: [PATCH 04/29] [ASTGen] Handle keypath methods. --- lib/ASTGen/Sources/ASTGen/Exprs.swift | 24 ++++++++++++++++++++-- lib/ASTGen/Sources/ASTGen/SourceFile.swift | 2 +- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/lib/ASTGen/Sources/ASTGen/Exprs.swift b/lib/ASTGen/Sources/ASTGen/Exprs.swift index 7cd8a51dd897a..9d7da49fedb39 100644 --- a/lib/ASTGen/Sources/ASTGen/Exprs.swift +++ b/lib/ASTGen/Sources/ASTGen/Exprs.swift @@ -736,9 +736,29 @@ extension ASTGenVisitor { additionalTrailingClosures: nil ) ).asExpr + + case .method(let method): + let dotLoc = self.generateSourceLoc(node.period) + let declNameRef = self.generateDeclNameRef(declReferenceExpr: method.declName) + let unresolvedDotExpr = BridgedUnresolvedDotExpr.createParsed( + self.ctx, + base: baseExpr, + dotLoc: dotLoc, + name: declNameRef.name, + nameLoc: declNameRef.loc + ).asExpr - case .method(_): - fatalError("unimplemented (method keypath)") + let args = self.generateArgumentList( + leftParen: method.leftParen, + labeledExprList: method.arguments, + rightParen: method.rightParen, + trailingClosure: nil, + additionalTrailingClosures: nil) + return BridgedCallExpr.createParsed( + self.ctx, + fn: unresolvedDotExpr, + args: args + ).asExpr } } diff --git a/lib/ASTGen/Sources/ASTGen/SourceFile.swift b/lib/ASTGen/Sources/ASTGen/SourceFile.swift index f81189dd1a9f2..be8e09d393f91 100644 --- a/lib/ASTGen/Sources/ASTGen/SourceFile.swift +++ b/lib/ASTGen/Sources/ASTGen/SourceFile.swift @@ -77,8 +77,8 @@ extension Parser.ExperimentalFeatures { mapFeature(.CoroutineAccessors, to: .coroutineAccessors) mapFeature(.ValueGenerics, to: .valueGenerics) mapFeature(.ABIAttribute, to: .abiAttribute) - // TODO: mapFeature(.KeypathWithMethodMembers, to: .keypathWithMethodMembers) mapFeature(.OldOwnershipOperatorSpellings, to: .oldOwnershipOperatorSpellings) + mapFeature(.KeyPathWithMethodMembers, to: .keypathWithMethodMembers) } } From 0c614e09d1d88f82c9caa78909e987b49d4a69e8 Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Wed, 4 Sep 2024 18:17:55 -0700 Subject: [PATCH 05/29] [Expr/AST] Add unresolvedApply component to handle method arguments. --- include/swift/AST/Expr.h | 67 ++++++++++++++++++++++++++++++---- lib/AST/ASTDumper.cpp | 3 ++ lib/AST/ASTPrinter.cpp | 5 +++ lib/AST/ASTWalker.cpp | 5 ++- lib/AST/Expr.cpp | 3 +- lib/IDE/SourceEntityWalker.cpp | 1 + lib/Parse/ParseExpr.cpp | 2 +- lib/Sema/PreCheckTarget.cpp | 9 ++++- 8 files changed, 81 insertions(+), 14 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 9f87b66f5df06..75fac411e9cc6 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -5889,6 +5889,7 @@ class KeyPathExpr : public Expr { public: enum class Kind : unsigned { Invalid, + UnresolvedApply, UnresolvedMember, UnresolvedSubscript, Member, @@ -5914,13 +5915,15 @@ class KeyPathExpr : public Expr { ArgumentList *ArgList; const ProtocolConformanceRef *HashableConformancesData; + std::optional ComponentFuncRefKind; unsigned TupleIndex; Kind KindValue; Type ComponentType; SourceLoc Loc; - // Private constructor for subscript component. + // Private constructor for unresolvedSubscript, subscript and + // unresolvedApply component. explicit Component(DeclNameOrRef decl, ArgumentList *argList, ArrayRef indexHashables, Kind kind, Type type, SourceLoc loc); @@ -5933,6 +5936,14 @@ class KeyPathExpr : public Expr { Decl = decl; } + // Private constructor for an unresolvedMember or member kind. + explicit Component(DeclNameOrRef decl, Kind kind, Type type, + FunctionRefInfo funcRefKind, SourceLoc loc) + : Decl(decl), ComponentFuncRefKind(funcRefKind), KindValue(kind), + ComponentType(type), Loc(loc) { + assert(kind == Kind::Member || kind == Kind::UnresolvedMember); + } + // Private constructor for tuple element kind. explicit Component(unsigned tupleIndex, Type elementType, SourceLoc loc) : Component(Kind::TupleElement, elementType, loc) { @@ -5941,15 +5952,25 @@ class KeyPathExpr : public Expr { // Private constructor for basic components with no additional information. explicit Component(Kind kind, Type type, SourceLoc loc) - : Decl(), KindValue(kind), ComponentType(type), Loc(loc) {} + : Decl(), ComponentFuncRefKind(std::nullopt), KindValue(kind), + ComponentType(type), Loc(loc) {} public: Component() : Component(Kind::Invalid, Type(), SourceLoc()) {} - /// Create an unresolved component for a member. + /// Create an unresolved component for an unresolved apply. + static Component forUnresolvedApply(ASTContext &ctx, + ArgumentList *argList) { + return Component({}, argList, {}, Kind::UnresolvedApply, Type(), + argList->getLParenLoc()); + }; + + /// Create an unresolved component for an unresolved member. static Component forUnresolvedMember(DeclNameRef UnresolvedName, - SourceLoc Loc) { - return Component(UnresolvedName, Kind::UnresolvedMember, Type(), Loc); + FunctionRefInfo funcRefKind, + SourceLoc loc) { + return Component(UnresolvedName, Kind::UnresolvedMember, Type(), + funcRefKind, loc); } /// Create an unresolved component for a subscript. @@ -5967,9 +5988,8 @@ class KeyPathExpr : public Expr { } /// Create a component for a property. - static Component forProperty(ConcreteDeclRef property, - Type propertyType, - SourceLoc loc) { + static Component forMember(ConcreteDeclRef property, Type propertyType, + SourceLoc loc) { return Component(property, Kind::Member, propertyType, loc); } @@ -6054,6 +6074,7 @@ class KeyPathExpr : public Expr { case Kind::UnresolvedSubscript: case Kind::UnresolvedMember: + case Kind::UnresolvedApply: case Kind::Invalid: case Kind::CodeCompletion: return false; @@ -6065,6 +6086,7 @@ class KeyPathExpr : public Expr { switch (getKind()) { case Kind::Subscript: case Kind::UnresolvedSubscript: + case Kind::UnresolvedApply: return ArgList; case Kind::Invalid: @@ -6099,6 +6121,7 @@ class KeyPathExpr : public Expr { case Kind::OptionalChain: case Kind::OptionalWrap: case Kind::OptionalForce: + case Kind::UnresolvedApply: case Kind::UnresolvedMember: case Kind::Member: case Kind::Identity: @@ -6122,6 +6145,7 @@ class KeyPathExpr : public Expr { case Kind::OptionalChain: case Kind::OptionalWrap: case Kind::OptionalForce: + case Kind::UnresolvedApply: case Kind::Member: case Kind::Identity: case Kind::TupleElement: @@ -6140,6 +6164,7 @@ class KeyPathExpr : public Expr { case Kind::Invalid: case Kind::UnresolvedMember: case Kind::UnresolvedSubscript: + case Kind::UnresolvedApply: case Kind::OptionalChain: case Kind::OptionalWrap: case Kind::OptionalForce: @@ -6161,6 +6186,7 @@ class KeyPathExpr : public Expr { case Kind::Invalid: case Kind::UnresolvedMember: case Kind::UnresolvedSubscript: + case Kind::UnresolvedApply: case Kind::OptionalChain: case Kind::OptionalWrap: case Kind::OptionalForce: @@ -6181,6 +6207,7 @@ class KeyPathExpr : public Expr { case Kind::Invalid: case Kind::UnresolvedMember: case Kind::UnresolvedSubscript: + case Kind::UnresolvedApply: case Kind::OptionalChain: case Kind::OptionalWrap: case Kind::OptionalForce: @@ -6194,6 +6221,30 @@ class KeyPathExpr : public Expr { llvm_unreachable("unhandled kind"); } + FunctionRefInfo getFunctionRefInfo() const { + switch (getKind()) { + case Kind::UnresolvedMember: + assert(ComponentFuncRefKind && + "FunctionRefInfo should not be nullopt for UnresolvedMember"); + return *ComponentFuncRefKind; + + case Kind::Member: + case Kind::Subscript: + case Kind::Invalid: + case Kind::UnresolvedSubscript: + case Kind::OptionalChain: + case Kind::OptionalWrap: + case Kind::OptionalForce: + case Kind::Identity: + case Kind::TupleElement: + case Kind::DictionaryKey: + case Kind::CodeCompletion: + case Kind::UnresolvedApply: + llvm_unreachable("no function ref kind for this kind"); + } + llvm_unreachable("unhandled kind"); + } + Type getComponentType() const { return ComponentType; } diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 6b4a1a2e3f046..87b5bfef6da26 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -4309,6 +4309,9 @@ class PrintExpr : public ExprVisitor, case KeyPathExpr::Component::Kind::CodeCompletion: printHead("completion", ASTNodeColor, label); break; + case KeyPathExpr::Component::Kind::UnresolvedApply: + printHead("unresolved_apply", ASTNodeColor, label); + break; } printTypeField(GetTypeOfKeyPathComponent(E, i), Label::always("type")); if (auto *args = component.getArgs()) { diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 4536b88aeb6d6..ff0ffce588d58 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -5118,6 +5118,11 @@ void PrintAST::printKeyPathComponents(KeyPathExpr *expr, ArrayRefsize() == indexHashables.size() || indexHashables.empty()); HashableConformancesData = diff --git a/lib/IDE/SourceEntityWalker.cpp b/lib/IDE/SourceEntityWalker.cpp index cc14b930f32fc..2491ae3ed5bcc 100644 --- a/lib/IDE/SourceEntityWalker.cpp +++ b/lib/IDE/SourceEntityWalker.cpp @@ -485,6 +485,7 @@ ASTWalker::PreWalkResult SemaAnnotator::walkToExprPre(Expr *E) { case KeyPathExpr::Component::Kind::Invalid: case KeyPathExpr::Component::Kind::UnresolvedMember: case KeyPathExpr::Component::Kind::UnresolvedSubscript: + case KeyPathExpr::Component::Kind::UnresolvedApply: case KeyPathExpr::Component::Kind::OptionalChain: case KeyPathExpr::Component::Kind::OptionalWrap: case KeyPathExpr::Component::Kind::OptionalForce: diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index cae4b07d7fc59..3bdf7757f0d11 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -851,7 +851,7 @@ ParserResult Parser::parseExprKeyPathObjC() { // Record the name we parsed. auto component = KeyPathExpr::Component::forUnresolvedMember( - name, nameLoc.getBaseNameLoc()); + name, FunctionRefInfo::unappliedBaseName(), nameLoc.getBaseNameLoc()); components.push_back(component); // After the first component, we can start parsing keywords. diff --git a/lib/Sema/PreCheckTarget.cpp b/lib/Sema/PreCheckTarget.cpp index 30fba1a63ef8e..83ea1712fcae1 100644 --- a/lib/Sema/PreCheckTarget.cpp +++ b/lib/Sema/PreCheckTarget.cpp @@ -2454,9 +2454,9 @@ void PreCheckTarget::resolveKeyPathExpr(KeyPathExpr *KPE) { SE->getSelfLoc())); expr = SE->getSubExpr(); } else if (auto UDE = dyn_cast(expr)) { - // .foo + // .foo, .foo() or .foo(val value: Int) components.push_back(KeyPathExpr::Component::forUnresolvedMember( - UDE->getName(), UDE->getLoc())); + UDE->getName(), UDE->getFunctionRefInfo(), UDE->getLoc())); expr = UDE->getBase(); } else if (auto CCE = dyn_cast(expr)) { @@ -2492,6 +2492,11 @@ void PreCheckTarget::resolveKeyPathExpr(KeyPathExpr *KPE) { (void)outermostExpr; assert(OEE == outermostExpr); expr = OEE->getSubExpr(); + } else if (auto AE = dyn_cast(expr)) { + // foo(), foo(val value: Int) or unapplied foo + components.push_back(KeyPathExpr::Component::forUnresolvedApply( + getASTContext(), AE->getArgs())); + expr = AE->getFn(); } else { if (emitErrors) { // \() may be an attempt to write a string interpolation outside From 978a5215d5ad2c67eba3446a7c5946a7b81181ed Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Thu, 5 Sep 2024 12:33:32 -0700 Subject: [PATCH 06/29] [Expr/AST] Add apply component to handle resolved method arguments. --- include/swift/AST/Expr.h | 22 +++++++++++++++++++--- lib/AST/ASTDumper.cpp | 3 +++ lib/AST/ASTPrinter.cpp | 3 ++- lib/AST/ASTWalker.cpp | 1 + lib/AST/Expr.cpp | 2 +- lib/IDE/SourceEntityWalker.cpp | 1 + 6 files changed, 27 insertions(+), 5 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 75fac411e9cc6..34c0745b3e8b1 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -5892,6 +5892,7 @@ class KeyPathExpr : public Expr { UnresolvedApply, UnresolvedMember, UnresolvedSubscript, + Apply, Member, Subscript, OptionalForce, @@ -5922,8 +5923,8 @@ class KeyPathExpr : public Expr { Type ComponentType; SourceLoc Loc; - // Private constructor for unresolvedSubscript, subscript and - // unresolvedApply component. + // Private constructor for unresolvedSubscript, subscript, + // unresolvedApply and apply component. explicit Component(DeclNameOrRef decl, ArgumentList *argList, ArrayRef indexHashables, Kind kind, Type type, SourceLoc loc); @@ -5986,7 +5987,14 @@ class KeyPathExpr : public Expr { static Component forUnresolvedOptionalChain(SourceLoc QuestionLoc) { return Component(Kind::OptionalChain, Type(), QuestionLoc); } - + + /// Create a component for resolved application. + static Component forApply(ArgumentList *argList, Type applyType, + ArrayRef indexHashables) { + return Component({}, argList, indexHashables, Kind::Apply, applyType, + argList->getLParenLoc()); + }; + /// Create a component for a property. static Component forMember(ConcreteDeclRef property, Type propertyType, SourceLoc loc) { @@ -6067,6 +6075,7 @@ class KeyPathExpr : public Expr { case Kind::OptionalWrap: case Kind::OptionalForce: case Kind::Member: + case Kind::Apply: case Kind::Identity: case Kind::TupleElement: case Kind::DictionaryKey: @@ -6087,6 +6096,7 @@ class KeyPathExpr : public Expr { case Kind::Subscript: case Kind::UnresolvedSubscript: case Kind::UnresolvedApply: + case Kind::Apply: return ArgList; case Kind::Invalid: @@ -6112,6 +6122,7 @@ class KeyPathExpr : public Expr { ArrayRef getIndexHashableConformances() const { switch (getKind()) { case Kind::Subscript: + case Kind::Apply: if (!HashableConformancesData) return {}; return {HashableConformancesData, ArgList->size()}; @@ -6146,6 +6157,7 @@ class KeyPathExpr : public Expr { case Kind::OptionalWrap: case Kind::OptionalForce: case Kind::UnresolvedApply: + case Kind::Apply: case Kind::Member: case Kind::Identity: case Kind::TupleElement: @@ -6165,6 +6177,7 @@ class KeyPathExpr : public Expr { case Kind::UnresolvedMember: case Kind::UnresolvedSubscript: case Kind::UnresolvedApply: + case Kind::Apply: case Kind::OptionalChain: case Kind::OptionalWrap: case Kind::OptionalForce: @@ -6187,6 +6200,7 @@ class KeyPathExpr : public Expr { case Kind::UnresolvedMember: case Kind::UnresolvedSubscript: case Kind::UnresolvedApply: + case Kind::Apply: case Kind::OptionalChain: case Kind::OptionalWrap: case Kind::OptionalForce: @@ -6208,6 +6222,7 @@ class KeyPathExpr : public Expr { case Kind::UnresolvedMember: case Kind::UnresolvedSubscript: case Kind::UnresolvedApply: + case Kind::Apply: case Kind::OptionalChain: case Kind::OptionalWrap: case Kind::OptionalForce: @@ -6240,6 +6255,7 @@ class KeyPathExpr : public Expr { case Kind::DictionaryKey: case Kind::CodeCompletion: case Kind::UnresolvedApply: + case Kind::Apply: llvm_unreachable("no function ref kind for this kind"); } llvm_unreachable("unhandled kind"); diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 87b5bfef6da26..342bdac713355 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -4312,6 +4312,9 @@ class PrintExpr : public ExprVisitor, case KeyPathExpr::Component::Kind::UnresolvedApply: printHead("unresolved_apply", ASTNodeColor, label); break; + case KeyPathExpr::Component::Kind::Apply: + printHead("apply", ASTNodeColor, label); + break; } printTypeField(GetTypeOfKeyPathComponent(E, i), Label::always("type")); if (auto *args = component.getArgs()) { diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index ff0ffce588d58..0064885da2c24 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -5118,7 +5118,8 @@ void PrintAST::printKeyPathComponents(KeyPathExpr *expr, ArrayRefsize() == indexHashables.size() || indexHashables.empty()); HashableConformancesData = diff --git a/lib/IDE/SourceEntityWalker.cpp b/lib/IDE/SourceEntityWalker.cpp index 2491ae3ed5bcc..d4187fcad454d 100644 --- a/lib/IDE/SourceEntityWalker.cpp +++ b/lib/IDE/SourceEntityWalker.cpp @@ -486,6 +486,7 @@ ASTWalker::PreWalkResult SemaAnnotator::walkToExprPre(Expr *E) { case KeyPathExpr::Component::Kind::UnresolvedMember: case KeyPathExpr::Component::Kind::UnresolvedSubscript: case KeyPathExpr::Component::Kind::UnresolvedApply: + case KeyPathExpr::Component::Kind::Apply: case KeyPathExpr::Component::Kind::OptionalChain: case KeyPathExpr::Component::Kind::OptionalWrap: case KeyPathExpr::Component::Kind::OptionalForce: From 6af6374e3968dbae2ec7fbb7422935b9c0608e48 Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Wed, 4 Sep 2024 19:44:01 -0700 Subject: [PATCH 07/29] [CSGen] Generate constraints for keypaths to methods. --- lib/Sema/CSGen.cpp | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 8f0d4224198f3..1e6215d3e0c55 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1173,6 +1173,39 @@ namespace { return outputTy; } + /// Add constraints for argument resolution for `UnresolvedApply` key path + /// component kind. + Type addApplyConstraints( + Expr *anchor, Type memberTy, ArgumentList *argList, + ConstraintLocator *applyComponentLoc, + SmallVectorImpl *addedTypeVars = nullptr) { + // Locators used in this expression. + assert(applyComponentLoc != nullptr && "applyComponentLoc should not be null"); + + auto fnLocator = CS.getConstraintLocator( + applyComponentLoc, ConstraintLocator::ApplyFunction); + + auto fnResultLocator = CS.getConstraintLocator( + applyComponentLoc, ConstraintLocator::FunctionResult); + + CS.associateArgumentList(applyComponentLoc, argList); + + Type outputTy = CS.createTypeVariable( + fnResultLocator, TVO_CanBindToLValue | TVO_CanBindToNoEscape); + if (addedTypeVars) + addedTypeVars->push_back(outputTy->castTo()); + + SmallVector params; + getMatchingParams(argList, params); + + // Add the constraint that the index expression's type be convertible + // to the input type of the subscript operator. + CS.addApplicationConstraint(FunctionType::get(params, outputTy), memberTy, + /*trailingClosureMatching=*/std::nullopt, + CurDC, fnLocator); + return outputTy; + } + Type openPackElement(Type packType, ConstraintLocator *locator, PackExpansionExpr *packElementEnvironment) { if (!packElementEnvironment) { @@ -3781,8 +3814,9 @@ namespace { // needed : component.getDeclRef().getDecl()->createNameRef(); + auto refKind = component.getFunctionRefInfo(); CS.addValueMemberConstraint(base, lookupName, memberTy, CurDC, - FunctionRefInfo::unapplied(lookupName), + refKind, /*outerAlternatives=*/{}, memberLocator); base = memberTy; break; @@ -3815,6 +3849,13 @@ namespace { break; } + case KeyPathExpr::Component::Kind::UnresolvedApply: + case KeyPathExpr::Component::Kind::Apply: { + base = addApplyConstraints(E, base, component.getArgs(), + memberLocator, &componentTypeVars); + break; + } + case KeyPathExpr::Component::Kind::TupleElement: { // Note: If implemented, the logic in `getCalleeLocator` will need // updating to return the correct callee locator for this. From c059d1521deb1e078db68e7a26796ea445150654 Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Thu, 5 Sep 2024 10:49:51 -0700 Subject: [PATCH 08/29] [ConstraintSystem] Evaluate capabilities for keypath type. --- lib/Sema/ConstraintSystem.cpp | 97 +++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 39 deletions(-) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 7b1e360b21de6..629ee69078d4b 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -724,7 +724,8 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator( } // If we have a locator that starts with a key path component element, we - // may have a callee given by a property or subscript component. + // may have a callee given by a property, subscript, initializer, or method + // component. if (auto componentElt = locator->getFirstElementAs()) { auto *kpExpr = castToExpr(anchor); @@ -744,6 +745,8 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator( case ComponentKind::TupleElement: llvm_unreachable("Not implemented by CSGen"); break; + case ComponentKind::UnresolvedApply: + case ComponentKind::Apply: case ComponentKind::Invalid: case ComponentKind::OptionalForce: case ComponentKind::OptionalChain: @@ -5061,6 +5064,36 @@ ConstraintSystem::inferKeyPathLiteralCapability(KeyPathExpr *keyPath) { if (rootTy->isPlaceholder()) return fail(); + auto isKnownSendability = + [&](const KeyPathExpr::Component &component) -> bool { + if (!Context.LangOpts.hasFeature(Feature::InferSendableFromCaptures)) + return true; + + // Key path is sendable only when all of its captures are sendable. + if (auto *args = component.getArgs()) { + auto *sendable = Context.getProtocol(KnownProtocolKind::Sendable); + + for (const auto &arg : *args) { + // No need to check more or delay since we already know + // that the type is not Sendable. + if (!isSendable) + break; + + auto argTy = simplifyType(getType(arg.getExpr())); + + // Sendability cannot be determined until the argument + // is fully resolved. + if (argTy->hasTypeVariable()) + return false; + + auto conformance = lookupConformance(argTy, sendable); + isSendable &= bool(conformance) && !conformance.hasMissingConformance(); + } + } + + return true; + }; + auto mutability = KeyPathMutability::Writable; for (unsigned i : indices(keyPath->getComponents())) { auto &component = keyPath->getComponents()[i]; @@ -5074,33 +5107,16 @@ ConstraintSystem::inferKeyPathLiteralCapability(KeyPathExpr *keyPath) { return fail(); } + case KeyPathExpr::Component::Kind::Apply: + case KeyPathExpr::Component::Kind::UnresolvedApply: { + if (!isKnownSendability(component)) + return delay(); + break; + } case KeyPathExpr::Component::Kind::UnresolvedSubscript: case KeyPathExpr::Component::Kind::Subscript: { - if (Context.LangOpts.hasFeature(Feature::InferSendableFromCaptures)) { - // Key path is sendable only when all of its captures are sendable. - if (auto *args = component.getArgs()) { - auto *sendable = Context.getProtocol(KnownProtocolKind::Sendable); - - for (const auto &arg : *args) { - // No need to check more or delay since we already known - // that the type is not Sendable. - if (!isSendable) - break; - - auto argTy = simplifyType(getType(arg.getExpr())); - - // Sendability cannot be determined until the argument - // is fully resolved. - if (argTy->hasTypeVariable()) - return delay(); - - auto conformance = lookupConformance(argTy, sendable); - isSendable &= - bool(conformance) && - !conformance.hasMissingConformance(); - } - } - } + if (!isKnownSendability(component)) + return delay(); LLVM_FALLTHROUGH; } case KeyPathExpr::Component::Kind::Member: @@ -5128,18 +5144,19 @@ ConstraintSystem::inferKeyPathLiteralCapability(KeyPathExpr *keyPath) { if (!choice.isDecl()) return fail(); - auto storage = dyn_cast(choice.getDecl()); - if (hasFixFor(componentLoc, FixKind::AllowInvalidRefInKeyPath) || hasFixFor(componentLoc, FixKind::UnwrapOptionalBase) || hasFixFor(componentLoc, FixKind::UnwrapOptionalBaseWithOptionalResult)) return fail(); - if (!storage) + auto *storageDecl = dyn_cast(choice.getDecl()); + if (!isa(choice.getDecl()) && !storageDecl) { return fail(); + } - switch (getActorIsolation(storage)) { + // Shared switch logic for actor isolation. + switch (getActorIsolation(choice.getDecl())) { case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: case ActorIsolation::CallerIsolationInheriting: @@ -5149,22 +5166,24 @@ ConstraintSystem::inferKeyPathLiteralCapability(KeyPathExpr *keyPath) { case ActorIsolation::Erased: llvm_unreachable("storage cannot have opaque isolation"); - // A reference to an actor isolated state make key path non-Sendable. + // A reference to an actor isolated state makes key path non-Sendable. case ActorIsolation::ActorInstance: case ActorIsolation::GlobalActor: isSendable = false; break; } - if (isReadOnlyKeyPathComponent(storage, component.getLoc())) { - mutability = KeyPathMutability::ReadOnly; - continue; - } + if (storageDecl) { + if (isReadOnlyKeyPathComponent(storageDecl, component.getLoc())) { + mutability = KeyPathMutability::ReadOnly; + continue; + } - // A nonmutating setter indicates a reference-writable base. - if (!storage->isSetterMutating()) { - mutability = KeyPathMutability::ReferenceWritable; - continue; + // A nonmutating setter indicates a reference-writable base. + if (!storageDecl->isSetterMutating()) { + mutability = KeyPathMutability::ReferenceWritable; + continue; + } } // Otherwise, the key path maintains its current capability. From 724e3f9910f2931d37bfe117a7618abcff7db36a Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Mon, 3 Mar 2025 22:55:13 -0800 Subject: [PATCH 09/29] [Constraint System] Evaluable hashability during constraint generation. --- include/swift/Sema/ConstraintSystem.h | 13 ++++--- lib/Sema/CSGen.cpp | 14 ++++++- lib/Sema/TypeOfReference.cpp | 53 ++++++++++++--------------- 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 493ac41624621..818fb193aa318 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -4459,11 +4459,8 @@ class ConstraintSystem { private: /// Add the constraints needed to bind an overload's type variable. - void bindOverloadType( - const SelectedOverload &overload, Type boundType, - ConstraintLocator *locator, DeclContext *useDC, - llvm::function_ref - verifyThatArgumentIsHashable); + void bindOverloadType(const SelectedOverload &overload, Type boundType, + ConstraintLocator *locator, DeclContext *useDC); /// Describes a direction of optional wrapping, either increasing optionality /// or decreasing optionality. @@ -4940,6 +4937,12 @@ class ConstraintSystem { Expr *buildTypeErasedExpr(Expr *expr, DeclContext *dc, Type contextualType, ContextualTypePurpose purpose); + /// Ensures that the given argument type conforms to the `Hashable` protocol + /// and adds a conformance constraint if it does not. This is required for + /// arguments used as key path components, as they serve as lookup keys. + void verifyThatArgumentIsHashable(unsigned index, Type argType, + ConstraintLocator *locator, SourceLoc loc); + private: /// Determines whether or not a given conversion at a given locator requires /// the creation of a temporary value that's only valid for a limited scope. diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 1e6215d3e0c55..14b6987822985 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1177,6 +1177,7 @@ namespace { /// component kind. Type addApplyConstraints( Expr *anchor, Type memberTy, ArgumentList *argList, + ConstraintLocator *memberComponentLoc, ConstraintLocator *applyComponentLoc, SmallVectorImpl *addedTypeVars = nullptr) { // Locators used in this expression. @@ -1198,6 +1199,14 @@ namespace { SmallVector params; getMatchingParams(argList, params); + SourceLoc loc = + CurDC->getAsDecl() ? CurDC->getAsDecl()->getLoc() : SourceLoc(); + for (auto index : indices(params)) { + const auto ¶m = params[index]; + CS.verifyThatArgumentIsHashable(index, param.getParameterType(), + memberComponentLoc, loc); + } + // Add the constraint that the index expression's type be convertible // to the input type of the subscript operator. CS.addApplicationConstraint(FunctionType::get(params, outputTy), memberTy, @@ -3851,8 +3860,11 @@ namespace { case KeyPathExpr::Component::Kind::UnresolvedApply: case KeyPathExpr::Component::Kind::Apply: { + auto prevMemberLocator = CS.getConstraintLocator( + locator, LocatorPathElt::KeyPathComponent(i - 1)); base = addApplyConstraints(E, base, component.getArgs(), - memberLocator, &componentTypeVars); + prevMemberLocator, memberLocator, + &componentTypeVars); break; } diff --git a/lib/Sema/TypeOfReference.cpp b/lib/Sema/TypeOfReference.cpp index 1a0bd822211c4..81a36228fdd72 100644 --- a/lib/Sema/TypeOfReference.cpp +++ b/lib/Sema/TypeOfReference.cpp @@ -1900,13 +1900,10 @@ Type ConstraintSystem::getEffectiveOverloadType(ConstraintLocator *locator, return type; } - - -void ConstraintSystem::bindOverloadType( - const SelectedOverload &overload, Type boundType, - ConstraintLocator *locator, DeclContext *useDC, - llvm::function_ref - verifyThatArgumentIsHashable) { +void ConstraintSystem::bindOverloadType(const SelectedOverload &overload, + Type boundType, + ConstraintLocator *locator, + DeclContext *useDC) { auto &ctx = getASTContext(); auto choice = overload.choice; auto openedType = overload.adjustedOpenedType; @@ -1952,7 +1949,8 @@ void ConstraintSystem::bindOverloadType( if (isExpr(locator->getAnchor())) { auto paramTy = fnTy->getParams()[0].getParameterType(); - verifyThatArgumentIsHashable(/*idx*/ 0, paramTy, locator); + verifyThatArgumentIsHashable(/*idx*/ 0, paramTy, locator, + choice.getDecl()->getLoc()); } }; switch (choice.getKind()) { @@ -2134,8 +2132,6 @@ void ConstraintSystem::bindOverloadType( llvm_unreachable("Unhandled OverloadChoiceKind in switch."); } - - static unsigned getApplicationLevel(ConstraintSystem &CS, Type baseTy, UnresolvedDotExpr *UDE) { unsigned level = 0; @@ -2346,24 +2342,8 @@ void ConstraintSystem::recordResolvedOverload(ConstraintLocator *locator, } void ConstraintSystem::resolveOverload(ConstraintLocator *locator, - Type boundType, - OverloadChoice choice, + Type boundType, OverloadChoice choice, DeclContext *useDC) { - // Add a conformance constraint to make sure that given type conforms - // to Hashable protocol, which is important for key path subscript - // components. - auto verifyThatArgumentIsHashable = [&](unsigned index, Type argType, - ConstraintLocator *locator) { - if (auto *hashable = TypeChecker::getProtocol( - argType->getASTContext(), choice.getDecl()->getLoc(), - KnownProtocolKind::Hashable)) { - addConstraint(ConstraintKind::ConformsTo, argType, - hashable->getDeclaredInterfaceType(), - getConstraintLocator( - locator, LocatorPathElt::TupleElement(index))); - } - }; - // Determine the type to which we'll bind the overload set's type. Type openedType; Type adjustedOpenedType; @@ -2514,7 +2494,8 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, // Hashable, because it would be used as a component inside key path. for (auto index : indices(subscriptTy->getParams())) { const auto ¶m = subscriptTy->getParams()[index]; - verifyThatArgumentIsHashable(index, param.getParameterType(), locator); + verifyThatArgumentIsHashable(index, param.getParameterType(), locator, + choice.getDecl()->getLoc()); } } } @@ -2603,8 +2584,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, recordResolvedOverload(locator, overload); // Add the constraints necessary to bind the overload type. - bindOverloadType(overload, boundType, locator, useDC, - verifyThatArgumentIsHashable); + bindOverloadType(overload, boundType, locator, useDC); if (isDebugMode()) { PrintOptions PO; @@ -2682,3 +2662,16 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, increaseScore(SK_UnresolvedMemberViaOptional, locator); } } + +void ConstraintSystem::verifyThatArgumentIsHashable(unsigned index, + Type argType, + ConstraintLocator *locator, + SourceLoc loc) { + if (auto *hashable = TypeChecker::getProtocol(argType->getASTContext(), loc, + KnownProtocolKind::Hashable)) { + addConstraint( + ConstraintKind::ConformsTo, argType, + hashable->getDeclaredInterfaceType(), + getConstraintLocator(locator, LocatorPathElt::TupleElement(index))); + } +} From 0e895c6a2bc5eedda4d36328b6fd3717626cff67 Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Fri, 10 Jan 2025 13:16:35 -0800 Subject: [PATCH 10/29] [CSApply] Solve keypath methods and initializers. --- lib/Sema/CSApply.cpp | 106 +++++++++++++++++++++++------ lib/Sema/CSSimplify.cpp | 3 - lib/Sema/ConstraintSystem.cpp | 1 + lib/Sema/TypeCheckAvailability.cpp | 2 + lib/Sema/TypeCheckEffects.cpp | 6 +- lib/Sema/TypeCheckExprObjC.cpp | 2 + 6 files changed, 95 insertions(+), 25 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 28db28b127bdf..3546992346919 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -335,21 +335,24 @@ static bool buildObjCKeyPathString(KeyPathExpr *E, case KeyPathExpr::Component::Kind::Member: { // Property references must be to @objc properties. - // TODO: If we added special properties matching KVC operators like '@sum', - // '@count', etc. those could be mapped too. - auto property = cast(component.getDeclRef().getDecl()); - if (!property->isObjC()) - return false; - if (!buf.empty()) { - buf.push_back('.'); + // TODO: If we added special properties matching KVC operators like + // '@sum', '@count', etc. those could be mapped too. + if (auto property = dyn_cast(component.getDeclRef().getDecl())) { + if (!property->isObjC()) + return false; + if (!buf.empty()) { + buf.push_back('.'); + } + auto objcName = property->getObjCPropertyName().str(); + buf.append(objcName.begin(), objcName.end()); } - auto objcName = property->getObjCPropertyName().str(); - buf.append(objcName.begin(), objcName.end()); - continue; + continue; } + + case KeyPathExpr::Component::Kind::Apply: case KeyPathExpr::Component::Kind::TupleElement: case KeyPathExpr::Component::Kind::Subscript: - // Subscripts and tuples aren't generally represented in KVC. + // Subscripts, methods and tuples aren't generally represented in KVC. // TODO: There are some subscript forms we could map to KVC, such as // when indexing a Dictionary or NSDictionary by string, or when applying // a mapping subscript operation to Array/Set or NSArray/NSSet. @@ -357,6 +360,7 @@ static bool buildObjCKeyPathString(KeyPathExpr *E, case KeyPathExpr::Component::Kind::Invalid: case KeyPathExpr::Component::Kind::UnresolvedMember: case KeyPathExpr::Component::Kind::UnresolvedSubscript: + case KeyPathExpr::Component::Kind::UnresolvedApply: case KeyPathExpr::Component::Kind::CodeCompletion: // Don't bother building the key path string if the key path didn't even // resolve. @@ -5161,6 +5165,20 @@ namespace { origComponent.getArgs(), componentLocator, resolvedComponents); break; } + case KeyPathExpr::Component::Kind::UnresolvedApply: { + // Get the calleeLoc of the member that requires application + // resolution of its arguments. Keypath methods are a member component + // followed by an unapplied component, so use the member component to + // find the overload. + auto memberComponentLoc = cs.getConstraintLocator( + E, LocatorPathElt::KeyPathComponent(i - 1)); + + buildKeyPathApplyComponent( + solution.getOverloadChoice(memberComponentLoc), + origComponent.getLoc(), origComponent.getArgs(), componentLocator, + memberComponentLoc, resolvedComponents); + break; + } case KeyPathExpr::Component::Kind::OptionalChain: { didOptionalChain = true; // Chaining always forces the element to be an rvalue. @@ -5206,6 +5224,7 @@ namespace { } case KeyPathExpr::Component::Kind::Member: case KeyPathExpr::Component::Kind::Subscript: + case KeyPathExpr::Component::Kind::Apply: case KeyPathExpr::Component::Kind::OptionalWrap: case KeyPathExpr::Component::Kind::TupleElement: llvm_unreachable("already resolved"); @@ -5214,9 +5233,6 @@ namespace { llvm_unreachable("DictionaryKey only valid in #keyPath"); break; } - - // Update "componentTy" with the result type of the last component. - assert(!resolvedComponents.empty()); componentTy = resolvedComponents.back().getComponentType(); } @@ -5373,14 +5389,14 @@ namespace { ConstraintLocator *locator, SmallVectorImpl &components) { auto resolvedTy = simplifyType(overload.adjustedOpenedType); - if (auto *property = overload.choice.getDeclOrNull()) { - // Key paths can only refer to properties currently. - auto varDecl = cast(property); - // Key paths don't work with mutating-get properties. - assert(!varDecl->isGetterMutating()); + if (auto *member = overload.choice.getDeclOrNull()) { + if (auto varDecl = dyn_cast(member)) { + // Key paths don't work with mutating-get properties. + assert(!varDecl->isGetterMutating()); + } // Compute the concrete reference to the member. - auto ref = resolveConcreteDeclRef(property, locator); + auto ref = resolveConcreteDeclRef(member, locator); components.push_back( KeyPathExpr::Component::forMember(ref, resolvedTy, componentLoc)); } else { @@ -5470,6 +5486,56 @@ namespace { buildKeyPathOptionalForceComponent(components); } + void buildKeyPathApplyComponent( + const SelectedOverload &overload, SourceLoc componentLoc, + ArgumentList *args, ConstraintLocator *applyLocator, + ConstraintLocator *memberLocator, + SmallVectorImpl &components) { + auto &ctx = cs.getASTContext(); + auto fnType = overload.adjustedOpenedType->castTo(); + + // Compute substitutions to refer to the member. + auto ref = + resolveConcreteDeclRef(overload.choice.getDecl(), memberLocator); + + // Coerce the args to the type the method/initializer expects. + args = coerceCallArguments( + args, fnType, ref, /*applyExpr*/ nullptr, + cs.getConstraintLocator(applyLocator, + ConstraintLocator::ApplyArgument), + /*appliedPropertyWrappers*/ {}); + + // We need to be able to hash the captured args in order for KeyPath + // itself to be hashable, so check that all of the arg components are + // hashable and collect their conformances here. + SmallVector conformances; + + auto hashable = ctx.getProtocol(KnownProtocolKind::Hashable); + + SmallVector newLabels; + for (auto ¶m : fnType->getParams()) { + newLabels.push_back(param.getLabel()); + + auto indexType = simplifyType(param.getParameterType()); + // Arg type conformance to Hashable protocol has been + // verified by the solver, we just need to get it again + // with all of the generic parameters resolved. + auto hashableConformance = checkConformance(indexType, hashable); + assert(hashableConformance); + + conformances.push_back(hashableConformance); + } + + auto comp = KeyPathExpr::Component::forApply( + args, fnType->getResult(), ctx.AllocateCopy(conformances)); + components.push_back(comp); + + auto unwrapCount = + getIUOForceUnwrapCount(applyLocator, IUOReferenceKind::ReturnValue); + for (unsigned i = 0; i < unwrapCount; ++i) + buildKeyPathOptionalForceComponent(components); + } + Expr *visitCurrentContextIsolationExpr(CurrentContextIsolationExpr *E) { E->setType(simplifyType(cs.getType(E))); return E; diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 770ac128af91f..8991ae66d6169 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -10825,9 +10825,6 @@ static ConstraintFix *validateInitializerRef(ConstraintSystem &cs, // which means MetatypeType has to be added after finding a type variable. if (baseLocator->isLastElement()) baseType = MetatypeType::get(baseType); - } else if (isExpr(anchor)) { - // Key path can't refer to initializers e.g. `\Type.init` - return AllowInvalidRefInKeyPath::forRef(cs, baseType, init, locator); } if (!baseType) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 629ee69078d4b..9dbc34350dc99 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -747,6 +747,7 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator( break; case ComponentKind::UnresolvedApply: case ComponentKind::Apply: + return getConstraintLocator(anchor, *componentElt); case ComponentKind::Invalid: case ComponentKind::OptionalForce: case ComponentKind::OptionalChain: diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 125cb460c3ab5..73368bb8ef871 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -2541,6 +2541,8 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker { case KeyPathExpr::Component::Kind::Invalid: case KeyPathExpr::Component::Kind::UnresolvedMember: case KeyPathExpr::Component::Kind::UnresolvedSubscript: + case KeyPathExpr::Component::Kind::UnresolvedApply: + case KeyPathExpr::Component::Kind::Apply: case KeyPathExpr::Component::Kind::OptionalChain: case KeyPathExpr::Component::Kind::OptionalWrap: case KeyPathExpr::Component::Kind::OptionalForce: diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index 595523f9d748a..6aff0dc9b0a32 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -621,7 +621,7 @@ class EffectsHandlingWalker : public ASTWalker { } else if (auto KPE = dyn_cast(E)) { for (auto &component : KPE->getComponents()) { switch (component.getKind()) { - case KeyPathExpr::Component::Kind::Property: + case KeyPathExpr::Component::Kind::Member: case KeyPathExpr::Component::Kind::Subscript: { (void)asImpl().checkDeclRef(KPE, component.getDeclRef(), component.getLoc(), @@ -633,8 +633,10 @@ class EffectsHandlingWalker : public ASTWalker { case KeyPathExpr::Component::Kind::TupleElement: case KeyPathExpr::Component::Kind::Invalid: - case KeyPathExpr::Component::Kind::UnresolvedProperty: + case KeyPathExpr::Component::Kind::UnresolvedMember: case KeyPathExpr::Component::Kind::UnresolvedSubscript: + case KeyPathExpr::Component::Kind::UnresolvedApply: + case KeyPathExpr::Component::Kind::Apply: case KeyPathExpr::Component::Kind::OptionalChain: case KeyPathExpr::Component::Kind::OptionalWrap: case KeyPathExpr::Component::Kind::OptionalForce: diff --git a/lib/Sema/TypeCheckExprObjC.cpp b/lib/Sema/TypeCheckExprObjC.cpp index 3dac9c333baf9..0373ad9259601 100644 --- a/lib/Sema/TypeCheckExprObjC.cpp +++ b/lib/Sema/TypeCheckExprObjC.cpp @@ -210,6 +210,7 @@ std::optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, continue; case KeyPathExpr::Component::Kind::UnresolvedMember: + case KeyPathExpr::Component::Kind::UnresolvedApply: break; case KeyPathExpr::Component::Kind::UnresolvedSubscript: case KeyPathExpr::Component::Kind::OptionalChain: @@ -222,6 +223,7 @@ std::optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, case KeyPathExpr::Component::Kind::OptionalWrap: case KeyPathExpr::Component::Kind::Member: case KeyPathExpr::Component::Kind::Subscript: + case KeyPathExpr::Component::Kind::Apply: case KeyPathExpr::Component::Kind::DictionaryKey: llvm_unreachable("already resolved!"); } From 7ff563eb0c9f51cbd8628714fc26d5f27cffd73f Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Thu, 5 Sep 2024 18:24:47 -0700 Subject: [PATCH 11/29] [CSDiagnostics] Remove checks preventing method/initializer keypaths. --- include/swift/Sema/CSFix.h | 7 ------- lib/Sema/CSDiagnostics.h | 6 ++---- lib/Sema/CSFix.cpp | 12 ------------ 3 files changed, 2 insertions(+), 23 deletions(-) diff --git a/include/swift/Sema/CSFix.h b/include/swift/Sema/CSFix.h index 5f85975ed65ee..acc3d31c9fee0 100644 --- a/include/swift/Sema/CSFix.h +++ b/include/swift/Sema/CSFix.h @@ -2010,9 +2010,6 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix { MutatingGetter, // Allow a reference to a method (instance or static) as // a key path component. - Method, - // Allow a reference to a initializer instance as a key path - // component. Initializer, // Allow a reference to an enum case as a key path component. EnumCase, @@ -2037,10 +2034,6 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix { case RefKind::MutatingGetter: return "allow reference to a member with mutating getter as a key " "path component"; - case RefKind::Method: - return "allow reference to a method as a key path component"; - case RefKind::Initializer: - return "allow reference to an init method as a key path component"; case RefKind::EnumCase: return "allow reference to an enum case as a key path component"; } diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index d70a79daa821f..7483c68cfc56c 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1834,17 +1834,15 @@ class InvalidMemberWithMutatingGetterInKeyPath final bool diagnoseAsError() override; }; -/// Diagnose an attempt to reference a method or initializer as a key path component -/// e.g. +/// Diagnose an attempt to reference a method or initializer as a key path +/// component e.g. /// /// ```swift /// struct S { /// init() { } -/// func foo() -> Int { return 42 } /// static func bar() -> Int { return 0 } /// } /// -/// _ = \S.foo /// _ = \S.Type.bar /// _ = \S.init /// ``` diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index d5f285128286d..ae5ea1611e40c 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -1252,7 +1252,6 @@ bool AllowInvalidRefInKeyPath::diagnose(const Solution &solution, return failure.diagnose(asNote); } - case RefKind::Method: case RefKind::Initializer: { InvalidMethodRefInKeyPath failure(solution, Member, getLocator()); return failure.diagnose(asNote); @@ -1306,23 +1305,12 @@ AllowInvalidRefInKeyPath::forRef(ConstraintSystem &cs, Type baseType, cs, baseType, RefKind::StaticMember, member, locator); } - // Referencing (instance or static) methods in key path is - // not currently allowed. - if (isa(member)) - return AllowInvalidRefInKeyPath::create(cs, baseType, RefKind::Method, - member, locator); - // Referencing enum cases in key path is not currently allowed. if (isa(member)) { return AllowInvalidRefInKeyPath::create(cs, baseType, RefKind::EnumCase, member, locator); } - // Referencing initializers in key path is not currently allowed. - if (isa(member)) - return AllowInvalidRefInKeyPath::create(cs, baseType, RefKind::Initializer, - member, locator); - if (auto *storage = dyn_cast(member)) { // Referencing members with mutating getters in key path is not // currently allowed. From 970159c09da8aa1ca4f8eca610e24a12e233afd4 Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Thu, 9 Jan 2025 19:02:25 -0800 Subject: [PATCH 12/29] [CSDiagnostics] Block async, throws and mutating methods. --- include/swift/AST/DiagnosticsSema.def | 7 ++- include/swift/Sema/CSFix.h | 12 +++-- lib/Sema/CSDiagnostics.cpp | 10 +++- lib/Sema/CSDiagnostics.h | 78 +++++++++++++++++++-------- lib/Sema/CSFix.cpp | 26 +++++++-- lib/Sema/MiscDiagnostics.cpp | 3 +- 6 files changed, 102 insertions(+), 34 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 0b0878a7913d9..1c93955ef3f77 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -687,6 +687,9 @@ NOTE(keypath_static_member_access_from_unsupported_module_note,none, ERROR(expr_keypath_enum_case,none, "%select{key path|dynamic key path member lookup}1 cannot refer to enum case %0", (const ValueDecl *, bool)) +ERROR(expr_keypath_mutating_method,none, + "%select{key path|dynamic key path member lookup}1 cannot refer to mutating method %0", + (const ValueDecl *, bool)) ERROR(expr_keypath_empty,none, "empty key path does not refer to a property", ()) ERROR(expr_unsupported_objc_key_path_component,none, @@ -5594,8 +5597,8 @@ ERROR(actor_isolated_keypath_component,none, "cannot form key path to %0 %kind1", (ActorIsolation, const ValueDecl *)) ERROR(effectful_keypath_component,none, - "cannot form key path to %0 with 'throws' or 'async'", - (DescriptiveDeclKind)) + "cannot form %select{key path|dynamic key path member lookup}1 to %0 with 'throws' or 'async'", + (DescriptiveDeclKind, bool)) ERROR(local_function_executed_concurrently,none, "concurrently-executed %kind0 must be marked as '@Sendable'", (const ValueDecl *)) diff --git a/include/swift/Sema/CSFix.h b/include/swift/Sema/CSFix.h index acc3d31c9fee0..c95a28f50f1ed 100644 --- a/include/swift/Sema/CSFix.h +++ b/include/swift/Sema/CSFix.h @@ -2008,9 +2008,10 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix { // Allow a reference to a declaration with mutating getter as // a key path component. MutatingGetter, - // Allow a reference to a method (instance or static) as - // a key path component. - Initializer, + // Allow a reference to a mutating method. + MutatingMethod, + // Allow a reference to an async or throwing method. + AsyncOrThrowsMethod, // Allow a reference to an enum case as a key path component. EnumCase, } Kind; @@ -2036,6 +2037,11 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix { "path component"; case RefKind::EnumCase: return "allow reference to an enum case as a key path component"; + case RefKind::MutatingMethod: + return "allow reference to mutating method as a key path component"; + case RefKind::AsyncOrThrowsMethod: + return "allow reference to async or throwing method as a key path " + "component"; } llvm_unreachable("covered switch"); } diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 8dda76009c7aa..ae4a928e99a08 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -6415,8 +6415,14 @@ bool InvalidMemberWithMutatingGetterInKeyPath::diagnoseAsError() { return true; } -bool InvalidMethodRefInKeyPath::diagnoseAsError() { - emitDiagnostic(diag::expr_keypath_not_property, getMember(), +bool InvalidMutatingMethodRefInKeyPath::diagnoseAsError() { + emitDiagnostic(diag::expr_keypath_mutating_method, getMember(), + isForKeyPathDynamicMemberLookup()); + return true; +} + +bool InvalidAsyncOrThrowsMethodRefInKeyPath::diagnoseAsError() { + emitDiagnostic(diag::effectful_keypath_component, getKind(), isForKeyPathDynamicMemberLookup()); return true; } diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 7483c68cfc56c..d297570fc8968 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1806,6 +1806,61 @@ class InvalidEnumCaseRefInKeyPath final : public InvalidMemberRefInKeyPath { bool diagnoseAsError() override; }; +/// Diagnose an attempt to reference a mutating method as a key path component +/// e.g. +/// +/// ```swift +/// struct S { +/// var year = 2024 +/// +/// mutating func updateYear(to newYear: Int) { +/// self.year = newYear +/// } +/// +/// _ = \S.updateYear(to: 2025) +/// ``` +class InvalidMutatingMethodRefInKeyPath final + : public InvalidMemberRefInKeyPath { +public: + InvalidMutatingMethodRefInKeyPath(const Solution &solution, ValueDecl *member, + ConstraintLocator *locator) + : InvalidMemberRefInKeyPath(solution, member, locator) { + assert(isa(member)); + } + + bool diagnoseAsError() override; +}; + +/// Diagnose an attempt to reference an async or throwing method as a key path +/// component e.g. +/// +/// ```swift +/// struct S { +/// var year = 2024 +/// +/// func fetchAndValidate() async throws -> Int { +/// let fetchedYear = await fetchValue() +/// if fetchedYear < 0 { +/// throw ValidationError.invalidYear +/// } +/// return fetchedYear +/// } +/// +/// _ = \S.fetchAndValidate() +/// ``` +class InvalidAsyncOrThrowsMethodRefInKeyPath final + : public InvalidMemberRefInKeyPath { +public: + InvalidAsyncOrThrowsMethodRefInKeyPath(const Solution &solution, + ValueDecl *member, + ConstraintLocator *locator) + : InvalidMemberRefInKeyPath(solution, member, locator) { + assert(isa(member)); + } + + bool diagnoseAsError() override; +}; + /// Diagnose an attempt to reference a member which has a mutating getter as a /// key path component e.g. /// @@ -1834,29 +1889,6 @@ class InvalidMemberWithMutatingGetterInKeyPath final bool diagnoseAsError() override; }; -/// Diagnose an attempt to reference a method or initializer as a key path -/// component e.g. -/// -/// ```swift -/// struct S { -/// init() { } -/// static func bar() -> Int { return 0 } -/// } -/// -/// _ = \S.Type.bar -/// _ = \S.init -/// ``` -class InvalidMethodRefInKeyPath final : public InvalidMemberRefInKeyPath { -public: - InvalidMethodRefInKeyPath(const Solution &solution, ValueDecl *method, - ConstraintLocator *locator) - : InvalidMemberRefInKeyPath(solution, method, locator) { - assert(isa(method) || isa(method)); - } - - bool diagnoseAsError() override; -}; - /// Diagnose an attempt return something from a function which /// doesn't have a return type specified e.g. /// diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index ae5ea1611e40c..fe9ad22a08dcb 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -1251,9 +1251,13 @@ bool AllowInvalidRefInKeyPath::diagnose(const Solution &solution, getLocator()); return failure.diagnose(asNote); } - - case RefKind::Initializer: { - InvalidMethodRefInKeyPath failure(solution, Member, getLocator()); + case RefKind::MutatingMethod: { + InvalidMutatingMethodRefInKeyPath failure(solution, Member, getLocator()); + return failure.diagnose(asNote); + } + case RefKind::AsyncOrThrowsMethod: { + InvalidAsyncOrThrowsMethodRefInKeyPath failure(solution, Member, + getLocator()); return failure.diagnose(asNote); } } @@ -1319,6 +1323,22 @@ AllowInvalidRefInKeyPath::forRef(ConstraintSystem &cs, Type baseType, cs, baseType, RefKind::MutatingGetter, member, locator); } + // Referencing mutating, throws or async method members is not currently + // allowed. + if (auto method = dyn_cast(member)) { + if (method->isAsyncContext()) + return AllowInvalidRefInKeyPath::create( + cs, baseType, RefKind::AsyncOrThrowsMethod, member, locator); + if (auto methodType = method->getInterfaceType()->getAs()) { + if (methodType->getResult()->getAs()->isThrowing()) + return AllowInvalidRefInKeyPath::create( + cs, baseType, RefKind::AsyncOrThrowsMethod, member, locator); + } + if (method->isMutating()) + return AllowInvalidRefInKeyPath::create( + cs, baseType, RefKind::MutatingMethod, member, locator); + } + return nullptr; } diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index a6ff23c9bda73..ef8d48d04cbda 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -344,7 +344,8 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, if (asd->getEffectfulGetAccessor()) { Ctx.Diags.diagnose(component.getLoc(), diag::effectful_keypath_component, - asd->getDescriptiveKind()); + asd->getDescriptiveKind(), + /*dynamic member lookup*/ false); Ctx.Diags.diagnose(asd->getLoc(), diag::kind_declared_here, asd->getDescriptiveKind()); } From a96b780a285706b145c84e3e62701c29b376676c Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Thu, 16 Jan 2025 19:07:00 -0800 Subject: [PATCH 13/29] [Sema] Diagnose method arg captures that are not Hashable/Equatable. --- include/swift/AST/DiagnosticsSema.def | 4 ++-- include/swift/Sema/ConstraintLocator.h | 4 ++++ lib/Sema/CSDiagnostics.cpp | 7 +++++-- lib/Sema/CSDiagnostics.h | 5 +++-- lib/Sema/CSSimplify.cpp | 5 +++-- lib/Sema/ConstraintLocator.cpp | 19 +++++++++++++++++++ 6 files changed, 36 insertions(+), 8 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 1c93955ef3f77..01165ffd03dce 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -719,8 +719,8 @@ ERROR(expr_swift_keypath_empty, none, "key path must have at least one component", ()) ERROR(expr_string_interpolation_outside_string,none, "string interpolation can only appear inside a string literal", ()) -ERROR(expr_keypath_subscript_index_not_hashable, none, - "subscript index of type %0 in a key path must be Hashable", (Type)) +ERROR(expr_keypath_arg_or_index_not_hashable, none, + "%select{method argument|subscript index}0 of type %1 in a key path must be Hashable", (bool, Type)) ERROR(expr_smart_keypath_application_type_mismatch,none, "key path of type %0 cannot be applied to a base of type %1", (Type, Type)) diff --git a/include/swift/Sema/ConstraintLocator.h b/include/swift/Sema/ConstraintLocator.h index edec6a1f22344..9a6ea1dc05108 100644 --- a/include/swift/Sema/ConstraintLocator.h +++ b/include/swift/Sema/ConstraintLocator.h @@ -256,6 +256,10 @@ class ConstraintLocator : public llvm::FoldingSetNode { /// of the key path at some index. bool isKeyPathSubscriptComponent() const; + /// Determine whether this locator points to a member component + /// of the key path at some index. + bool isKeyPathMemberComponent() const; + /// Determine whether this locator points to the member found /// via key path dynamic member lookup. bool isForKeyPathDynamicMemberLookup() const; diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index ae4a928e99a08..db5a82770ccad 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -6347,7 +6347,8 @@ bool AnyObjectKeyPathRootFailure::diagnoseAsError() { SourceLoc KeyPathSubscriptIndexHashableFailure::getLoc() const { auto *locator = getLocator(); - if (locator->isKeyPathSubscriptComponent()) { + if (locator->isKeyPathSubscriptComponent() || + locator->isKeyPathMemberComponent()) { auto *KPE = castToExpr(getAnchor()); if (auto kpElt = locator->findFirst()) return KPE->getComponents()[kpElt->getIndex()].getLoc(); @@ -6357,7 +6358,9 @@ SourceLoc KeyPathSubscriptIndexHashableFailure::getLoc() const { } bool KeyPathSubscriptIndexHashableFailure::diagnoseAsError() { - emitDiagnostic(diag::expr_keypath_subscript_index_not_hashable, + auto *locator = getLocator(); + emitDiagnostic(diag::expr_keypath_arg_or_index_not_hashable, + !locator->isKeyPathMemberComponent(), resolveType(NonConformingType)); return true; } diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index d297570fc8968..1433525e5ce77 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1707,8 +1707,9 @@ class KeyPathSubscriptIndexHashableFailure final : public FailureDiagnostic { KeyPathSubscriptIndexHashableFailure(const Solution &solution, Type type, ConstraintLocator *locator) : FailureDiagnostic(solution, locator), NonConformingType(type) { - assert(locator->isResultOfKeyPathDynamicMemberLookup() || - locator->isKeyPathSubscriptComponent()); + assert((locator->isResultOfKeyPathDynamicMemberLookup() || + locator->isKeyPathSubscriptComponent()) || + locator->isKeyPathMemberComponent()); } SourceLoc getLoc() const override; diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 8991ae66d6169..51891f7752971 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -9073,8 +9073,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( // If this is an implicit Hashable conformance check generated for each // index argument of the keypath subscript component, we could just treat // it as though it conforms. - if (loc->isResultOfKeyPathDynamicMemberLookup() || - loc->isKeyPathSubscriptComponent()) { + if ((loc->isResultOfKeyPathDynamicMemberLookup() || + loc->isKeyPathSubscriptComponent()) || + loc->isKeyPathMemberComponent()) { if (protocol == getASTContext().getProtocol(KnownProtocolKind::Hashable)) { auto *fix = diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index ce8205e45f9df..5d567187eb6e4 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -607,6 +607,25 @@ bool ConstraintLocator::isKeyPathSubscriptComponent() const { }); } +bool ConstraintLocator::isKeyPathMemberComponent() const { + auto *anchor = getAsExpr(getAnchor()); + auto *KPE = dyn_cast_or_null(anchor); + if (!KPE) + return false; + + using ComponentKind = KeyPathExpr::Component::Kind; + return llvm::any_of(getPath(), [&](const LocatorPathElt &elt) { + auto keyPathElt = elt.getAs(); + if (!keyPathElt) + return false; + + auto index = keyPathElt->getIndex(); + auto &component = KPE->getComponents()[index]; + return component.getKind() == ComponentKind::Member || + component.getKind() == ComponentKind::UnresolvedMember; + }); +} + bool ConstraintLocator::isForKeyPathDynamicMemberLookup() const { auto path = getPath(); return !path.empty() && path.back().isKeyPathDynamicMember(); From 86d456e4ebc40c918120b5fcdc1be09ad010a33f Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Fri, 17 Jan 2025 18:02:38 -0800 Subject: [PATCH 14/29] [Sema] Block inout method arguments. --- include/swift/AST/DiagnosticsSema.def | 2 ++ lib/Sema/PreCheckTarget.cpp | 3 +++ 2 files changed, 5 insertions(+) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 01165ffd03dce..bb8df1fd34fb8 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1561,6 +1561,8 @@ ERROR(cannot_pass_inout_arg_to_subscript,none, "cannot pass an inout argument to a subscript; use " "'withUnsafeMutablePointer' to explicitly convert argument " "to a pointer", ()) +ERROR(cannot_pass_inout_arg_to_keypath_method,none, + "cannot pass an inout argument to a keypath method", ()) ERROR(incorrect_property_wrapper_reference,none, "cannot convert value %0 of type %1 to expected type %2, " diff --git a/lib/Sema/PreCheckTarget.cpp b/lib/Sema/PreCheckTarget.cpp index 83ea1712fcae1..d15c8d43c530e 100644 --- a/lib/Sema/PreCheckTarget.cpp +++ b/lib/Sema/PreCheckTarget.cpp @@ -1298,6 +1298,9 @@ class PreCheckTarget final : public ASTWalker { diag::extraneous_address_of); diag.fixItExchange(expr->getLoc(), lastInnerParenLoc); } + if (!parents.empty() && isa(parents[0])) + diags.diagnose(expr->getStartLoc(), + diag::cannot_pass_inout_arg_to_keypath_method); return finish(true, expr); } From 555a486cda177d5160f9d3bb949ce2d37c049bb6 Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Sat, 21 Dec 2024 20:52:45 -0800 Subject: [PATCH 15/29] [Mangler] Add new mangling schemes. --- docs/ABI/Mangling.rst | 2 + include/swift/AST/ASTMangler.h | 14 ++-- include/swift/Demangling/DemangleNodes.def | 2 + lib/AST/ASTMangler.cpp | 79 ++++++++++++++++++++-- lib/Demangling/Demangler.cpp | 14 +++- lib/Demangling/NodePrinter.cpp | 10 ++- lib/Demangling/OldRemangler.cpp | 12 ++++ lib/Demangling/Remangler.cpp | 10 +++ 8 files changed, 127 insertions(+), 16 deletions(-) diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index fb27d30f55b4a..04be4c0e201af 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -259,6 +259,8 @@ types where the metadata itself has unknown layout.) global ::= from-type to-type generic-signature? 'Tr' // obsolete mangling for reabstraction thunk global ::= entity generic-signature? type type* 'TK' // key path getter global ::= entity generic-signature? type type* 'Tk' // key path setter + global ::= entity generic-signature? type type* 'Tkmu' // key path unapplied method + global ::= entity generic-signature? type type* 'TkMA' // key path applied method global ::= type generic-signature 'TH' // key path equality global ::= type generic-signature 'Th' // key path hasher global ::= global generic-signature? 'TJ' AUTODIFF-FUNCTION-KIND INDEX-SUBSET 'p' INDEX-SUBSET 'r' // autodiff function diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index 36f4d54409b47..fd383d91f448e 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -346,16 +346,20 @@ class ASTMangler : public Mangler { AutoDiffLinearMapKind linearMapKind, const AutoDiffConfig &config); - std::string mangleKeyPathGetterThunkHelper(const AbstractStorageDecl *property, - GenericSignature signature, - CanType baseType, - SubstitutionMap subs, - ResilienceExpansion expansion); + std::string mangleKeyPathGetterThunkHelper( + const AbstractStorageDecl *property, GenericSignature signature, + CanType baseType, SubstitutionMap subs, ResilienceExpansion expansion); std::string mangleKeyPathSetterThunkHelper(const AbstractStorageDecl *property, GenericSignature signature, CanType baseType, SubstitutionMap subs, ResilienceExpansion expansion); + std::string mangleKeyPathUnappliedMethodThunkHelper( + const AbstractFunctionDecl *method, GenericSignature signature, + CanType baseType, SubstitutionMap subs, ResilienceExpansion expansion); + std::string mangleKeyPathAppliedMethodThunkHelper( + const AbstractFunctionDecl *method, GenericSignature signature, + CanType baseType, SubstitutionMap subs, ResilienceExpansion expansion); std::string mangleKeyPathEqualsHelper(ArrayRef indices, GenericSignature signature, ResilienceExpansion expansion); diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index 244af4bb5ccaa..33af2aa1c4bb8 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -161,6 +161,8 @@ NODE(NonIsolatedCallerFunctionType) NODE(SendingResultFunctionType) NODE(KeyPathGetterThunkHelper) NODE(KeyPathSetterThunkHelper) +NODE(KeyPathUnappliedMethodThunkHelper) +NODE(KeyPathAppliedMethodThunkHelper) NODE(KeyPathEqualsThunkHelper) NODE(KeyPathHashThunkHelper) NODE(LazyProtocolWitnessTableAccessor) diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 19c75b343f4d1..1025d7646bc36 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -377,19 +377,16 @@ std::string ASTMangler::mangleGlobalVariableFull(const VarDecl *decl) { } std::string ASTMangler::mangleKeyPathGetterThunkHelper( - const AbstractStorageDecl *property, - GenericSignature signature, - CanType baseType, - SubstitutionMap subs, - ResilienceExpansion expansion) { + const AbstractStorageDecl *property, GenericSignature signature, + CanType baseType, SubstitutionMap subs, ResilienceExpansion expansion) { beginMangling(); appendEntity(property); if (signature) appendGenericSignature(signature); appendType(baseType, signature); if (isa(property)) { - // Subscripts can be generic, and different key paths could capture the same - // subscript at different generic arguments. + // Subscripts can be generic, and different key paths could + // capture the same subscript at different generic arguments. for (auto sub : subs.getReplacementTypes()) { sub = sub->mapTypeOutOfContext(); @@ -448,6 +445,74 @@ std::string ASTMangler::mangleKeyPathSetterThunkHelper( return finalize(); } +std::string ASTMangler::mangleKeyPathAppliedMethodThunkHelper( + const AbstractFunctionDecl *method, GenericSignature signature, + CanType baseType, SubstitutionMap subs, ResilienceExpansion expansion) { + beginMangling(); + isa(method) + ? appendConstructorEntity(cast(method), false) + : appendEntity(method); + + if (signature) + appendGenericSignature(signature); + appendType(baseType, signature); + if (isa(method) || isa(method)) { + // Methods can be generic, and different key paths could capture the same + // method at different generic arguments. + for (auto sub : subs.getReplacementTypes()) { + sub = sub->mapTypeOutOfContext(); + + // FIXME: This seems wrong. We used to just mangle opened archetypes as + // their interface type. Let's make that explicit now. + sub = sub.transformRec([](Type t) -> std::optional { + if (auto *openedExistential = t->getAs()) + return openedExistential->getInterfaceType(); + return std::nullopt; + }); + + appendType(sub->getCanonicalType(), signature); + } + } + appendOperator("TkMA"); + if (expansion == ResilienceExpansion::Minimal) + appendOperator("q"); + return finalize(); +} + +std::string ASTMangler::mangleKeyPathUnappliedMethodThunkHelper( + const AbstractFunctionDecl *method, GenericSignature signature, + CanType baseType, SubstitutionMap subs, ResilienceExpansion expansion) { + beginMangling(); + isa(method) + ? appendConstructorEntity(cast(method), false) + : appendEntity(method); + + if (signature) + appendGenericSignature(signature); + appendType(baseType, signature); + if (isa(method) || isa(method)) { + // Methods can be generic, and different key paths could capture the same + // method at different generic arguments. + for (auto sub : subs.getReplacementTypes()) { + sub = sub->mapTypeOutOfContext(); + + // FIXME: This seems wrong. We used to just mangle opened archetypes as + // their interface type. Let's make that explicit now. + sub = sub.transformRec([](Type t) -> std::optional { + if (auto *openedExistential = t->getAs()) + return openedExistential->getInterfaceType(); + return std::nullopt; + }); + + appendType(sub->getCanonicalType(), signature); + } + } + appendOperator("Tkmu"); + if (expansion == ResilienceExpansion::Minimal) + appendOperator("q"); + return finalize(); +} + std::string ASTMangler::mangleKeyPathEqualsHelper(ArrayRef indices, GenericSignature signature, ResilienceExpansion expansion) { diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index dbdcf1d0bb6b4..9a56de65728af 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -2933,8 +2933,9 @@ NodePointer Demangler::demangleThunkOrSpecialization() { addChild(Thunk, popNode(Node::Kind::Type)); return Thunk; } - case 'g': + case 'g': { return demangleGenericSpecialization(Node::Kind::GenericSpecialization, nullptr); + } case 'G': return demangleGenericSpecialization(Node::Kind:: GenericSpecializationNotReAbstracted, nullptr); @@ -2966,8 +2967,15 @@ NodePointer Demangler::demangleThunkOrSpecialization() { return demangleFunctionSpecialization(); case 'K': case 'k': { - auto nodeKind = c == 'K' ? Node::Kind::KeyPathGetterThunkHelper - : Node::Kind::KeyPathSetterThunkHelper; + Node::Kind nodeKind; + if (nextIf("mu")) { + nodeKind = Node::Kind::KeyPathUnappliedMethodThunkHelper; + } else if (nextIf("MA")) { + nodeKind = Node::Kind::KeyPathAppliedMethodThunkHelper; + } else { + nodeKind = c == 'K' ? Node::Kind::KeyPathGetterThunkHelper + : Node::Kind::KeyPathSetterThunkHelper; + } bool isSerialized = nextIf('q'); diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index bb2b915508f24..a6698b65dab5c 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -456,6 +456,8 @@ class NodePrinter { case Node::Kind::PropertyWrapperInitFromProjectedValue: case Node::Kind::KeyPathGetterThunkHelper: case Node::Kind::KeyPathSetterThunkHelper: + case Node::Kind::KeyPathUnappliedMethodThunkHelper: + case Node::Kind::KeyPathAppliedMethodThunkHelper: case Node::Kind::KeyPathEqualsThunkHelper: case Node::Kind::KeyPathHashThunkHelper: case Node::Kind::LazyProtocolWitnessTableAccessor: @@ -2104,10 +2106,16 @@ NodePointer NodePrinter::print(NodePointer Node, unsigned depth, return nullptr; case Node::Kind::KeyPathGetterThunkHelper: case Node::Kind::KeyPathSetterThunkHelper: + case Node::Kind::KeyPathUnappliedMethodThunkHelper: + case Node::Kind::KeyPathAppliedMethodThunkHelper: if (Node->getKind() == Node::Kind::KeyPathGetterThunkHelper) Printer << "key path getter for "; - else + else if (Node->getKind() == Node::Kind::KeyPathSetterThunkHelper) Printer << "key path setter for "; + else if (Node->getKind() == Node::Kind::KeyPathUnappliedMethodThunkHelper) + Printer << "key path unapplied method "; + else if (Node->getKind() == Node::Kind::KeyPathAppliedMethodThunkHelper) + Printer << "key path applied method "; print(Node->getChild(0), depth + 1); Printer << " : "; diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index f7855ab5e4c49..bdae2c4d2d8c7 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -2709,6 +2709,18 @@ ManglingError Remangler::mangleKeyPathSetterThunkHelper(Node *node, return mangleChildNodes(node, depth + 1); } +ManglingError +Remangler::mangleKeyPathUnappliedMethodThunkHelper(Node *node, unsigned depth) { + Buffer << "Tkmu"; + return mangleChildNodes(node, depth + 1); +} + +ManglingError Remangler::mangleKeyPathAppliedMethodThunkHelper(Node *node, + unsigned depth) { + Buffer << "TkMA"; + return mangleChildNodes(node, depth + 1); +} + ManglingError Remangler::mangleKeyPathEqualsThunkHelper(Node *node, unsigned depth) { Buffer << "TH"; diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index a2de9f9bb8271..3d491ec0212da 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -3056,6 +3056,16 @@ ManglingError Remangler::mangleKeyPathSetterThunkHelper(Node *node, return mangleKeyPathThunkHelper(node, "Tk", depth + 1); } +ManglingError +Remangler::mangleKeyPathUnappliedMethodThunkHelper(Node *node, unsigned depth) { + return mangleKeyPathThunkHelper(node, "Tkmu", depth + 1); +} + +ManglingError Remangler::mangleKeyPathAppliedMethodThunkHelper(Node *node, + unsigned depth) { + return mangleKeyPathThunkHelper(node, "TkMA", depth + 1); +} + ManglingError Remangler::mangleKeyPathEqualsThunkHelper(Node *node, unsigned depth) { return mangleKeyPathThunkHelper(node, "TH", depth + 1); From e7e354d989a7ddfef2f33992379fa0fe76bf2544 Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Tue, 26 Nov 2024 00:23:14 -0800 Subject: [PATCH 16/29] [NFC] Generalize subscript code for methods. --- include/swift/SIL/SILInstruction.h | 10 +-- lib/IRGen/GenKeyPath.cpp | 73 ++++++++++----------- lib/SIL/IR/SILInstructions.cpp | 30 ++++----- lib/SIL/IR/SILPrinter.cpp | 26 ++++---- lib/SIL/Verifier/SILVerifier.cpp | 30 ++++----- lib/SILGen/SILGenApply.cpp | 29 ++++---- lib/SILGen/SILGenExpr.cpp | 43 +++++------- lib/SILGen/SILGenFunction.h | 32 ++++----- lib/SILGen/SILGenLValue.cpp | 2 +- lib/SILOptimizer/Utils/KeyPathProjector.cpp | 19 +++--- lib/Serialization/SerializeSIL.cpp | 16 ++--- 11 files changed, 146 insertions(+), 164 deletions(-) diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index d00600f2f40b5..1caebe7ae0f87 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -3691,7 +3691,7 @@ class KeyPathPatternComponent { llvm_unreachable("unhandled kind"); } - SILFunction *getComputedPropertyGetter() const { + SILFunction *getComputedPropertyForGettable() const { switch (getKind()) { case Kind::StoredProperty: case Kind::OptionalChain: @@ -3706,7 +3706,7 @@ class KeyPathPatternComponent { llvm_unreachable("unhandled kind"); } - SILFunction *getComputedPropertySetter() const { + SILFunction *getComputedPropertyForSettable() const { switch (getKind()) { case Kind::StoredProperty: case Kind::GettableProperty: @@ -3721,7 +3721,7 @@ class KeyPathPatternComponent { llvm_unreachable("unhandled kind"); } - ArrayRef getSubscriptIndices() const { + ArrayRef getArguments() const { switch (getKind()) { case Kind::StoredProperty: case Kind::OptionalChain: @@ -3736,7 +3736,7 @@ class KeyPathPatternComponent { llvm_unreachable("unhandled kind"); } - SILFunction *getSubscriptIndexEquals() const { + SILFunction *getIndexEquals() const { switch (getKind()) { case Kind::StoredProperty: case Kind::OptionalChain: @@ -3750,7 +3750,7 @@ class KeyPathPatternComponent { } llvm_unreachable("unhandled kind"); } - SILFunction *getSubscriptIndexHash() const { + SILFunction *getIndexHash() const { switch (getKind()) { case Kind::StoredProperty: case Kind::OptionalChain: diff --git a/lib/IRGen/GenKeyPath.cpp b/lib/IRGen/GenKeyPath.cpp index b4c392489e9e3..98a65640cb31d 100644 --- a/lib/IRGen/GenKeyPath.cpp +++ b/lib/IRGen/GenKeyPath.cpp @@ -104,16 +104,16 @@ getAccessorForComputedComponent(IRGenModule &IGM, SILFunction *accessor; switch (whichAccessor) { case Getter: - accessor = component.getComputedPropertyGetter(); + accessor = component.getComputedPropertyForGettable(); break; case Setter: - accessor = component.getComputedPropertySetter(); + accessor = component.getComputedPropertyForSettable(); break; case Equals: - accessor = component.getSubscriptIndexEquals(); + accessor = component.getIndexEquals(); break; case Hash: - accessor = component.getSubscriptIndexHash(); + accessor = component.getIndexHash(); break; } // If the accessor is locally available, we can use it as is. @@ -202,7 +202,7 @@ getLayoutFunctionForComputedComponent(IRGenModule &IGM, llvm::Value *size = llvm::ConstantInt::get(IGM.SizeTy, 0); llvm::Value *alignMask = llvm::ConstantInt::get(IGM.SizeTy, 0); - for (auto &index : component.getSubscriptIndices()) { + for (auto &index : component.getArguments()) { auto ty = genericEnv ? genericEnv->mapTypeIntoContext(IGM.getSILModule(), index.LoweredType) : index.LoweredType; @@ -218,7 +218,7 @@ getLayoutFunctionForComputedComponent(IRGenModule &IGM, alignMask = IGF.Builder.CreateOr(alignMask, indexAlign); } - + // If there's generic environment to capture, then it's stored as a block // of pointer-aligned words after the captured values. @@ -251,20 +251,20 @@ getWitnessTableForComputedComponent(IRGenModule &IGM, // If the only thing we're capturing is generic environment, then we can // use a prefab witness table from the runtime. A null reference will be // filled in by the runtime. - if (component.getSubscriptIndices().empty()) { + if (component.getArguments().empty()) { return nullptr; } - + // Are the index values trivial? bool isTrivial = true; - for (auto &component : component.getSubscriptIndices()) { + for (auto &component : component.getArguments()) { auto ty = genericEnv ? genericEnv->mapTypeIntoContext(IGM.getSILModule(), component.LoweredType) : component.LoweredType; auto &ti = IGM.getTypeInfo(ty); isTrivial &= ti.isTriviallyDestroyable(ResilienceExpansion::Minimal); } - + llvm::Constant *destroy = nullptr; llvm::Constant *copy; if (isTrivial) { @@ -290,14 +290,12 @@ getWitnessTableForComputedComponent(IRGenModule &IGM, auto params = IGF.collectParameters(); auto componentArgsBuf = params.claimNext(); auto componentArgsBufSize = params.claimNext(); - bindPolymorphicArgumentsFromComponentIndices(IGF, - genericEnv, requirements, - componentArgsBuf, - componentArgsBufSize, - !component.getSubscriptIndices().empty()); - + bindPolymorphicArgumentsFromComponentIndices( + IGF, genericEnv, requirements, componentArgsBuf, componentArgsBufSize, + !component.getArguments().empty()); + llvm::Value *offset = nullptr; - for (auto &component : component.getSubscriptIndices()) { + for (auto &component : component.getArguments()) { auto ty = genericEnv ? genericEnv->mapTypeIntoContext(IGM.getSILModule(), component.LoweredType) @@ -342,15 +340,13 @@ getWitnessTableForComputedComponent(IRGenModule &IGM, auto sourceArgsBuf = params.claimNext(); auto destArgsBuf = params.claimNext(); auto componentArgsBufSize = params.claimNext(); - bindPolymorphicArgumentsFromComponentIndices(IGF, - genericEnv, requirements, - sourceArgsBuf, - componentArgsBufSize, - !component.getSubscriptIndices().empty()); - + bindPolymorphicArgumentsFromComponentIndices( + IGF, genericEnv, requirements, sourceArgsBuf, componentArgsBufSize, + !component.getArguments().empty()); + // Copy over the index values. llvm::Value *offset = nullptr; - for (auto &component : component.getSubscriptIndices()) { + for (auto &component : component.getArguments()) { auto ty = genericEnv ? genericEnv->mapTypeIntoContext(IGM.getSILModule(), component.LoweredType) @@ -379,7 +375,7 @@ getWitnessTableForComputedComponent(IRGenModule &IGM, auto size = ti.getSize(IGF, ty); offset = IGF.Builder.CreateAdd(offset, size); } - + // Copy over the generic environment. if (genericEnv) { auto envAlignMask = llvm::ConstantInt::get(IGM.SizeTy, @@ -461,10 +457,10 @@ getInitializerForComputedComponent(IRGenModule &IGM, SmallVector srcAddresses; int lastOperandNeeded = -1; - for (auto &index : component.getSubscriptIndices()) { + for (auto &index : component.getArguments()) { lastOperandNeeded = std::max(lastOperandNeeded, (int)index.Operand); } - + llvm::Value *offset; if (genericEnv) { @@ -510,9 +506,9 @@ getInitializerForComputedComponent(IRGenModule &IGM, offset = llvm::ConstantInt::get(IGM.SizeTy, 0); // Transfer the operands we want into the destination buffer. - for (unsigned i : indices(component.getSubscriptIndices())) { - auto &index = component.getSubscriptIndices()[i]; - + for (unsigned i : indices(component.getArguments())) { + auto &index = component.getArguments()[i]; + auto ty = genericEnv ? genericEnv->mapTypeIntoContext(IGM.getSILModule(), index.LoweredType) @@ -543,14 +539,14 @@ getInitializerForComputedComponent(IRGenModule &IGM, auto size = ti.getSize(IGF, ty); offset = IGF.Builder.CreateAdd(offset, size); } - + // Transfer the generic environment. // External components don't need to store the key path environment (and // can't), since they need to already have enough information to function // independently of any context using the component. if (genericEnv) { auto destGenericEnv = dest; - if (!component.getSubscriptIndices().empty()) { + if (!component.getArguments().empty()) { auto genericEnvAlignMask = llvm::ConstantInt::get(IGM.SizeTy, IGM.getPointerAlignment().getMaskValue()); auto notGenericEnvAlignMask = IGF.Builder.CreateNot(genericEnvAlignMask); @@ -559,7 +555,7 @@ getInitializerForComputedComponent(IRGenModule &IGM, destGenericEnv = IGF.Builder.CreateInBoundsGEP(IGM.Int8Ty, dest, offset); } - + auto align = IGM.getPointerAlignment().getValue(); IGF.Builder.CreateMemCpy( destGenericEnv, llvm::MaybeAlign(align), src, llvm::MaybeAlign(align), @@ -1169,7 +1165,7 @@ IRGenModule::getAddrOfKeyPathPattern(KeyPathPattern *pattern, switch (component.getKind()) { case KeyPathPatternComponent::Kind::GettableProperty: case KeyPathPatternComponent::Kind::SettableProperty: - for (auto &index : component.getSubscriptIndices()) { + for (auto &index : component.getArguments()) { operands[index.Operand].LoweredType = index.LoweredType; operands[index.Operand].LastUser = &component; } @@ -1185,12 +1181,11 @@ IRGenModule::getAddrOfKeyPathPattern(KeyPathPattern *pattern, for (unsigned i : indices(pattern->getComponents())) { auto &component = pattern->getComponents()[i]; - + emitKeyPathComponent(*this, fields, component, isInstantiableOnce, - genericEnv, requirements, - baseTy, operands, - !component.getSubscriptIndices().empty()); - + genericEnv, requirements, baseTy, operands, + !component.getArguments().empty()); + // For all but the last component, we pack in the type of the component. if (i + 1 != pattern->getComponents().size()) { fields.addRelativeAddress( diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index 1c8904ca7f311..db414626b28bc 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -2939,7 +2939,7 @@ bool KeyPathPatternComponent::isComputedSettablePropertyMutating() const { case Kind::TupleElement: llvm_unreachable("not a settable computed property"); case Kind::SettableProperty: { - auto setter = getComputedPropertySetter(); + auto setter = getComputedPropertyForSettable(); return setter->getLoweredFunctionType()->getParameters()[1].getConvention() == ParameterConvention::Indirect_Inout; } @@ -2958,11 +2958,11 @@ forEachRefcountableReference(const KeyPathPatternComponent &component, case KeyPathPatternComponent::Kind::TupleElement: return; case KeyPathPatternComponent::Kind::SettableProperty: - forFunction(component.getComputedPropertySetter()); + forFunction(component.getComputedPropertyForSettable()); LLVM_FALLTHROUGH; case KeyPathPatternComponent::Kind::GettableProperty: - forFunction(component.getComputedPropertyGetter()); - + forFunction(component.getComputedPropertyForGettable()); + switch (component.getComputedPropertyId().getKind()) { case KeyPathPatternComponent::ComputedPropertyId::DeclRef: // Mark the vtable entry as used somehow? @@ -2973,10 +2973,10 @@ forEachRefcountableReference(const KeyPathPatternComponent &component, case KeyPathPatternComponent::ComputedPropertyId::Property: break; } - - if (auto equals = component.getSubscriptIndexEquals()) + + if (auto equals = component.getIndexEquals()) forFunction(equals); - if (auto hash = component.getSubscriptIndexHash()) + if (auto hash = component.getIndexHash()) forFunction(hash); return; } @@ -3017,7 +3017,7 @@ KeyPathPattern::get(SILModule &M, CanGenericSignature signature, case KeyPathPatternComponent::Kind::GettableProperty: case KeyPathPatternComponent::Kind::SettableProperty: - for (auto &index : component.getSubscriptIndices()) { + for (auto &index : component.getArguments()) { maxOperandNo = std::max(maxOperandNo, (int)index.Operand); } } @@ -3098,10 +3098,10 @@ void KeyPathPattern::Profile(llvm::FoldingSetNodeID &ID, break; case KeyPathPatternComponent::Kind::SettableProperty: - ID.AddPointer(component.getComputedPropertySetter()); + ID.AddPointer(component.getComputedPropertyForSettable()); LLVM_FALLTHROUGH; case KeyPathPatternComponent::Kind::GettableProperty: - ID.AddPointer(component.getComputedPropertyGetter()); + ID.AddPointer(component.getComputedPropertyForGettable()); auto id = component.getComputedPropertyId(); ID.AddInteger(id.getKind()); switch (id.getKind()) { @@ -3122,7 +3122,7 @@ void KeyPathPattern::Profile(llvm::FoldingSetNodeID &ID, break; } } - profileIndices(component.getSubscriptIndices()); + profileIndices(component.getArguments()); ID.AddPointer(component.getExternalDecl()); component.getExternalSubstitutions().profile(ID); break; @@ -3219,10 +3219,10 @@ visitReferencedFunctionsAndMethods( std::function methodCallBack) const { switch (getKind()) { case KeyPathPatternComponent::Kind::SettableProperty: - functionCallBack(getComputedPropertySetter()); + functionCallBack(getComputedPropertyForSettable()); LLVM_FALLTHROUGH; case KeyPathPatternComponent::Kind::GettableProperty: { - functionCallBack(getComputedPropertyGetter()); + functionCallBack(getComputedPropertyForGettable()); auto id = getComputedPropertyId(); switch (id.getKind()) { case KeyPathPatternComponent::ComputedPropertyId::DeclRef: { @@ -3236,9 +3236,9 @@ visitReferencedFunctionsAndMethods( break; } - if (auto equals = getSubscriptIndexEquals()) + if (auto equals = getIndexEquals()) functionCallBack(equals); - if (auto hash = getSubscriptIndexHash()) + if (auto hash = getIndexHash()) functionCallBack(hash); break; diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 59db6c1a4b20c..9f2001e28f814 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -3124,29 +3124,27 @@ class SILPrinter : public SILInstructionVisitor { } } *this << ", getter "; - component.getComputedPropertyGetter()->printName(PrintState.OS); + component.getComputedPropertyForGettable()->printName(PrintState.OS); *this << " : " - << component.getComputedPropertyGetter()->getLoweredType(); + << component.getComputedPropertyForGettable()->getLoweredType(); if (kind == KeyPathPatternComponent::Kind::SettableProperty) { *this << ", setter "; - component.getComputedPropertySetter()->printName(PrintState.OS); + component.getComputedPropertyForSettable()->printName(PrintState.OS); *this << " : " - << component.getComputedPropertySetter()->getLoweredType(); + << component.getComputedPropertyForSettable()->getLoweredType(); } - - if (!component.getSubscriptIndices().empty()) { + + if (!component.getArguments().empty()) { *this << ", indices "; - printComponentIndices(component.getSubscriptIndices()); + printComponentIndices(component.getArguments()); *this << ", indices_equals "; - component.getSubscriptIndexEquals()->printName(PrintState.OS); - *this << " : " - << component.getSubscriptIndexEquals()->getLoweredType(); + component.getIndexEquals()->printName(PrintState.OS); + *this << " : " << component.getIndexEquals()->getLoweredType(); *this << ", indices_hash "; - component.getSubscriptIndexHash()->printName(PrintState.OS); - *this << " : " - << component.getSubscriptIndexHash()->getLoweredType(); + component.getIndexHash()->printName(PrintState.OS); + *this << " : " << component.getIndexHash()->getLoweredType(); } - + if (auto external = component.getExternalDecl()) { *this << ", external #"; printValueDecl(external, PrintState.OS); diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 8bb6d4c9f4978..08b1653599d8e 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -295,12 +295,12 @@ void verifyKeyPathComponent(SILModule &M, auto getTypeInExpansionContext = [&](CanType ty) -> CanType { return M.Types.getLoweredType(opaque, ty, typeExpansionContext).getASTType(); }; - auto checkIndexEqualsAndHash = [&]{ - if (!component.getSubscriptIndices().empty()) { + auto checkIndexEqualsAndHash = [&] { + if (!component.getArguments().empty()) { // Equals should be // @convention(thin) (RawPointer, RawPointer) -> Bool { - auto equals = component.getSubscriptIndexEquals(); + auto equals = component.getIndexEquals(); require(equals, "key path pattern with indexes must have equals " "operator"); @@ -332,7 +332,7 @@ void verifyKeyPathComponent(SILModule &M, { // Hash should be // @convention(thin) (RawPointer) -> Int - auto hash = component.getSubscriptIndexHash(); + auto hash = component.getIndexHash(); require(hash, "key path pattern with indexes must have hash " "operator"); @@ -359,8 +359,7 @@ void verifyKeyPathComponent(SILModule &M, "result should be Int"); } } else { - require(!component.getSubscriptIndexEquals() - && !component.getSubscriptIndexHash(), + require(!component.getIndexEquals() && !component.getIndexHash(), "component without indexes must not have equals/hash"); } }; @@ -397,16 +396,15 @@ void verifyKeyPathComponent(SILModule &M, case KeyPathPatternComponent::Kind::GettableProperty: case KeyPathPatternComponent::Kind::SettableProperty: { if (forPropertyDescriptor) { - require(component.getSubscriptIndices().empty() - && !component.getSubscriptIndexEquals() - && !component.getSubscriptIndexHash(), + require(component.getArguments().empty() && !component.getIndexEquals() && + !component.getIndexHash(), "property descriptor should not have index information"); - + require(component.getExternalDecl() == nullptr && component.getExternalSubstitutions().empty(), "property descriptor should not refer to another external decl"); } else { - require(hasIndices == !component.getSubscriptIndices().empty(), + require(hasIndices == !component.getArguments().empty(), "component for subscript should have indices"); } @@ -414,7 +412,7 @@ void verifyKeyPathComponent(SILModule &M, // Getter should be @convention(thin) (@in_guaranteed Base) -> @out Result { - auto getter = component.getComputedPropertyGetter(); + auto getter = component.getComputedPropertyForGettable(); if (expansion == ResilienceExpansion::Minimal) { require(serializedKind != IsNotSerialized && getter->hasValidLinkageForFragileRef(serializedKind), @@ -460,8 +458,8 @@ void verifyKeyPathComponent(SILModule &M, if (kind == KeyPathPatternComponent::Kind::SettableProperty) { // Setter should be // @convention(thin) (@in_guaranteed Result, @in Base) -> () - - auto setter = component.getComputedPropertySetter(); + + auto setter = component.getComputedPropertyForSettable(); if (expansion == ResilienceExpansion::Minimal) { require(serializedKind != IsNotSerialized && setter->hasValidLinkageForFragileRef(serializedKind), @@ -510,7 +508,7 @@ void verifyKeyPathComponent(SILModule &M, } if (!forPropertyDescriptor) { - for (auto &index : component.getSubscriptIndices()) { + for (auto &index : component.getArguments()) { auto opIndex = index.Operand; auto contextType = index.LoweredType.subst(M, patternSubs); @@ -5900,7 +5898,7 @@ class SILVerifier : public SILVerifierBase { switch (component.getKind()) { case KeyPathPatternComponent::Kind::GettableProperty: case KeyPathPatternComponent::Kind::SettableProperty: - hasIndices = !component.getSubscriptIndices().empty(); + hasIndices = !component.getArguments().empty(); break; case KeyPathPatternComponent::Kind::StoredProperty: diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 2599cfc4990c1..1c2ce7970e892 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -7232,17 +7232,16 @@ static void emitPseudoFunctionArguments(SILGenFunction &SGF, outVals.swap(argValues); } -PreparedArguments -SILGenFunction::prepareSubscriptIndices(SILLocation loc, - SubscriptDecl *subscript, - SubstitutionMap subs, - AccessStrategy strategy, - ArgumentList *argList) { +PreparedArguments SILGenFunction::prepareIndices(SILLocation loc, + ValueDecl *decl, + SubstitutionMap subs, + AccessStrategy strategy, + ArgumentList *argList) { // TODO: use the real abstraction pattern from the accessor(s) in the // strategy. // Currently we use the substituted type so that we can reconstitute these // as RValues. - Type interfaceType = subscript->getInterfaceType(); + Type interfaceType = decl->getInterfaceType(); CanFunctionType substFnType; if (subs) @@ -7900,10 +7899,11 @@ SILGenFunction::emitDynamicSubscriptGetterApply(SILLocation loc, emitManagedRValueWithCleanup(optResult, optTL)); } -SmallVector SILGenFunction::emitKeyPathSubscriptOperands( - SILLocation loc, SubscriptDecl *subscript, - SubstitutionMap subs, ArgumentList *argList) { - Type interfaceType = subscript->getInterfaceType(); +SmallVector +SILGenFunction::emitKeyPathOperands(SILLocation loc, ValueDecl *decl, + SubstitutionMap subs, + ArgumentList *argList) { + Type interfaceType = decl->getInterfaceType(); CanFunctionType substFnType = subs ? cast(interfaceType->castTo() ->substGenericArgs(subs) @@ -7920,10 +7920,9 @@ SmallVector SILGenFunction::emitKeyPathSubscriptOperands( ClaimedParamsRef(fnType->getParameters()), argValues, delayedArgs, ForeignInfo{}); - auto prepared = - prepareSubscriptIndices(loc, subscript, subs, - // Strategy doesn't matter - AccessStrategy::getStorage(), argList); + auto prepared = prepareIndices(loc, decl, subs, + // Strategy doesn't matter + AccessStrategy::getStorage(), argList); emitter.emitPreparedArgs(std::move(prepared), origFnType); if (!delayedArgs.empty()) diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 1d863e9b8e464..4d12b2ba2b42d 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3175,13 +3175,10 @@ RValue RValueEmitter::visitObjCSelectorExpr(ObjCSelectorExpr *e, SGFContext C) { ManagedValue::forObjectRValueWithoutOwnership(selectorValue)); } -static ManagedValue -emitKeyPathRValueBase(SILGenFunction &subSGF, - AbstractStorageDecl *storage, - SILLocation loc, - SILValue paramArg, - CanType &baseType, - SubstitutionMap subs) { +static ManagedValue emitKeyPathRValueBase(SILGenFunction &subSGF, + ValueDecl *storage, SILLocation loc, + SILValue paramArg, CanType &baseType, + SubstitutionMap subs) { // If the storage is at global scope, then the base value () is a formality. // There no real argument to pass to the underlying accessors. if (!storage->getDeclContext()->isTypeContext()) @@ -3237,16 +3234,14 @@ emitKeyPathRValueBase(SILGenFunction &subSGF, using IndexTypePair = std::pair; -/// Helper function to load the captured indexes out of a key path component -/// in order to invoke the accessors on that key path. A component with captured -/// indexes passes down a pointer to those captures to the accessor thunks, -/// which we can copy out of to produce values we can pass to the real -/// accessor functions. -static PreparedArguments -loadIndexValuesForKeyPathComponent(SILGenFunction &SGF, SILLocation loc, - AbstractStorageDecl *storage, - ArrayRef indexes, - SILValue pointer) { +/// Helper function to load the captured indexes out of a key path +/// component in order to invoke the accessors on that key path. A component +/// with captured indexes passes down a pointer to those captures to the +/// accessor thunks, which we can copy out of to produce values we can pass to +/// the real accessor functions. +static PreparedArguments loadIndexValuesForKeyPathComponent( + SILGenFunction &SGF, SILLocation loc, ValueDecl *storage, + ArrayRef indexes, SILValue pointer) { // If not a subscript, do nothing. if (!isa(storage)) return PreparedArguments(); @@ -4125,12 +4120,10 @@ lowerKeyPathSubscriptIndexTypes( } } -static void -lowerKeyPathSubscriptIndexPatterns( - SmallVectorImpl &indexPatterns, - ArrayRef indexTypes, - ArrayRef indexHashables, - unsigned &baseOperand) { +static void lowerKeyPathMemberIndexPatterns( + SmallVectorImpl &indexPatterns, + ArrayRef indexTypes, + ArrayRef indexHashables, unsigned &baseOperand) { for (unsigned i : indices(indexTypes)) { CanType formalTy; SILType loweredTy; @@ -4352,7 +4345,7 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc, SILFunction *indexEquals = nullptr, *indexHash = nullptr; // Property descriptors get their index information from the client. if (!forPropertyDescriptor) { - lowerKeyPathSubscriptIndexPatterns(indexPatterns, + lowerKeyPathMemberIndexPatterns(indexPatterns, indexTypes, indexHashables, baseOperand); @@ -4442,7 +4435,7 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { break; auto subscript = cast(decl); - auto loweredArgs = SGF.emitKeyPathSubscriptOperands( + auto loweredArgs = SGF.emitKeyPathOperands( E, subscript, component.getDeclRef().getSubstitutions(), component.getArgs()); diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 3a383d8f71949..42e6e468e4383 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -1138,18 +1138,21 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction bool isSelfConformance, bool isPreconcurrency, std::optional enterIsolation); - /// Generates subscript arguments for keypath. This function handles lowering - /// of all index expressions including default arguments. + /// Generates subscript or method arguments for keypath. This function handles + /// lowering of all arguments or index expressions including default + /// arguments. /// /// \returns Lowered index arguments. - /// \param subscript - The subscript decl who's arguments are being lowered. - /// \param subs - Used to get subscript function type and to substitute generic args. - /// \param argList - The argument list of the subscript. - SmallVector - emitKeyPathSubscriptOperands(SILLocation loc, - SubscriptDecl *subscript, - SubstitutionMap subs, - ArgumentList *argList); + /// \param decl - The subscript or method decl whose arguments are being + /// lowered. + /// \param subs - Used to get subscript or method function type and to + /// substitute generic args. + /// \param argList - The argument list of the + /// subscript or method. + SmallVector emitKeyPathOperands(SILLocation loc, + ValueDecl *decl, + SubstitutionMap subs, + ArgumentList *argList); /// Convert a block to a native function with a thunk. ManagedValue emitBlockToFunc(SILLocation loc, @@ -1930,11 +1933,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction const FunctionTypeInfo &typeContext, SubstitutionMap subs); - PreparedArguments prepareSubscriptIndices(SILLocation loc, - SubscriptDecl *subscript, - SubstitutionMap subs, - AccessStrategy strategy, - ArgumentList *argList); + PreparedArguments prepareIndices(SILLocation loc, ValueDecl *decl, + SubstitutionMap subs, + AccessStrategy strategy, + ArgumentList *argList); ArgumentSource prepareAccessorBaseArg(SILLocation loc, ManagedValue base, CanType baseFormalType, diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 778300b6e94e1..26594ab73e975 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -4286,7 +4286,7 @@ LValue SILGenLValue::visitSubscriptExpr(SubscriptExpr *e, actorIso = getActorIsolation(decl); auto *argList = e->getArgs(); - auto indices = SGF.prepareSubscriptIndices(e, decl, subs, strategy, argList); + auto indices = SGF.prepareIndices(e, decl, subs, strategy, argList); lv.addMemberSubscriptComponent(SGF, e, decl, subs, options, e->isSuper(), accessKind, strategy, diff --git a/lib/SILOptimizer/Utils/KeyPathProjector.cpp b/lib/SILOptimizer/Utils/KeyPathProjector.cpp index 558fd741dac10..22da71d94ca54 100644 --- a/lib/SILOptimizer/Utils/KeyPathProjector.cpp +++ b/lib/SILOptimizer/Utils/KeyPathProjector.cpp @@ -221,10 +221,10 @@ class GettablePropertyProjector : public ComponentProjector { component.getKind() == KeyPathPatternComponent::Kind::SettableProperty); assert(accessType == AccessType::Get && "property is not settable"); - + parent->project(accessType, [&](SILValue parentValue) { - auto getter = component.getComputedPropertyGetter(); - + auto getter = component.getComputedPropertyForGettable(); + // The callback expects a memory address it can read from, // so allocate a buffer. auto &function = builder.getFunction(); @@ -248,7 +248,6 @@ class GettablePropertyProjector : public ComponentProjector { builder.createDestroyAddr(loc, addr); builder.createDeallocStack(loc, addr); }); - } protected: KeyPathInst *keyPath; @@ -256,7 +255,7 @@ class GettablePropertyProjector : public ComponentProjector { BeginAccessInst *&beginAccess; void assertHasNoContext() { - assert(component.getSubscriptIndices().empty() && + assert(component.getArguments().empty() && component.getExternalSubstitutions().empty() && "cannot yet optimize key path component with external context; " "we should have checked for this before trying to project"); @@ -298,11 +297,11 @@ class SettablePropertyProjector : public GettablePropertyProjector { } else { parentAccessType = AccessType::Get; } - + parent->project(parentAccessType, [&](SILValue parentValue) { - auto getter = component.getComputedPropertyGetter(); - auto setter = component.getComputedPropertySetter(); - + auto getter = component.getComputedPropertyForGettable(); + auto setter = component.getComputedPropertyForSettable(); + // The callback expects a memory address it can write to, // so allocate a writeback buffer. auto &function = builder.getFunction(); @@ -696,7 +695,7 @@ KeyPathProjector::create(SILValue keyPath, SILValue root, case KeyPathPatternComponent::Kind::GettableProperty: case KeyPathPatternComponent::Kind::SettableProperty: if (!comp.getExternalSubstitutions().empty() || - !comp.getSubscriptIndices().empty()) { + !comp.getArguments().empty()) { // TODO: right now we can't optimize computed properties that require // additional context for subscript indices or generic environment // See https://github.com/apple/swift/pull/28799#issuecomment-570299845 diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 8fb3036a78547..adba6080dd97d 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -884,8 +884,8 @@ SILSerializer::writeKeyPathPatternComponent( ListOfValues.push_back(S.addDeclRef(component.getExternalDecl())); ListOfValues.push_back( S.addSubstitutionMapRef(component.getExternalSubstitutions())); - - auto indices = component.getSubscriptIndices(); + + auto indices = component.getArguments(); ListOfValues.push_back(indices.size()); for (auto &index : indices) { ListOfValues.push_back(index.Operand); @@ -896,10 +896,8 @@ SILSerializer::writeKeyPathPatternComponent( ListOfValues.push_back(S.addConformanceRef(index.Hashable)); } if (!indices.empty()) { - ListOfValues.push_back( - addSILFunctionRef(component.getSubscriptIndexEquals())); - ListOfValues.push_back( - addSILFunctionRef(component.getSubscriptIndexHash())); + ListOfValues.push_back(addSILFunctionRef(component.getIndexEquals())); + ListOfValues.push_back(addSILFunctionRef(component.getIndexHash())); } }; @@ -912,16 +910,16 @@ SILSerializer::writeKeyPathPatternComponent( handleComponentCommon(KeyPathComponentKindEncoding::GettableProperty); handleComputedId(component.getComputedPropertyId()); ListOfValues.push_back( - addSILFunctionRef(component.getComputedPropertyGetter())); + addSILFunctionRef(component.getComputedPropertyForGettable())); handleComputedExternalReferenceAndIndices(component); break; case KeyPathPatternComponent::Kind::SettableProperty: handleComponentCommon(KeyPathComponentKindEncoding::SettableProperty); handleComputedId(component.getComputedPropertyId()); ListOfValues.push_back( - addSILFunctionRef(component.getComputedPropertyGetter())); + addSILFunctionRef(component.getComputedPropertyForGettable())); ListOfValues.push_back( - addSILFunctionRef(component.getComputedPropertySetter())); + addSILFunctionRef(component.getComputedPropertyForSettable())); handleComputedExternalReferenceAndIndices(component); break; case KeyPathPatternComponent::Kind::OptionalChain: From ae86b0e96d6656330a2c2ad4540f808f2e9a68fb Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Tue, 21 Jan 2025 16:20:29 -0800 Subject: [PATCH 17/29] [NFC] Refactor getter/setter logic for reuse. --- lib/SILGen/SILGenExpr.cpp | 429 ++++++++++++++++++-------------------- 1 file changed, 200 insertions(+), 229 deletions(-) diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 4d12b2ba2b42d..92760ee6fa0b0 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3298,138 +3298,210 @@ static CanType buildKeyPathIndicesTuple(ASTContext &C, return TupleType::get(indicesElements, C)->getCanonicalType(); } -static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, - AbstractStorageDecl *property, - SubstitutionMap subs, - GenericEnvironment *genericEnv, - ResilienceExpansion expansion, - ArrayRef indexes, - CanType baseType, - CanType propertyType) { +/// Emit keypath thunk return. +static void emitReturn(SILGenFunction &subSGF, CanType methodType, + ManagedValue &resultSubst, SILType resultArgTy, + SILFunctionArgument *resultArg, ArgumentScope &scope, + SILLocation loc) { + if (resultSubst.getType().getAddressType() != resultArgTy) + resultSubst = subSGF.emitSubstToOrigValue( + loc, resultSubst, AbstractionPattern::getOpaque(), methodType); + + if (subSGF.F.getModule().useLoweredAddresses()) { + resultSubst.forwardInto(subSGF, loc, resultArg); + scope.pop(); + subSGF.B.createReturn(loc, subSGF.emitEmptyTuple(loc)); + } else { + auto result = resultSubst.forward(subSGF); + scope.pop(); + subSGF.B.createReturn(loc, result); + } +} + +/// For keypaths to properties and subscripts defined in protocols, use the decl +/// defined in the conforming type's implementation. +static void lookupPropertyViaProtocol(AbstractStorageDecl *&property, + SubstitutionMap &subs, + AccessorKind accessorKind) { // If the storage declaration is from a protocol, chase the override chain // back to the declaration whose getter introduced the witness table // entry. if (isa(property->getDeclContext())) { - auto accessor = getRepresentativeAccessorForKeyPath(property); + AccessorDecl *accessor = nullptr; + + if (accessorKind == AccessorKind::Set) { + accessor = property->getOpaqueAccessor(AccessorKind::Set); + } else { + accessor = getRepresentativeAccessorForKeyPath(property); + } + if (!accessor->requiresNewWitnessTableEntry()) { - // Find the getter that does have a witness table entry. - auto wtableAccessor = - cast(SILDeclRef::getOverriddenWitnessTableEntry(accessor)); + // Find the overridden accessor that has a witness table entry. + auto wtableAccessor = cast( + SILDeclRef::getOverriddenWitnessTableEntry(accessor)); // Substitute the 'Self' type of the base protocol. subs = SILGenModule::mapSubstitutionsForWitnessOverride( - accessor, wtableAccessor, subs); + accessor, wtableAccessor, subs); property = wtableAccessor->getStorage(); } } +} - auto genericSig = - genericEnv ? genericEnv->getGenericSignature().getCanonicalSignature() - : nullptr; - if (genericSig && genericSig->areAllParamsConcrete()) { - genericSig = nullptr; - genericEnv = nullptr; - } - - // Build the signature of the thunk as expected by the keypath runtime. - auto signature = [&]() { - CanType loweredBaseTy, loweredPropTy; - AbstractionPattern opaque = AbstractionPattern::getOpaque(); - +/// Build the signature of the thunk as expected by the keypath runtime. +static CanSILFunctionType +createKeyPathSignature(SILGenModule &SGM, GenericSignature genericSig, + CanType baseType, CanType propertyType, + ArrayRef indexes, + SILFunctionType::Representation representation, + ParameterConvention paramConvention, + AbstractStorageDecl *property = nullptr) { + CanType loweredBaseTy, loweredPropTy; + { + auto opaque = AbstractionPattern::getOpaque(); loweredBaseTy = SGM.Types.getLoweredRValueType( TypeExpansionContext::minimal(), opaque, baseType); loweredPropTy = SGM.Types.getLoweredRValueType( TypeExpansionContext::minimal(), opaque, propertyType); - - auto paramConvention = ParameterConvention::Indirect_In_Guaranteed; + } - SmallVector params; + SmallVector params; + if (representation == + SILFunctionType::Representation::KeyPathAccessorSetter) { + params.push_back({loweredPropTy, paramConvention}); + params.push_back({loweredBaseTy, property && property->isSetterMutating() + ? ParameterConvention::Indirect_Inout + : paramConvention}); + } else { params.push_back({loweredBaseTy, paramConvention}); - auto &C = SGM.getASTContext(); + } - if (!indexes.empty()) { - if (indexes.size() > 1) { - SmallVector indicesElements; - for (auto &elt : indexes) { - indicesElements.emplace_back(elt.first); - } - auto indicesTupleTy = TupleType::get(indicesElements, C)->getCanonicalType(); - params.push_back({indicesTupleTy, paramConvention}); - } else { - params.push_back({indexes[0].first, paramConvention}); - } + auto &C = SGM.getASTContext(); + if (!indexes.empty()) { + if (indexes.size() > 1) { + SmallVector indicesElements; + for (auto &elt : indexes) + indicesElements.emplace_back(elt.first); + auto indicesTupleTy = + TupleType::get(indicesElements, C)->getCanonicalType(); + params.push_back({indicesTupleTy, paramConvention}); + } else { + params.push_back({indexes[0].first, paramConvention}); } + } - SILResultInfo result(loweredPropTy, ResultConvention::Indirect); - - return SILFunctionType::get( - genericSig, - SILFunctionType::ExtInfo().withRepresentation( - SILFunctionType::Representation::KeyPathAccessorGetter), - SILCoroutineKind::None, ParameterConvention::Direct_Unowned, params, {}, - result, std::nullopt, SubstitutionMap(), SubstitutionMap(), - SGM.getASTContext()); - }(); - - // Find the function and see if we already created it. - auto name = Mangle::ASTMangler(SGM.getASTContext(), property) - .mangleKeyPathGetterThunkHelper(property, genericSig, baseType, - subs, expansion); - auto loc = RegularLocation::getAutoGeneratedLocation(); + SmallVector results; + if (representation == + SILFunctionType::Representation::KeyPathAccessorGetter) { + results.push_back(SILResultInfo(loweredPropTy, ResultConvention::Indirect)); + } + return SILFunctionType::get( + genericSig, SILFunctionType::ExtInfo().withRepresentation(representation), + SILCoroutineKind::None, ParameterConvention::Direct_Unowned, params, {}, + results, std::nullopt, SubstitutionMap(), SubstitutionMap(), C); +} +/// Find a preexisting function or define a new thunk. +static SILFunction *getOrCreateKeypathThunk(SILGenModule &SGM, + const std::string &name, + CanSILFunctionType signature, + GenericEnvironment *genericEnv, + ResilienceExpansion expansion, + RegularLocation loc) { SILGenFunctionBuilder builder(SGM); auto thunk = builder.getOrCreateSharedFunction( loc, name, signature, IsBare, IsNotTransparent, - (expansion == ResilienceExpansion::Minimal - ? IsSerialized - : IsNotSerialized), + (expansion == ResilienceExpansion::Minimal ? IsSerialized + : IsNotSerialized), ProfileCounter(), IsThunk, IsNotDynamic, IsNotDistributed, IsNotRuntimeAccessible); + return thunk; +} + +static void emitKeyPathThunk( + SILGenModule &SGM, SILGenFunction &subSGF, GenericEnvironment *genericEnv, + CanSILFunctionType signature, SILFunction *thunk, + ArrayRef args, SILFunctionArgument *&resultArg, + SILType &resultArgTy, SILFunctionArgument *&baseArg, SILType &baseArgTy, + SILValue &argPtr, SILParameterInfo paramInfo, bool lowerValueArg = false) { + auto entry = thunk->begin(); + if (genericEnv) { + resultArgTy = genericEnv->mapTypeIntoContext(SGM.M, resultArgTy); + baseArgTy = genericEnv->mapTypeIntoContext(SGM.M, baseArgTy); + } + if (!lowerValueArg) { + if (SGM.M.useLoweredAddresses()) { + resultArg = entry->createFunctionArgument(resultArgTy); + } + } else { + resultArg = entry->createFunctionArgument(resultArgTy); + } + baseArg = entry->createFunctionArgument(baseArgTy); + if (!args.empty()) { + auto argTy = subSGF.silConv.getSILType(paramInfo, signature, + subSGF.F.getTypeExpansionContext()); + if (genericEnv) + argTy = genericEnv->mapTypeIntoContext(SGM.M, argTy); + argPtr = entry->createFunctionArgument(argTy); + } +} + +static SILFunction *getOrCreateKeyPathGetter( + SILGenModule &SGM, AbstractStorageDecl *property, SubstitutionMap subs, + GenericEnvironment *genericEnv, ResilienceExpansion expansion, + ArrayRef indexes, CanType baseType, CanType propertyType) { + lookupPropertyViaProtocol(property, subs, AccessorKind::Get); + + auto genericSig = + genericEnv ? genericEnv->getGenericSignature().getCanonicalSignature() + : nullptr; + if (genericSig && genericSig->areAllParamsConcrete()) { + genericSig = nullptr; + genericEnv = nullptr; + } + + auto signature = createKeyPathSignature( + SGM, genericSig, baseType, propertyType, indexes, + SILFunctionType::Representation::KeyPathAccessorGetter, + ParameterConvention::Indirect_In_Guaranteed); + + // Find the function and see if we already created it. + auto name = Mangle::ASTMangler(SGM.getASTContext(), property) + .mangleKeyPathGetterThunkHelper(property, genericSig, + baseType, subs, expansion); + auto loc = RegularLocation::getAutoGeneratedLocation(); + + auto thunk = + getOrCreateKeypathThunk(SGM, name, signature, genericEnv, expansion, loc); if (!thunk->empty()) return thunk; - + // Emit the thunk, which accesses the underlying property normally with // reabstraction where necessary. if (genericEnv) { baseType = genericEnv->mapTypeIntoContext(baseType)->getCanonicalType(); - propertyType = genericEnv->mapTypeIntoContext(propertyType) - ->getCanonicalType(); + propertyType = + genericEnv->mapTypeIntoContext(propertyType)->getCanonicalType(); thunk->setGenericEnvironment(genericEnv); } - SILGenFunction subSGF(SGM, *thunk, SGM.SwiftModule); - signature = subSGF.F.getLoweredFunctionTypeInContext( - subSGF.F.getTypeExpansionContext()); - - auto entry = thunk->begin(); + subSGF.F.getTypeExpansionContext()); auto resultArgTy = subSGF.silConv.getSILType(signature->getSingleResult(), signature, subSGF.F.getTypeExpansionContext()); auto baseArgTy = subSGF.silConv.getSILType(signature->getParameters()[0], signature, subSGF.F.getTypeExpansionContext()); - if (genericEnv) { - resultArgTy = genericEnv->mapTypeIntoContext(SGM.M, resultArgTy); - baseArgTy = genericEnv->mapTypeIntoContext(SGM.M, baseArgTy); - } SILFunctionArgument *resultArg = nullptr; - if (SGM.M.useLoweredAddresses()) { - resultArg = entry->createFunctionArgument(resultArgTy); - } - auto baseArg = entry->createFunctionArgument(baseArgTy); + SILFunctionArgument *baseArg = nullptr; SILValue indexPtrArg; - if (!indexes.empty()) { - auto indexArgTy = - subSGF.silConv.getSILType(signature->getParameters()[1], signature, - subSGF.F.getTypeExpansionContext()); - if (genericEnv) - indexArgTy = genericEnv->mapTypeIntoContext(SGM.M, indexArgTy); - indexPtrArg = entry->createFunctionArgument(indexArgTy); - } - + emitKeyPathThunk(SGM, subSGF, genericEnv, signature, thunk, indexes, + resultArg, resultArgTy, baseArg, baseArgTy, indexPtrArg, + !indexes.empty() ? signature->getParameters()[1] + : SILParameterInfo()); + ArgumentScope scope(subSGF, loc); - auto baseSubstValue = emitKeyPathRValueBase(subSGF, property, loc, baseArg, baseType, subs); @@ -3466,50 +3538,17 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, resultSubst = std::move(resultRValue).getAsSingleValue(subSGF, loc); } - if (resultSubst.getType().getAddressType() != resultArgTy) - resultSubst = subSGF.emitSubstToOrigValue(loc, resultSubst, - AbstractionPattern::getOpaque(), - propertyType); - - if (SGM.M.useLoweredAddresses()) { - resultSubst.forwardInto(subSGF, loc, resultArg); - scope.pop(); - - subSGF.B.createReturn(loc, subSGF.emitEmptyTuple(loc)); - } else { - auto result = resultSubst.forward(subSGF); - scope.pop(); - subSGF.B.createReturn(loc, result); - } - + emitReturn(subSGF, propertyType, resultSubst, resultArgTy, resultArg, scope, + loc); SGM.emitLazyConformancesForFunction(thunk); return thunk; } -static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, - AbstractStorageDecl *property, - SubstitutionMap subs, - GenericEnvironment *genericEnv, - ResilienceExpansion expansion, - ArrayRef indexes, - CanType baseType, - CanType propertyType) { - // If the storage declaration is from a protocol, chase the override chain - // back to the declaration whose setter introduced the witness table - // entry. - if (isa(property->getDeclContext())) { - auto setter = property->getOpaqueAccessor(AccessorKind::Set); - if (!setter->requiresNewWitnessTableEntry()) { - // Find the setter that does have a witness table entry. - auto wtableSetter = - cast(SILDeclRef::getOverriddenWitnessTableEntry(setter)); - - // Substitute the 'Self' type of the base protocol. - subs = SILGenModule::mapSubstitutionsForWitnessOverride( - setter, wtableSetter, subs); - property = wtableSetter->getStorage(); - } - } +static SILFunction *getOrCreateKeyPathSetter( + SILGenModule &SGM, AbstractStorageDecl *property, SubstitutionMap subs, + GenericEnvironment *genericEnv, ResilienceExpansion expansion, + ArrayRef indexes, CanType baseType, CanType propertyType) { + lookupPropertyViaProtocol(property, subs, AccessorKind::Set); auto genericSig = genericEnv ? genericEnv->getGenericSignature().getCanonicalSignature() @@ -3519,131 +3558,66 @@ static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, genericEnv = nullptr; } - // Build the signature of the thunk as expected by the keypath runtime. - auto signature = [&]() { - CanType loweredBaseTy, loweredPropTy; - { - AbstractionPattern opaque = AbstractionPattern::getOpaque(); + auto signature = createKeyPathSignature( + SGM, genericSig, baseType, propertyType, indexes, + SILFunctionType::Representation::KeyPathAccessorSetter, + ParameterConvention::Indirect_In_Guaranteed, property); - loweredBaseTy = SGM.Types.getLoweredRValueType( - TypeExpansionContext::minimal(), opaque, baseType); - loweredPropTy = SGM.Types.getLoweredRValueType( - TypeExpansionContext::minimal(), opaque, propertyType); - } - - auto &C = SGM.getASTContext(); - - auto paramConvention = ParameterConvention::Indirect_In_Guaranteed; - - SmallVector params; - // property value - params.push_back({loweredPropTy, paramConvention}); - // base - params.push_back({loweredBaseTy, - property->isSetterMutating() - ? ParameterConvention::Indirect_Inout - : paramConvention}); - // indexes - if (!indexes.empty()) { - if (indexes.size() > 1) { - SmallVector indicesElements; - for (auto &elt : indexes) { - indicesElements.emplace_back(elt.first); - } - auto indicesTupleTy = TupleType::get(indicesElements, C)->getCanonicalType(); - params.push_back({indicesTupleTy, paramConvention}); - } else { - params.push_back({indexes[0].first, paramConvention}); - } - } - - return SILFunctionType::get( - genericSig, - SILFunctionType::ExtInfo().withRepresentation( - SILFunctionType::Representation::KeyPathAccessorSetter), - SILCoroutineKind::None, ParameterConvention::Direct_Unowned, params, {}, - {}, std::nullopt, SubstitutionMap(), SubstitutionMap(), - SGM.getASTContext()); - }(); - // Mangle the name of the thunk to see if we already created it. auto name = Mangle::ASTMangler(SGM.getASTContext(), property) - .mangleKeyPathSetterThunkHelper(property, genericSig, baseType, - subs, expansion); + .mangleKeyPathSetterThunkHelper(property, genericSig, + baseType, subs, expansion); auto loc = RegularLocation::getAutoGeneratedLocation(); - SILGenFunctionBuilder builder(SGM); - auto thunk = builder.getOrCreateSharedFunction( - loc, name, signature, IsBare, IsNotTransparent, - (expansion == ResilienceExpansion::Minimal - ? IsSerialized - : IsNotSerialized), - ProfileCounter(), IsThunk, IsNotDynamic, IsNotDistributed, - IsNotRuntimeAccessible); + auto thunk = + getOrCreateKeypathThunk(SGM, name, signature, genericEnv, expansion, loc); if (!thunk->empty()) return thunk; - + // Emit the thunk, which accesses the underlying property normally with // reabstraction where necessary. if (genericEnv) { baseType = genericEnv->mapTypeIntoContext(baseType)->getCanonicalType(); - propertyType = genericEnv->mapTypeIntoContext(propertyType) - ->getCanonicalType(); + propertyType = + genericEnv->mapTypeIntoContext(propertyType)->getCanonicalType(); thunk->setGenericEnvironment(genericEnv); } - SILGenFunction subSGF(SGM, *thunk, SGM.SwiftModule); - signature = subSGF.F.getLoweredFunctionTypeInContext( - subSGF.F.getTypeExpansionContext()); - - auto entry = thunk->begin(); + subSGF.F.getTypeExpansionContext()); auto valueArgTy = subSGF.silConv.getSILType(signature->getParameters()[0], signature, subSGF.getTypeExpansionContext()); auto baseArgTy = subSGF.silConv.getSILType(signature->getParameters()[1], signature, subSGF.getTypeExpansionContext()); - if (genericEnv) { - valueArgTy = genericEnv->mapTypeIntoContext(SGM.M, valueArgTy); - baseArgTy = genericEnv->mapTypeIntoContext(SGM.M, baseArgTy); - } - auto valueArg = entry->createFunctionArgument(valueArgTy); - auto baseArg = entry->createFunctionArgument(baseArgTy); + SILFunctionArgument *valueArg = nullptr; + SILFunctionArgument *baseArg = nullptr; SILValue indicesTupleArg; - - if (!indexes.empty()) { - auto indexArgTy = - subSGF.silConv.getSILType(signature->getParameters()[2], signature, - subSGF.getTypeExpansionContext()); - if (genericEnv) - indexArgTy = genericEnv->mapTypeIntoContext(SGM.M, indexArgTy); - indicesTupleArg = entry->createFunctionArgument(indexArgTy); - } + emitKeyPathThunk(SGM, subSGF, genericEnv, signature, thunk, indexes, valueArg, + valueArgTy, baseArg, baseArgTy, indicesTupleArg, + !indexes.empty() ? signature->getParameters()[2] + : SILParameterInfo(), + /*lowerValueArg*/ true); Scope scope(subSGF, loc); - - auto subscriptIndices = - loadIndexValuesForKeyPathComponent(subSGF, loc, property, - indexes, indicesTupleArg); - + auto subscriptIndices = loadIndexValuesForKeyPathComponent( + subSGF, loc, property, indexes, indicesTupleArg); auto valueOrig = valueArgTy.isTrivial(subSGF.F) ? ManagedValue::forRValueWithoutOwnership(valueArg) : ManagedValue::forBorrowedRValue(valueArg); valueOrig = valueOrig.copy(subSGF, loc); - auto valueSubst = subSGF.emitOrigToSubstValue(loc, valueOrig, - AbstractionPattern::getOpaque(), - propertyType); - + auto valueSubst = subSGF.emitOrigToSubstValue( + loc, valueOrig, AbstractionPattern::getOpaque(), propertyType); + LValue lv; if (!property->isSetterMutating()) { - auto baseSubst = emitKeyPathRValueBase(subSGF, property, - loc, baseArg, - baseType, subs); + auto baseSubst = + emitKeyPathRValueBase(subSGF, property, loc, baseArg, baseType, subs); - lv = LValue::forValue(SGFAccessKind::BorrowedObjectRead, - baseSubst, baseType); + lv = LValue::forValue(SGFAccessKind::BorrowedObjectRead, baseSubst, + baseType); } else { auto baseOrig = ManagedValue::forLValue(baseArg); lv = LValue::forAddress(SGFAccessKind::ReadWrite, baseOrig, std::nullopt, @@ -3654,8 +3628,7 @@ static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, auto opened = subs.getReplacementTypes()[0]->castTo(); baseType = opened->getCanonicalType(); lv = subSGF.emitOpenExistentialLValue(loc, std::move(lv), - CanArchetypeType(opened), - baseType, + CanArchetypeType(opened), baseType, SGFAccessKind::ReadWrite); } } @@ -3667,9 +3640,8 @@ static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, LValueOptions lvOptions; lv.addMemberComponent(subSGF, loc, property, subs, lvOptions, - /*super*/ false, SGFAccessKind::Write, - strategy, propertyType, - std::move(subscriptIndices), + /*super*/ false, SGFAccessKind::Write, strategy, + propertyType, std::move(subscriptIndices), /*index for diags*/ nullptr); // If the assigned value will need to be reabstracted, add a reabstraction @@ -3681,13 +3653,12 @@ static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, lv.addOrigToSubstComponent(loweredSubstType); } - subSGF.emitAssignToLValue(loc, - RValue(subSGF, loc, propertyType, valueSubst), - std::move(lv)); + subSGF.emitAssignToLValue(loc, RValue(subSGF, loc, propertyType, valueSubst), + std::move(lv)); scope.pop(); - + subSGF.B.createReturn(loc, subSGF.emitEmptyTuple(loc)); - + SGM.emitLazyConformancesForFunction(thunk); return thunk; } From 91c94a608d618104921f232f32adfb033f72215a Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Fri, 17 Jan 2025 11:21:50 -0800 Subject: [PATCH 18/29] [NFC] Refactor callee formation for reuse. --- lib/SILGen/SILGenApply.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 1c2ce7970e892..c8addd450131d 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -554,6 +554,19 @@ class Callee { subs.mapIntoTypeExpansionContext(SGF.getTypeExpansionContext()), l); } + static Callee formCallee(SILGenFunction &SGF, AbstractFunctionDecl *decl, + CanType protocolSelfType, SILDeclRef declRef, + SubstitutionMap subs, SILLocation loc) { + if (isa(decl->getDeclContext())) { + return Callee::forWitnessMethod(SGF, protocolSelfType, declRef, subs, + loc); + } else if (getMethodDispatch(decl) == MethodDispatch::Class) { + return Callee::forClassMethod(SGF, declRef, subs, loc); + } else { + return Callee::forDirect(SGF, declRef, subs, loc); + } + } + Callee(Callee &&) = default; Callee &operator=(Callee &&) = default; @@ -6656,16 +6669,8 @@ RValue SILGenFunction::emitApplyAllocatingInitializer(SILLocation loc, } // Form the callee. - std::optional callee; - if (isa(ctor->getDeclContext())) { - callee.emplace(Callee::forWitnessMethod( - *this, selfMetaVal.getType().getASTType(), - initRef, subs, loc)); - } else if (getMethodDispatch(ctor) == MethodDispatch::Class) { - callee.emplace(Callee::forClassMethod(*this, initRef, subs, loc)); - } else { - callee.emplace(Callee::forDirect(*this, initRef, subs, loc)); - } + std::optional callee = Callee::formCallee( + *this, ctor, selfMetaVal.getType().getASTType(), initRef, subs, loc); auto substFormalType = callee->getSubstFormalType(); auto resultType = cast(substFormalType.getResult()).getResult(); From e5362c761d9cb1ab632240d501c162cac9f846f6 Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Tue, 21 Jan 2025 16:58:27 -0800 Subject: [PATCH 19/29] [NFC] Refactor subscript argument evaluation for reuse. --- lib/SILGen/SILGenApply.cpp | 50 ++++++++++++++++++------------------- lib/SILGen/SILGenFunction.h | 8 ++++-- lib/SILGen/SILGenLValue.cpp | 3 ++- 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index c8addd450131d..69618e959e637 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -7237,28 +7237,31 @@ static void emitPseudoFunctionArguments(SILGenFunction &SGF, outVals.swap(argValues); } +CanFunctionType SILGenFunction::prepareStorageType(ValueDecl *decl, + SubstitutionMap subs) { + auto computeSubstitutedType = [&](Type interfaceType) -> CanFunctionType { + if (subs) { + if (auto genericFnType = interfaceType->getAs()) { + return cast( + genericFnType->substGenericArgs(subs)->getCanonicalType()); + } + } + return cast(interfaceType->getCanonicalType()); + }; + Type interfaceType; + if (auto *subscript = dyn_cast(decl)) { + // TODO: Use the real abstraction pattern from the accessor(s) in the + // strategy. Currently, the substituted type is used to reconstitute these + // as RValues. + interfaceType = subscript->getInterfaceType(); + } + return computeSubstitutedType(interfaceType); +} + PreparedArguments SILGenFunction::prepareIndices(SILLocation loc, - ValueDecl *decl, - SubstitutionMap subs, + CanFunctionType substFnType, AccessStrategy strategy, ArgumentList *argList) { - // TODO: use the real abstraction pattern from the accessor(s) in the - // strategy. - // Currently we use the substituted type so that we can reconstitute these - // as RValues. - Type interfaceType = decl->getInterfaceType(); - - CanFunctionType substFnType; - if (subs) - substFnType = cast(interfaceType - ->castTo() - ->substGenericArgs(subs) - ->getCanonicalType()); - else - substFnType = cast(interfaceType - ->getCanonicalType()); - - AbstractionPattern origFnType(substFnType); // Prepare the unevaluated index expression. @@ -7908,12 +7911,7 @@ SmallVector SILGenFunction::emitKeyPathOperands(SILLocation loc, ValueDecl *decl, SubstitutionMap subs, ArgumentList *argList) { - Type interfaceType = decl->getInterfaceType(); - CanFunctionType substFnType = - subs ? cast(interfaceType->castTo() - ->substGenericArgs(subs) - ->getCanonicalType()) - : cast(interfaceType->getCanonicalType()); + auto substFnType = prepareStorageType(decl, subs); AbstractionPattern origFnType(substFnType); auto fnType = getLoweredType(origFnType, substFnType).castTo() @@ -7925,7 +7923,7 @@ SILGenFunction::emitKeyPathOperands(SILLocation loc, ValueDecl *decl, ClaimedParamsRef(fnType->getParameters()), argValues, delayedArgs, ForeignInfo{}); - auto prepared = prepareIndices(loc, decl, subs, + auto prepared = prepareIndices(loc, substFnType, // Strategy doesn't matter AccessStrategy::getStorage(), argList); emitter.emitPreparedArgs(std::move(prepared), origFnType); diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 42e6e468e4383..d5e9e83e249a1 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -1933,8 +1933,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction const FunctionTypeInfo &typeContext, SubstitutionMap subs); - PreparedArguments prepareIndices(SILLocation loc, ValueDecl *decl, - SubstitutionMap subs, + /// Get substituted type for a given interface type, optionally apply a + /// substitution map if provided. + CanFunctionType prepareStorageType(ValueDecl *decl, SubstitutionMap subs); + + /// Evaluate and associate arguments with their expressions. + PreparedArguments prepareIndices(SILLocation loc, CanFunctionType substFnType, AccessStrategy strategy, ArgumentList *argList); diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 26594ab73e975..dd796d107d254 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -4286,7 +4286,8 @@ LValue SILGenLValue::visitSubscriptExpr(SubscriptExpr *e, actorIso = getActorIsolation(decl); auto *argList = e->getArgs(); - auto indices = SGF.prepareIndices(e, decl, subs, strategy, argList); + auto storageCanType = SGF.prepareStorageType(decl, subs); + auto indices = SGF.prepareIndices(e, storageCanType, strategy, argList); lv.addMemberSubscriptComponent(SGF, e, decl, subs, options, e->isSuper(), accessKind, strategy, From 43df15e514830fb569a6833256e143986906dbf6 Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Sun, 22 Dec 2024 21:45:07 -0800 Subject: [PATCH 20/29] [SILGen] Add KeyPathComponentKind. --- include/swift/SIL/SILInstruction.h | 81 ++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 1caebe7ae0f87..6ac404710587a 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -3524,17 +3524,18 @@ class KeyPathPatternComponent { return Value.DeclRef; } }; - - enum class Kind: unsigned { + + enum class Kind : unsigned { StoredProperty, GettableProperty, SettableProperty, + Method, TupleElement, OptionalChain, OptionalForce, OptionalWrap, }; - + // Description of a captured index value and its Hashable conformance for a // subscript keypath. struct Index { @@ -3560,6 +3561,7 @@ class KeyPathPatternComponent { return PackedStored; case Kind::GettableProperty: case Kind::SettableProperty: + case Kind::Method: return PackedComputed; case Kind::OptionalChain: case Kind::OptionalForce: @@ -3588,7 +3590,7 @@ class KeyPathPatternComponent { SILFunction *Hash; } IndexEquality; CanType ComponentType; - AbstractStorageDecl *ExternalStorage; + ValueDecl *ExternalStorage; SubstitutionMap ExternalSubstitutions; /// Constructor for stored components @@ -3598,26 +3600,18 @@ class KeyPathPatternComponent { ComponentType(ComponentType) {} /// Constructor for computed components - KeyPathPatternComponent(ComputedPropertyId id, - SILFunction *getter, - SILFunction *setter, - ArrayRef indices, - SILFunction *indicesEqual, - SILFunction *indicesHash, - AbstractStorageDecl *externalStorage, + KeyPathPatternComponent(ComputedPropertyId id, SILFunction *getter, + SILFunction *setter, ArrayRef indices, + SILFunction *indicesEqual, SILFunction *indicesHash, + ValueDecl *externalStorage, SubstitutionMap externalSubstitutions, CanType ComponentType) - : ValueAndKind(getter, PackedComputed), - SetterAndIdKind{setter, id.Kind}, - IdValue{id.Value}, - Indices(indices), - IndexEquality{indicesEqual, indicesHash}, - ComponentType(ComponentType), - ExternalStorage(externalStorage), - ExternalSubstitutions(externalSubstitutions) - { - } - + : ValueAndKind(getter, PackedComputed), + SetterAndIdKind{setter, id.Kind}, IdValue{id.Value}, + Indices(indices), IndexEquality{indicesEqual, indicesHash}, + ComponentType(ComponentType), ExternalStorage(externalStorage), + ExternalSubstitutions(externalSubstitutions) {} + /// Constructor for optional components. KeyPathPatternComponent(Kind kind, CanType componentType) : ValueAndKind((void*)((uintptr_t)kind << KindPackingBits), Unpacked), @@ -3647,9 +3641,21 @@ class KeyPathPatternComponent { case PackedStored: return TupleIndex ? Kind::TupleElement : Kind::StoredProperty; - case PackedComputed: - return SetterAndIdKind.getPointer() - ? Kind::SettableProperty : Kind::GettableProperty; + case PackedComputed: { + if (SetterAndIdKind.getPointer()) { + return Kind::SettableProperty; + } + // Filter out AccessorDecls like subscript getter/setter to only handle + // methods. + auto computedId = ComputedPropertyId(IdValue, SetterAndIdKind.getInt()); + if (computedId.getKind() == ComputedPropertyId::DeclRef) { + auto decl = computedId.getDeclRef().getDecl(); + if (dyn_cast(decl) && !isa(decl)) { + return Kind::Method; + } + } + return Kind::GettableProperty; + } case Unpacked: return (Kind)((uintptr_t)ValueAndKind.getPointer() >> KindPackingBits); } @@ -3666,6 +3672,7 @@ class KeyPathPatternComponent { return static_cast(ValueAndKind.getPointer()); case Kind::GettableProperty: case Kind::SettableProperty: + case Kind::Method: case Kind::OptionalChain: case Kind::OptionalForce: case Kind::OptionalWrap: @@ -3685,6 +3692,7 @@ class KeyPathPatternComponent { llvm_unreachable("not a computed property"); case Kind::GettableProperty: case Kind::SettableProperty: + case Kind::Method: return ComputedPropertyId(IdValue, SetterAndIdKind.getInt()); } @@ -3701,6 +3709,7 @@ class KeyPathPatternComponent { llvm_unreachable("not a computed property"); case Kind::GettableProperty: case Kind::SettableProperty: + case Kind::Method: return static_cast(ValueAndKind.getPointer()); } llvm_unreachable("unhandled kind"); @@ -3710,6 +3719,7 @@ class KeyPathPatternComponent { switch (getKind()) { case Kind::StoredProperty: case Kind::GettableProperty: + case Kind::Method: case Kind::OptionalChain: case Kind::OptionalForce: case Kind::OptionalWrap: @@ -3731,6 +3741,7 @@ class KeyPathPatternComponent { return {}; case Kind::GettableProperty: case Kind::SettableProperty: + case Kind::Method: return Indices; } llvm_unreachable("unhandled kind"); @@ -3746,6 +3757,7 @@ class KeyPathPatternComponent { llvm_unreachable("not a computed property"); case Kind::GettableProperty: case Kind::SettableProperty: + case Kind::Method: return IndexEquality.Equal; } llvm_unreachable("unhandled kind"); @@ -3760,6 +3772,7 @@ class KeyPathPatternComponent { llvm_unreachable("not a computed property"); case Kind::GettableProperty: case Kind::SettableProperty: + case Kind::Method: return IndexEquality.Hash; } llvm_unreachable("unhandled kind"); @@ -3771,8 +3784,8 @@ class KeyPathPatternComponent { CanType ty) { return KeyPathPatternComponent(property, ty); } - - AbstractStorageDecl *getExternalDecl() const { + + ValueDecl *getExternalDecl() const { switch (getKind()) { case Kind::StoredProperty: case Kind::OptionalChain: @@ -3782,6 +3795,7 @@ class KeyPathPatternComponent { llvm_unreachable("not a computed property"); case Kind::GettableProperty: case Kind::SettableProperty: + case Kind::Method: return ExternalStorage; } llvm_unreachable("unhandled kind"); @@ -3797,6 +3811,7 @@ class KeyPathPatternComponent { llvm_unreachable("not a computed property"); case Kind::GettableProperty: case Kind::SettableProperty: + case Kind::Method: return ExternalSubstitutions; } llvm_unreachable("unhandled kind"); @@ -3810,6 +3825,7 @@ class KeyPathPatternComponent { case Kind::OptionalWrap: case Kind::GettableProperty: case Kind::SettableProperty: + case Kind::Method: llvm_unreachable("not a tuple element"); case Kind::TupleElement: return TupleIndex - 1; @@ -3817,6 +3833,16 @@ class KeyPathPatternComponent { llvm_unreachable("unhandled kind"); } + static KeyPathPatternComponent + forMethod(ComputedPropertyId identifier, SILFunction *method, + ArrayRef args, SILFunction *argsEquals, + SILFunction *argsHash, AbstractFunctionDecl *externalDecl, + SubstitutionMap externalSubs, CanType ty) { + return KeyPathPatternComponent(identifier, method, nullptr, args, + argsEquals, argsHash, externalDecl, + externalSubs, ty); + } + static KeyPathPatternComponent forComputedGettableProperty(ComputedPropertyId identifier, SILFunction *getter, @@ -3863,6 +3889,7 @@ class KeyPathPatternComponent { case Kind::StoredProperty: case Kind::GettableProperty: case Kind::SettableProperty: + case Kind::Method: case Kind::TupleElement: llvm_unreachable("not an optional kind"); } From 60e03a85d9a9ebe13a0fab077925f2ab31bf4979 Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Tue, 21 Jan 2025 13:29:58 -0800 Subject: [PATCH 21/29] [SILGen] Lower applied keypath methods. --- include/swift/AST/Decl.h | 12 + lib/AST/Decl.cpp | 44 +++ lib/SILGen/SILGen.h | 21 +- lib/SILGen/SILGenApply.cpp | 35 +- lib/SILGen/SILGenExpr.cpp | 689 +++++++++++++++++++++--------------- lib/SILGen/SILGenFunction.h | 8 +- lib/SILGen/SILGenLValue.cpp | 2 +- 7 files changed, 512 insertions(+), 299 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index f985a5dc053cb..affa10ffca32d 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -8171,6 +8171,18 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// The async function marked as the alternative to this function, if any. AbstractFunctionDecl *getAsyncAlternative() const; + /// True if the storage can be referenced by a keypath directly. + /// Otherwise, its override must be referenced. + bool isValidKeyPathComponent() const; + + /// Do we need to use resilient access patterns outside of this + /// method's resilience domain? + bool isResilient() const; + + /// Do we need to use resilient access patterns when accessing this + /// method from the given module? + bool isResilient(ModuleDecl *M, ResilienceExpansion expansion) const; + /// If \p asyncAlternative is set, then compare its parameters to this /// (presumed synchronous) function's parameters to find the index of the /// completion handler parameter. This should be the only missing diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index f8b6d375b3660..3ae901ab97f3a 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -10377,6 +10377,50 @@ void AbstractFunctionDecl::setParameters(ParameterList *BodyParams) { BodyParams->setDeclContextOfParamDecls(this); } +bool AbstractFunctionDecl::isValidKeyPathComponent() const { + // Check whether we're an ABI compatible override of another method. If we + // are, then the key path should refer to the base decl instead. + auto &ctx = getASTContext(); + auto isABICompatibleOverride = evaluateOrDefault( + ctx.evaluator, + IsABICompatibleOverrideRequest{const_cast(this)}, + false); + return !isABICompatibleOverride; +} + +bool AbstractFunctionDecl::isResilient() const { + // Check for attributes that makes functions non-resilient. + if (getAttrs().hasAttribute() && + getAttrs().hasAttribute()) + return false; + + // If this is a function of a type, check that type's resilience. + if (auto *nominalDecl = getDeclContext()->getSelfNominalTypeDecl()) + return nominalDecl->isResilient(); + + // Functions in non-public access scopes are not resilient. + auto accessScope = + getFormalAccessScope(/*useDC=*/nullptr, + /*treatUsableFromInlineAsPublic=*/true); + if (!accessScope.isPublicOrPackage()) + return false; + + return getModuleContext()->isResilient(); +} + +bool AbstractFunctionDecl::isResilient(ModuleDecl *M, + ResilienceExpansion expansion) const { + switch (expansion) { + case ResilienceExpansion::Minimal: + return isResilient(); + case ResilienceExpansion::Maximal: + if (M == getModuleContext()) + return false; + return isResilient(); + } + llvm_unreachable("bad resilience expansion"); +} + OpaqueTypeDecl::OpaqueTypeDecl(ValueDecl *NamingDecl, GenericParamList *GenericParams, DeclContext *DC, GenericSignature OpaqueInterfaceGenericSignature, diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index 3599eadf8acd7..d6daf6b2fd797 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -420,24 +420,17 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { /// Is the self method of the given nonmutating method passed indirectly? bool isNonMutatingSelfIndirect(SILDeclRef method); - SILDeclRef getAccessorDeclRef(AccessorDecl *accessor, - ResilienceExpansion expansion); + SILDeclRef getFuncDeclRef(FuncDecl *funcDecl, ResilienceExpansion expansion); bool canStorageUseStoredKeyPathComponent(AbstractStorageDecl *decl, ResilienceExpansion expansion); - KeyPathPatternComponent - emitKeyPathComponentForDecl(SILLocation loc, - GenericEnvironment *genericEnv, - ResilienceExpansion expansion, - unsigned &baseOperand, - bool &needsGenericContext, - SubstitutionMap subs, - AbstractStorageDecl *storage, - ArrayRef indexHashables, - CanType baseTy, - DeclContext *useDC, - bool forPropertyDescriptor); + KeyPathPatternComponent emitKeyPathComponentForDecl( + SILLocation loc, GenericEnvironment *genericEnv, + ResilienceExpansion expansion, unsigned &baseOperand, + bool &needsGenericContext, SubstitutionMap subs, ValueDecl *decl, + ArrayRef indexHashables, CanType baseTy, + DeclContext *useDC, bool forPropertyDescriptor); /// Emit all differentiability witnesses for the given function, visiting its /// `@differentiable` and `@derivative` attributes. diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 69618e959e637..8e851aa7ae8fd 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -7254,6 +7254,9 @@ CanFunctionType SILGenFunction::prepareStorageType(ValueDecl *decl, // strategy. Currently, the substituted type is used to reconstitute these // as RValues. interfaceType = subscript->getInterfaceType(); + } else if (auto *func = dyn_cast(decl)) { + interfaceType = + func->getInterfaceType()->castTo()->getResult(); } return computeSubstitutedType(interfaceType); } @@ -7292,14 +7295,14 @@ PreparedArguments SILGenFunction::prepareIndices(SILLocation loc, return result; } -SILDeclRef SILGenModule::getAccessorDeclRef(AccessorDecl *accessor, - ResilienceExpansion expansion) { - auto declRef = SILDeclRef(accessor, SILDeclRef::Kind::Func); +SILDeclRef SILGenModule::getFuncDeclRef(FuncDecl *funcDecl, + ResilienceExpansion expansion) { + auto declRef = SILDeclRef(funcDecl, SILDeclRef::Kind::Func); - if (requiresBackDeploymentThunk(accessor, expansion)) + if (requiresBackDeploymentThunk(funcDecl, expansion)) return declRef.asBackDeploymentKind(SILDeclRef::BackDeploymentKind::Thunk); - return declRef.asForeign(requiresForeignEntryPoint(accessor)); + return declRef.asForeign(requiresForeignEntryPoint(funcDecl)); } /// Emit a call to a getter. @@ -7383,6 +7386,28 @@ void SILGenFunction::emitSetAccessor(SILLocation loc, SILDeclRef set, emission.apply(); } +/// Emit a call to a keypath method +RValue SILGenFunction::emitRValueForKeyPathMethod( + SILLocation loc, ManagedValue base, CanType baseType, + AbstractFunctionDecl *method, Type methodTy, PreparedArguments &&methodArgs, + SubstitutionMap subs, SGFContext C) { + FormalEvaluationScope writebackScope(*this); + + RValue selfRValue(*this, loc, baseType, base); + ArgumentSource selfArg(loc, std::move(selfRValue)); + + std::optional callee = + Callee::formCallee(*this, method, selfArg.getSubstRValueType(), + SILDeclRef(method), subs, loc); + + CallEmission emission(*this, std::move(*callee), std::move(writebackScope)); + emission.addSelfParam(loc, std::move(selfArg), + callee->getSubstFormalType().getParams()[0]); + emission.addCallSite(loc, std::move(methodArgs)); + + return emission.apply(C); +} + /// Emit a call to an addressor. /// /// Returns an l-value managed value. diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 92760ee6fa0b0..5f86fa3193586 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3234,16 +3234,16 @@ static ManagedValue emitKeyPathRValueBase(SILGenFunction &subSGF, using IndexTypePair = std::pair; -/// Helper function to load the captured indexes out of a key path +/// Helper function to load the captured args/indexes out of a key path /// component in order to invoke the accessors on that key path. A component -/// with captured indexes passes down a pointer to those captures to the +/// with captured args/indexes passes down a pointer to those captures to the /// accessor thunks, which we can copy out of to produce values we can pass to /// the real accessor functions. static PreparedArguments loadIndexValuesForKeyPathComponent( SILGenFunction &SGF, SILLocation loc, ValueDecl *storage, ArrayRef indexes, SILValue pointer) { - // If not a subscript, do nothing. - if (!isa(storage)) + // If not a subscript or method, do nothing. + if (!(isa(storage) || isa(storage))) return PreparedArguments(); SmallVector indexParams; @@ -3663,6 +3663,93 @@ static SILFunction *getOrCreateKeyPathSetter( return thunk; } +static SILFunction *getOrCreateKeyPathAppliedMethod( + SILGenModule &SGM, AbstractFunctionDecl *method, SubstitutionMap subs, + GenericEnvironment *genericEnv, ResilienceExpansion expansion, + ArrayRef args, CanType baseType, CanType methodType) { + + // Handle protocol method overrides for key paths + if (isa(method->getDeclContext())) { + if (!method->requiresNewWitnessTableEntry()) { + // Find the method that has a witness table entry + auto wtableMethod = cast( + SILDeclRef::getOverriddenWitnessTableEntry(method)); + + // Substitute the 'Self' type of the base protocol + subs = SILGenModule::mapSubstitutionsForWitnessOverride( + method, wtableMethod, subs); + method = wtableMethod; + } + } + + auto genericSig = + genericEnv ? genericEnv->getGenericSignature().getCanonicalSignature() + : nullptr; + if (genericSig && genericSig->areAllParamsConcrete()) { + genericSig = nullptr; + genericEnv = nullptr; + } + + auto signature = createKeyPathSignature( + SGM, genericSig, baseType, methodType, args, + SILFunctionType::Representation::KeyPathAccessorGetter, + ParameterConvention::Indirect_In_Guaranteed); + + // Mangle the name of the thunk to see if we already created it. + auto name = Mangle::ASTMangler(SGM.getASTContext(), method) + .mangleKeyPathAppliedMethodThunkHelper( + method, genericSig, baseType, subs, expansion); + auto loc = RegularLocation::getAutoGeneratedLocation(); + auto thunk = + getOrCreateKeypathThunk(SGM, name, signature, genericEnv, expansion, loc); + if (!thunk->empty()) + return thunk; + + // Emit the thunk, which accesses the underlying property normally with + // reabstraction where necessary. + if (genericEnv) { + baseType = genericEnv->mapTypeIntoContext(baseType)->getCanonicalType(); + methodType = genericEnv->mapTypeIntoContext(methodType)->getCanonicalType(); + thunk->setGenericEnvironment(genericEnv); + } + SILGenFunction subSGF(SGM, *thunk, SGM.SwiftModule); + signature = subSGF.F.getLoweredFunctionTypeInContext( + subSGF.F.getTypeExpansionContext()); + auto resultArgTy = + subSGF.silConv.getSILType(signature->getSingleResult(), signature, + subSGF.F.getTypeExpansionContext()); + auto baseArgTy = + subSGF.silConv.getSILType(signature->getParameters()[0], signature, + subSGF.F.getTypeExpansionContext()); + SILFunctionArgument *resultArg = nullptr; + SILFunctionArgument *baseArg = nullptr; + SILValue argPtr; + emitKeyPathThunk(SGM, subSGF, genericEnv, signature, thunk, args, resultArg, + resultArgTy, baseArg, baseArgTy, argPtr, + !args.empty() ? signature->getParameters()[1] + : SILParameterInfo()); + + ArgumentScope scope(subSGF, loc); + auto baseSubstValue = + emitKeyPathRValueBase(subSGF, method, loc, baseArg, baseType, subs); + auto preparedArgs = + loadIndexValuesForKeyPathComponent(subSGF, loc, method, args, argPtr); + + ManagedValue resultSubst; + { + RValue resultRValue = subSGF.emitRValueForKeyPathMethod( + loc, baseSubstValue, baseType, method, methodType, + std::move(preparedArgs), subs, SGFContext()); + + resultSubst = std::move(resultRValue).getAsSingleValue(subSGF, loc); + } + + emitReturn(subSGF, methodType, resultSubst, resultArgTy, resultArg, scope, + loc); + SGM.emitLazyConformancesForFunction(thunk); + return thunk; +} + static void getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, SILLocation loc, @@ -4042,8 +4129,8 @@ getIdForKeyPathComponentComputedProperty(SILGenModule &SGM, } case AccessStrategy::DispatchToAccessor: { // Identify the property by its vtable or wtable slot. - return SGM.getAccessorDeclRef(getRepresentativeAccessorForKeyPath(storage), - expansion); + return SGM.getFuncDeclRef(getRepresentativeAccessorForKeyPath(storage), + expansion); } case AccessStrategy::DispatchToDistributedThunk: { @@ -4057,37 +4144,44 @@ getIdForKeyPathComponentComputedProperty(SILGenModule &SGM, llvm_unreachable("unhandled access strategy"); } -static void -lowerKeyPathSubscriptIndexTypes( - SILGenModule &SGM, - SmallVectorImpl &indexPatterns, - SubscriptDecl *subscript, - SubstitutionMap subscriptSubs, - bool &needsGenericContext) { - // Capturing an index value dependent on the generic context means we - // need the generic context captured in the key path. - auto subscriptSubstTy = subscript->getInterfaceType(); - SubstitutionMap subMap; - auto sig = subscript->getGenericSignature(); - if (sig) { - subscriptSubstTy = subscriptSubstTy.subst(subscriptSubs); - } - needsGenericContext |= subscriptSubstTy->hasArchetype(); - - for (auto *index : *subscript->getIndices()) { - auto indexTy = index->getInterfaceType(); - if (sig) { - indexTy = indexTy.subst(subscriptSubs); +static void lowerKeyPathMemberIndexTypes( + SILGenModule &SGM, SmallVectorImpl &indexPatterns, + ValueDecl *decl, SubstitutionMap memberSubs, bool &needsGenericContext) { + auto processIndicesOrParameters = [&](ParameterList *params, + const GenericSignature *sig) { + for (auto *param : *params) { + auto paramTy = param->getInterfaceType(); + if (sig) { + paramTy = paramTy.subst(memberSubs); + } + + auto paramLoweredTy = SGM.Types.getLoweredType( + AbstractionPattern::getOpaque(), paramTy, + TypeExpansionContext::noOpaqueTypeArchetypesSubstitution( + ResilienceExpansion::Minimal)); + paramLoweredTy = paramLoweredTy.mapTypeOutOfContext(); + + indexPatterns.push_back( + {paramTy->mapTypeOutOfContext()->getCanonicalType(), paramLoweredTy}); } + }; - auto indexLoweredTy = SGM.Types.getLoweredType( - AbstractionPattern::getOpaque(), indexTy, - TypeExpansionContext::noOpaqueTypeArchetypesSubstitution( - ResilienceExpansion::Minimal)); - indexLoweredTy = indexLoweredTy.mapTypeOutOfContext(); - indexPatterns.push_back({indexTy->mapTypeOutOfContext() - ->getCanonicalType(), - indexLoweredTy}); + if (auto subscript = dyn_cast(decl)) { + auto subscriptSubstTy = subscript->getInterfaceType(); + auto sig = subscript->getGenericSignature(); + if (sig) { + subscriptSubstTy = subscriptSubstTy.subst(memberSubs); + } + needsGenericContext |= subscriptSubstTy->hasArchetype(); + processIndicesOrParameters(subscript->getIndices(), &sig); + } else if (auto method = dyn_cast(decl)) { + auto methodSubstTy = method->getInterfaceType(); + auto sig = method->getGenericSignature(); + if (sig) { + methodSubstTy = methodSubstTy.subst(memberSubs); + } + needsGenericContext |= methodSubstTy->hasArchetype(); + processIndicesOrParameters(method->getParameters(), &sig); } } @@ -4107,263 +4201,283 @@ static void lowerKeyPathMemberIndexPatterns( } } -KeyPathPatternComponent -SILGenModule::emitKeyPathComponentForDecl(SILLocation loc, - GenericEnvironment *genericEnv, - ResilienceExpansion expansion, - unsigned &baseOperand, - bool &needsGenericContext, - SubstitutionMap subs, - AbstractStorageDecl *storage, - ArrayRef indexHashables, - CanType baseTy, - DeclContext *useDC, - bool forPropertyDescriptor) { - auto baseDecl = storage; - - // ABI-compatible overrides do not have property descriptors, so we need - // to reference the overridden declaration instead. - if (isa(baseDecl->getDeclContext())) { - while (!baseDecl->isValidKeyPathComponent()) - baseDecl = baseDecl->getOverriddenDecl(); - } - - /// Returns true if a key path component for the given property or - /// subscript should be externally referenced. - auto shouldUseExternalKeyPathComponent = [&]() -> bool { - // The property descriptor has the canonical key path component information - // so doesn't have to refer to another external descriptor. - if (forPropertyDescriptor) { - return false; - } - - // Don't need to use the external component if we're inside the resilience - // domain of its defining module. - if (baseDecl->getModuleContext() == SwiftModule - && !baseDecl->isResilient(SwiftModule, expansion)) { - return false; +KeyPathPatternComponent SILGenModule::emitKeyPathComponentForDecl( + SILLocation loc, GenericEnvironment *genericEnv, + ResilienceExpansion expansion, unsigned &baseOperand, + bool &needsGenericContext, SubstitutionMap subs, ValueDecl *decl, + ArrayRef indexHashables, CanType baseTy, + DeclContext *useDC, bool forPropertyDescriptor) { + if (auto *storage = dyn_cast(decl)) { + // ABI-compatible overrides do not have property descriptors, so we need + // to reference the overridden declaration instead. + auto baseDecl = storage; + if (isa(baseDecl->getDeclContext())) { + while (!baseDecl->isValidKeyPathComponent()) + baseDecl = baseDecl->getOverriddenDecl(); } - // Protocol requirements don't have nor need property descriptors. - if (isa(baseDecl->getDeclContext())) { - return false; - } - - // Always-emit-into-client properties can't reliably refer to a property - // descriptor that may not exist in older versions of their home dylib. - // Their definition is also always entirely visible to clients so it isn't - // needed. - if (baseDecl->getAttrs().hasAttribute()) { - return false; - } + AbstractFunctionDecl *externalDecl = nullptr; + SubstitutionMap externalSubs; + CanType componentTy; + auto methodTy = decl->getInterfaceType()->castTo(); + // Otherwise, component type is method type without Self. + auto methodResTy = decl->getInterfaceType()->castTo(); + if (auto genMethodTy = methodResTy->getAs()) + methodResTy = genMethodTy->substGenericArgs(subs); + auto methodInterfaceTy = cast( + methodResTy->mapTypeOutOfContext()->getCanonicalType()); + componentTy = methodInterfaceTy.getResult(); + + SmallVector argTypes; + lowerKeyPathMemberIndexTypes(*this, argTypes, decl, subs, + needsGenericContext); + + SmallVector argPatterns; + SILFunction *argEquals = nullptr, *argHash = nullptr; + lowerKeyPathMemberIndexPatterns(argPatterns, argTypes, indexHashables, + baseOperand); + + getOrCreateKeyPathEqualsAndHash(*this, loc, + needsGenericContext ? genericEnv : nullptr, + expansion, argPatterns, argEquals, argHash); + + auto representative = SILDeclRef(storage, SILDeclRef::Kind::Func, + /*isForeign*/ storage->isImportAsMember()); + auto id = getFunction(representative, NotForDefinition); + + auto func = getOrCreateKeyPathAppliedMethod( + *this, storage, subs, needsGenericContext ? genericEnv : nullptr, + expansion, argTypes, baseTy, componentTy); + + auto argPatternsCopy = getASTContext().AllocateCopy(argPatterns); + return KeyPathPatternComponent::forMethod(id, func, argPatternsCopy, + argEquals, argHash, externalDecl, + externalSubs, componentTy); + } else if (auto *storage = dyn_cast(decl)) { + auto baseDecl = storage; - // Back deployed properties have the same restrictions as - // always-emit-into-client properties. - if (requiresBackDeploymentThunk(baseDecl, expansion)) { - return false; + // ABI-compatible overrides do not have property descriptors, so we need + // to reference the overridden declaration instead. + if (isa(baseDecl->getDeclContext())) { + while (!baseDecl->isValidKeyPathComponent()) + baseDecl = baseDecl->getOverriddenDecl(); } - // Properties that only dispatch via ObjC lookup do not have nor - // need property descriptors, since the selector identifies the - // storage. - // Properties that are not public don't need property descriptors - // either. - if (baseDecl->requiresOpaqueAccessors()) { - auto representative = getAccessorDeclRef( - getRepresentativeAccessorForKeyPath(baseDecl), expansion); - if (representative.isForeign) + /// Returns true if a key path component for the given property or + /// subscript should be externally referenced. + auto shouldUseExternalKeyPathComponent = [&]() -> bool { + // The property descriptor has the canonical key path component + // information so doesn't have to refer to another external descriptor. + if (forPropertyDescriptor) { return false; + } - switch (representative.getLinkage(ForDefinition)) { - case SILLinkage::Public: - case SILLinkage::PublicNonABI: - case SILLinkage::Package: - case SILLinkage::PackageNonABI: - break; - case SILLinkage::Hidden: - case SILLinkage::Shared: - case SILLinkage::Private: - case SILLinkage::PublicExternal: - case SILLinkage::PackageExternal: - case SILLinkage::HiddenExternal: + // Don't need to use the external component if we're inside the resilience + // domain of its defining module. + if (baseDecl->getModuleContext() == SwiftModule && + !baseDecl->isResilient(SwiftModule, expansion)) { return false; } - } - - return true; - }; - auto strategy = storage->getAccessStrategy( - AccessSemantics::Ordinary, - storage->supportsMutation() ? AccessKind::ReadWrite : AccessKind::Read, - M.getSwiftModule(), expansion, - /*useOldABI=*/false); + // Protocol requirements don't have nor need property descriptors. + if (isa(baseDecl->getDeclContext())) { + return false; + } - AbstractStorageDecl *externalDecl = nullptr; - SubstitutionMap externalSubs; - - if (shouldUseExternalKeyPathComponent()) { - externalDecl = storage; - // Map the substitutions out of context. - if (!subs.empty()) { - externalSubs = subs; - // If any of the substitutions involve primary archetypes, then the - // key path pattern needs to capture the generic context, and we need - // to map the pattern substitutions out of this context. - if (externalSubs.getRecursiveProperties().hasArchetype()) { - needsGenericContext = true; - // FIXME: This doesn't do anything for local archetypes! - externalSubs = externalSubs.mapReplacementTypesOutOfContext(); + // Always-emit-into-client properties can't reliably refer to a property + // descriptor that may not exist in older versions of their home dylib. + // Their definition is also always entirely visible to clients so it isn't + // needed. + if (baseDecl->getAttrs().hasAttribute()) { + return false; } - } - // ABI-compatible overrides do not have property descriptors, so we need - // to reference the overridden declaration instead. - if (baseDecl != externalDecl) { - externalSubs = SubstitutionMap::getOverrideSubstitutions(baseDecl, - externalDecl) - .subst(externalSubs); - externalDecl = baseDecl; - } - } - - auto isSettableInComponent = [&]() -> bool { - // For storage we reference by a property descriptor, the descriptor will - // supply the settability if needed. We only reference it here if the - // setter is public. - if (shouldUseExternalKeyPathComponent()) - return storage->isSettableInSwift(useDC) && - storage->isSetterAccessibleFrom(useDC); - return storage->isSettableInSwift(storage->getDeclContext()); - }; + // Back deployed properties have the same restrictions as + // always-emit-into-client properties. + if (requiresBackDeploymentThunk(baseDecl, expansion)) { + return false; + } - if (auto var = dyn_cast(storage)) { - CanType componentTy; - if (!var->getDeclContext()->isTypeContext()) { - componentTy = var->getInterfaceType()->getCanonicalType(); - } else if (var->getDeclContext()->getSelfProtocolDecl() && - baseTy->isExistentialType()) { - componentTy = var->getValueInterfaceType()->getCanonicalType(); - ASSERT(!componentTy->hasTypeParameter()); - } else { - // The mapTypeIntoContext() / mapTypeOutOfContext() dance is there - // to handle the case where baseTy being a type parameter subject - // to a superclass requirement. - componentTy = - var->getValueInterfaceType() - .subst(GenericEnvironment::mapTypeIntoContext( - genericEnv, baseTy->getMetatypeInstanceType()) - ->getContextSubstitutionMap(var->getDeclContext())) - ->mapTypeOutOfContext() - ->getCanonicalType(); - } + // Properties that only dispatch via ObjC lookup do not have nor + // need property descriptors, since the selector identifies the + // storage. + // Properties that are not public don't need property descriptors + // either. + if (baseDecl->requiresOpaqueAccessors()) { + auto representative = getFuncDeclRef( + getRepresentativeAccessorForKeyPath(baseDecl), expansion); + if (representative.isForeign) + return false; + + switch (representative.getLinkage(ForDefinition)) { + case SILLinkage::Public: + case SILLinkage::PublicNonABI: + case SILLinkage::Package: + case SILLinkage::PackageNonABI: + break; + case SILLinkage::Hidden: + case SILLinkage::Shared: + case SILLinkage::Private: + case SILLinkage::PublicExternal: + case SILLinkage::PackageExternal: + case SILLinkage::HiddenExternal: + return false; + } + } - // The component type for an @objc optional requirement needs to be - // wrapped in an optional. - if (var->getAttrs().hasAttribute()) { - componentTy = OptionalType::get(componentTy)->getCanonicalType(); - } - - if (canStorageUseStoredKeyPathComponent(var, expansion)) { - return KeyPathPatternComponent::forStoredProperty(var, componentTy); - } + return true; + }; - // We need thunks to bring the getter and setter to the right signature - // expected by the key path runtime. - auto id = getIdForKeyPathComponentComputedProperty(*this, var, expansion, - strategy); - auto getter = getOrCreateKeyPathGetter(*this, - var, subs, - needsGenericContext ? genericEnv : nullptr, - expansion, {}, baseTy, componentTy); - - if (isSettableInComponent()) { - auto setter = getOrCreateKeyPathSetter(*this, - var, subs, - needsGenericContext ? genericEnv : nullptr, - expansion, {}, baseTy, componentTy); - return KeyPathPatternComponent::forComputedSettableProperty(id, - getter, setter, {}, nullptr, nullptr, - externalDecl, externalSubs, componentTy); - } else { - return KeyPathPatternComponent::forComputedGettableProperty(id, - getter, {}, nullptr, nullptr, - externalDecl, externalSubs, componentTy); + auto strategy = storage->getAccessStrategy( + AccessSemantics::Ordinary, + storage->supportsMutation() ? AccessKind::ReadWrite : AccessKind::Read, + M.getSwiftModule(), expansion, + /*useOldABI=*/false); + + AbstractStorageDecl *externalDecl = nullptr; + SubstitutionMap externalSubs; + + if (shouldUseExternalKeyPathComponent()) { + externalDecl = storage; + // Map the substitutions out of context. + if (!subs.empty()) { + externalSubs = subs; + // If any of the substitutions involve primary archetypes, then the + // key path pattern needs to capture the generic context, and we need + // to map the pattern substitutions out of this context. + if (externalSubs.getRecursiveProperties().hasArchetype()) { + needsGenericContext = true; + // FIXME: This doesn't do anything for local archetypes! + externalSubs = externalSubs.mapReplacementTypesOutOfContext(); + } + } + + // ABI-compatible overrides do not have property descriptors, so we need + // to reference the overridden declaration instead. + if (baseDecl != externalDecl) { + externalSubs = + SubstitutionMap::getOverrideSubstitutions(baseDecl, externalDecl) + .subst(externalSubs); + externalDecl = baseDecl; + } } - } - - if (auto decl = dyn_cast(storage)) { - auto baseSubscriptTy = - decl->getInterfaceType()->castTo(); - if (auto genSubscriptTy = baseSubscriptTy->getAs()) - baseSubscriptTy = genSubscriptTy->substGenericArgs(subs); - auto baseSubscriptInterfaceTy = cast( - baseSubscriptTy->mapTypeOutOfContext()->getCanonicalType()); - - auto componentTy = baseSubscriptInterfaceTy.getResult(); - if (decl->getAttrs().hasAttribute()) { + + auto isSettableInComponent = [&]() -> bool { + // For storage we reference by a property descriptor, the descriptor will + // supply the settability if needed. We only reference it here if the + // setter is public. + if (shouldUseExternalKeyPathComponent()) + return storage->isSettableInSwift(useDC) && + storage->isSetterAccessibleFrom(useDC); + return storage->isSettableInSwift(storage->getDeclContext()); + }; + + if (auto var = dyn_cast(storage)) { + CanType componentTy; + if (!var->getDeclContext()->isTypeContext()) { + componentTy = var->getInterfaceType()->getCanonicalType(); + } else if (var->getDeclContext()->getSelfProtocolDecl() && + baseTy->isExistentialType()) { + componentTy = var->getValueInterfaceType()->getCanonicalType(); + ASSERT(!componentTy->hasTypeParameter()); + } else { + // The mapTypeIntoContext() / mapTypeOutOfContext() dance is there + // to handle the case where baseTy being a type parameter subject + // to a superclass requirement. + componentTy = + var->getValueInterfaceType() + .subst(GenericEnvironment::mapTypeIntoContext( + genericEnv, baseTy->getMetatypeInstanceType()) + ->getContextSubstitutionMap(var->getDeclContext())) + ->mapTypeOutOfContext() + ->getCanonicalType(); + } + // The component type for an @objc optional requirement needs to be - // wrapped in an optional - componentTy = OptionalType::get(componentTy)->getCanonicalType(); - } + // wrapped in an optional. + if (var->getAttrs().hasAttribute()) { + componentTy = OptionalType::get(componentTy)->getCanonicalType(); + } - SmallVector indexTypes; - lowerKeyPathSubscriptIndexTypes(*this, indexTypes, - decl, subs, - needsGenericContext); - - SmallVector indexPatterns; - SILFunction *indexEquals = nullptr, *indexHash = nullptr; - // Property descriptors get their index information from the client. - if (!forPropertyDescriptor) { - lowerKeyPathMemberIndexPatterns(indexPatterns, - indexTypes, indexHashables, - baseOperand); - - getOrCreateKeyPathEqualsAndHash(*this, loc, - needsGenericContext ? genericEnv : nullptr, - expansion, - indexPatterns, - indexEquals, indexHash); + if (canStorageUseStoredKeyPathComponent(var, expansion)) { + return KeyPathPatternComponent::forStoredProperty(var, componentTy); + } + + // We need thunks to bring the getter and setter to the right signature + // expected by the key path runtime. + auto id = getIdForKeyPathComponentComputedProperty(*this, var, expansion, + strategy); + auto getter = getOrCreateKeyPathGetter( + *this, var, subs, needsGenericContext ? genericEnv : nullptr, + expansion, {}, baseTy, componentTy); + + if (isSettableInComponent()) { + auto setter = getOrCreateKeyPathSetter( + *this, var, subs, needsGenericContext ? genericEnv : nullptr, + expansion, {}, baseTy, componentTy); + return KeyPathPatternComponent::forComputedSettableProperty( + id, getter, setter, {}, nullptr, nullptr, externalDecl, + externalSubs, componentTy); + } else { + return KeyPathPatternComponent::forComputedGettableProperty( + id, getter, {}, nullptr, nullptr, externalDecl, externalSubs, + componentTy); + } } - auto id = getIdForKeyPathComponentComputedProperty(*this, decl, expansion, - strategy); - auto getter = getOrCreateKeyPathGetter(*this, - decl, subs, - needsGenericContext ? genericEnv : nullptr, - expansion, - indexTypes, - baseTy, componentTy); - - auto indexPatternsCopy = getASTContext().AllocateCopy(indexPatterns); - if (isSettableInComponent()) { - auto setter = getOrCreateKeyPathSetter(*this, - decl, subs, - needsGenericContext ? genericEnv : nullptr, - expansion, - indexTypes, - baseTy, componentTy); - return KeyPathPatternComponent::forComputedSettableProperty(id, - getter, setter, - indexPatternsCopy, - indexEquals, - indexHash, - externalDecl, - externalSubs, - componentTy); - } else { - return KeyPathPatternComponent::forComputedGettableProperty(id, - getter, - indexPatternsCopy, - indexEquals, - indexHash, - externalDecl, - externalSubs, - componentTy); + if (auto decl = dyn_cast(storage)) { + auto baseSubscriptTy = + decl->getInterfaceType()->castTo(); + if (auto genSubscriptTy = baseSubscriptTy->getAs()) + baseSubscriptTy = genSubscriptTy->substGenericArgs(subs); + auto baseSubscriptInterfaceTy = cast( + baseSubscriptTy->mapTypeOutOfContext()->getCanonicalType()); + + auto componentTy = baseSubscriptInterfaceTy.getResult(); + if (decl->getAttrs().hasAttribute()) { + // The component type for an @objc optional requirement needs to be + // wrapped in an optional + componentTy = OptionalType::get(componentTy)->getCanonicalType(); + } + + SmallVector indexTypes; + lowerKeyPathMemberIndexTypes(*this, indexTypes, decl, subs, + needsGenericContext); + + SmallVector indexPatterns; + SILFunction *indexEquals = nullptr, *indexHash = nullptr; + // Property descriptors get their index information from the client. + if (!forPropertyDescriptor) { + lowerKeyPathMemberIndexPatterns(indexPatterns, indexTypes, + indexHashables, baseOperand); + + getOrCreateKeyPathEqualsAndHash( + *this, loc, needsGenericContext ? genericEnv : nullptr, expansion, + indexPatterns, indexEquals, indexHash); + } + + auto id = getIdForKeyPathComponentComputedProperty(*this, decl, expansion, + strategy); + auto getter = getOrCreateKeyPathGetter( + *this, decl, subs, needsGenericContext ? genericEnv : nullptr, + expansion, indexTypes, baseTy, componentTy); + + auto indexPatternsCopy = getASTContext().AllocateCopy(indexPatterns); + if (isSettableInComponent()) { + auto setter = getOrCreateKeyPathSetter( + *this, decl, subs, needsGenericContext ? genericEnv : nullptr, + expansion, indexTypes, baseTy, componentTy); + return KeyPathPatternComponent::forComputedSettableProperty( + id, getter, setter, indexPatternsCopy, indexEquals, indexHash, + externalDecl, externalSubs, componentTy); + } else { + return KeyPathPatternComponent::forComputedGettableProperty( + id, getter, indexPatternsCopy, indexEquals, indexHash, externalDecl, + externalSubs, componentTy); + } } } - + llvm_unreachable("unknown kind of storage"); } @@ -4388,27 +4502,38 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { auto baseTy = rootTy; SmallVector operands; - for (auto &component : E->getComponents()) { + auto components = E->getComponents(); + for (size_t i = 0; i < components.size(); i++) { + auto &component = components[i]; + switch (auto kind = component.getKind()) { case KeyPathExpr::Component::Kind::Member: case KeyPathExpr::Component::Kind::Subscript: { - auto decl = cast(component.getDeclRef().getDecl()); + auto decl = component.getDeclRef().getDecl(); + + // If method is applied, get args from subsequent Apply component. + auto argComponent = components[i]; + if (auto func = dyn_cast(decl); + func && i + 1 < components.size() && + components[i + 1].getKind() == KeyPathExpr::Component::Kind::Apply) { + argComponent = components[i + 1]; + } unsigned numOperands = operands.size(); loweredComponents.push_back(SGF.SGM.emitKeyPathComponentForDecl( SILLocation(E), SGF.F.getGenericEnvironment(), SGF.F.getResilienceExpansion(), numOperands, needsGenericContext, component.getDeclRef().getSubstitutions(), decl, - component.getIndexHashableConformances(), baseTy, SGF.FunctionDC, + argComponent.getIndexHashableConformances(), baseTy, SGF.FunctionDC, /*for descriptor*/ false)); baseTy = loweredComponents.back().getComponentType(); - if (kind == KeyPathExpr::Component::Kind::Member) + if (kind == KeyPathExpr::Component::Kind::Member && + !dyn_cast(decl)) break; - auto subscript = cast(decl); auto loweredArgs = SGF.emitKeyPathOperands( - E, subscript, component.getDeclRef().getSubstitutions(), - component.getArgs()); + E, decl, component.getDeclRef().getSubstitutions(), + argComponent.getArgs()); for (auto &arg : loweredArgs) { operands.push_back(arg.forward(SGF)); @@ -4417,6 +4542,12 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { break; } + case KeyPathExpr::Component::Kind::Apply: { + // Apply arguments for methods are handled above in + // Component::Kind::Member + break; + } + case KeyPathExpr::Component::Kind::TupleElement: { assert(baseTy->is() && "baseTy is expected to be a TupleType"); @@ -4464,6 +4595,7 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { case KeyPathExpr::Component::Kind::Invalid: case KeyPathExpr::Component::Kind::UnresolvedMember: case KeyPathExpr::Component::Kind::UnresolvedSubscript: + case KeyPathExpr::Component::Kind::UnresolvedApply: case KeyPathExpr::Component::Kind::CodeCompletion: llvm_unreachable("not resolved"); break; @@ -4473,12 +4605,12 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { break; } } - + StringRef objcString; if (auto objcExpr = dyn_cast_or_null (E->getObjCStringLiteralExpr())) objcString = objcExpr->getValue(); - + auto pattern = KeyPathPattern::get(SGF.SGM.M, needsGenericContext ? SGF.F.getLoweredFunctionType() @@ -4494,6 +4626,7 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { operands, loweredTy); auto value = SGF.emitManagedRValueWithCleanup(keyPath); + return RValue(SGF, E, value); } diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index d5e9e83e249a1..a3ec76dc58a8a 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -1961,6 +1961,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction ArgumentSource &&value, bool isOnSelfParameter); + RValue emitRValueForKeyPathMethod(SILLocation loc, ManagedValue base, + CanType baseFormalType, + AbstractFunctionDecl *method, Type methodTy, + PreparedArguments &&methodArgs, + SubstitutionMap subs, SGFContext C); + ManagedValue emitAsyncLetStart(SILLocation loc, SILValue taskOptions, AbstractClosureExpr *asyncLetEntryPoint, @@ -3007,7 +3013,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// Returns the SILDeclRef to use for references to the given accessor. SILDeclRef getAccessorDeclRef(AccessorDecl *accessor) { - return SGM.getAccessorDeclRef(accessor, F.getResilienceExpansion()); + return SGM.getFuncDeclRef(accessor, F.getResilienceExpansion()); } /// Given a lowered pack expansion type, produce a generic environment diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index dd796d107d254..0f174edce9300 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -3877,7 +3877,7 @@ static SGFAccessKind getBaseAccessKindForAccessor(SILGenModule &SGM, if (accessor->isMutating()) return SGFAccessKind::ReadWrite; - auto declRef = SGM.getAccessorDeclRef(accessor, ResilienceExpansion::Minimal); + auto declRef = SGM.getFuncDeclRef(accessor, ResilienceExpansion::Minimal); if (shouldEmitSelfAsRValue(accessor, baseFormalType, forBorrowExpr)) { return SGM.isNonMutatingSelfIndirect(declRef) ? SGFAccessKind::OwnedAddressRead From 4479996d7b458240b81955e5e9130ffaf72e18b5 Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Tue, 21 Jan 2025 21:21:29 -0800 Subject: [PATCH 22/29] [NFC] Refactor method thunk handling for reuse. --- lib/SILGen/SILGenExpr.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 5f86fa3193586..9fd7655cb9666 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3663,12 +3663,10 @@ static SILFunction *getOrCreateKeyPathSetter( return thunk; } -static SILFunction *getOrCreateKeyPathAppliedMethod( - SILGenModule &SGM, AbstractFunctionDecl *method, SubstitutionMap subs, - GenericEnvironment *genericEnv, ResilienceExpansion expansion, - ArrayRef args, CanType baseType, CanType methodType) { - - // Handle protocol method overrides for key paths +/// For keypaths to methods defined in protocols, use the decl defined in the +/// conforming type's implementation. +static void lookupMethodViaProtocol(AbstractFunctionDecl *&method, + SubstitutionMap &subs) { if (isa(method->getDeclContext())) { if (!method->requiresNewWitnessTableEntry()) { // Find the method that has a witness table entry @@ -3681,6 +3679,13 @@ static SILFunction *getOrCreateKeyPathAppliedMethod( method = wtableMethod; } } +} + +static SILFunction *getOrCreateKeyPathAppliedMethod( + SILGenModule &SGM, AbstractFunctionDecl *method, SubstitutionMap subs, + GenericEnvironment *genericEnv, ResilienceExpansion expansion, + ArrayRef args, CanType baseType, CanType methodType) { + lookupMethodViaProtocol(method, subs); auto genericSig = genericEnv ? genericEnv->getGenericSignature().getCanonicalSignature() From 45a7b45ad977e6b3b4402e20e80c8ad75b815fe9 Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Fri, 20 Dec 2024 00:35:33 -0800 Subject: [PATCH 23/29] [SILGen] Lower unapplied methods. --- lib/SILGen/SILGen.h | 2 +- lib/SILGen/SILGenApply.cpp | 52 ++++++++++++ lib/SILGen/SILGenExpr.cpp | 162 ++++++++++++++++++++++++++++++------ lib/SILGen/SILGenFunction.h | 6 ++ 4 files changed, 197 insertions(+), 25 deletions(-) diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index d6daf6b2fd797..aee42f2f7a8ee 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -430,7 +430,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { ResilienceExpansion expansion, unsigned &baseOperand, bool &needsGenericContext, SubstitutionMap subs, ValueDecl *decl, ArrayRef indexHashables, CanType baseTy, - DeclContext *useDC, bool forPropertyDescriptor); + DeclContext *useDC, bool forPropertyDescriptor, bool isApplied = false); /// Emit all differentiability witnesses for the given function, visiting its /// `@differentiable` and `@derivative` attributes. diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 8e851aa7ae8fd..0cb333a86d5b7 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -7408,6 +7408,58 @@ RValue SILGenFunction::emitRValueForKeyPathMethod( return emission.apply(C); } +/// Emit a call to an unapplied keypath method. +RValue SILGenFunction::emitUnappliedKeyPathMethod( + SILLocation loc, ManagedValue base, CanType baseType, + AbstractFunctionDecl *method, Type methodTy, PreparedArguments &&methodArgs, + SubstitutionMap subs, SGFContext C) { + FormalEvaluationScope writebackScope(*this); + + RValue selfRValue(*this, loc, baseType, base); + ArgumentSource selfArg(loc, std::move(selfRValue)); + + std::optional callee = + Callee::formCallee(*this, method, selfArg.getSubstRValueType(), + SILDeclRef(method), subs, loc); + + SILValue methodRef; + if (callee) { + SILDeclRef calleeMethod = callee->getMethodName(); + auto &constantInfo = + SGM.Types.getConstantInfo(F.getTypeExpansionContext(), calleeMethod); + SILType methodTy = constantInfo.getSILType(); + + switch (callee->kind) { + case Callee::Kind::StandaloneFunction: { + SILFunction *silMethod = SGM.getFunction(calleeMethod, NotForDefinition); + methodRef = B.createFunctionRef(loc, silMethod); + break; + } + case Callee::Kind::ClassMethod: { + methodRef = + B.createClassMethod(loc, base.getValue(), calleeMethod, methodTy); + break; + } + case Callee::Kind::WitnessMethod: { + CanType protocolSelfType = baseType->getCanonicalType(); + ProtocolDecl *protocol = cast(method->getDeclContext()); + ProtocolConformanceRef conformance = + subs.lookupConformance(protocolSelfType, protocol); + methodRef = B.createWitnessMethod(loc, protocolSelfType, conformance, + calleeMethod, methodTy); + break; + } + default: + llvm_unreachable("Unhandled callee kind for unapplied key path method."); + } + } + + auto partialMV = B.createPartialApply(loc, methodRef, subs, base, + ParameterConvention::Direct_Guaranteed); + CanType partialType = methodTy->getCanonicalType(); + return RValue(*this, loc, partialType, partialMV); +} + /// Emit a call to an addressor. /// /// Returns an l-value managed value. diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 9fd7655cb9666..2d6cb84626cdc 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3755,6 +3755,96 @@ static SILFunction *getOrCreateKeyPathAppliedMethod( return thunk; } +static SILFunction *getOrCreateUnappliedKeypathMethod( + SILGenModule &SGM, AbstractFunctionDecl *method, SubstitutionMap subs, + GenericEnvironment *genericEnv, ResilienceExpansion expansion, + ArrayRef args, CanType baseType, CanType methodType) { + lookupMethodViaProtocol(method, subs); + + auto genericSig = + genericEnv ? genericEnv->getGenericSignature().getCanonicalSignature() + : nullptr; + if (genericSig && genericSig->areAllParamsConcrete()) { + genericSig = nullptr; + genericEnv = nullptr; + } + + // Build the thunk signature for an unapplied method. + auto signature = [&]() { + CanType loweredBaseTy = SGM.Types.getLoweredRValueType( + TypeExpansionContext::minimal(), AbstractionPattern::getOpaque(), + baseType); + CanType loweredMethodTy = SGM.Types.getLoweredRValueType( + TypeExpansionContext::minimal(), AbstractionPattern::getOpaque(), + methodType); + + return SILFunctionType::get( + genericSig, + SILFunctionType::ExtInfo().withRepresentation( + SILFunctionType::Representation::KeyPathAccessorGetter), + SILCoroutineKind::None, ParameterConvention::Direct_Unowned, + {SILParameterInfo(loweredBaseTy, + ParameterConvention::Indirect_In_Guaranteed)}, + {}, SILResultInfo(loweredMethodTy, ResultConvention::Indirect), + std::nullopt, SubstitutionMap(), SubstitutionMap(), + SGM.getASTContext()); + }(); + + // Mangle the name of the thunk to see if we already created it. + auto name = Mangle::ASTMangler(SGM.getASTContext(), method) + .mangleKeyPathUnappliedMethodThunkHelper( + method, genericSig, baseType, subs, expansion); + auto loc = RegularLocation::getAutoGeneratedLocation(); + auto thunk = + getOrCreateKeypathThunk(SGM, name, signature, genericEnv, expansion, loc); + if (!thunk->empty()) + return thunk; + + // Emit the thunk, which accesses the underlying property normally with + // reabstraction where necessary. + if (genericEnv) { + baseType = genericEnv->mapTypeIntoContext(baseType)->getCanonicalType(); + methodType = genericEnv->mapTypeIntoContext(methodType)->getCanonicalType(); + thunk->setGenericEnvironment(genericEnv); + } + SILGenFunction subSGF(SGM, *thunk, SGM.SwiftModule); + signature = subSGF.F.getLoweredFunctionTypeInContext( + subSGF.F.getTypeExpansionContext()); + auto resultArgTy = + subSGF.silConv.getSILType(signature->getSingleResult(), signature, + subSGF.F.getTypeExpansionContext()); + auto baseArgTy = + subSGF.silConv.getSILType(signature->getParameters()[0], signature, + subSGF.F.getTypeExpansionContext()); + SILFunctionArgument *resultArg = nullptr; + SILFunctionArgument *baseArg = nullptr; + SILValue argPtr; + emitKeyPathThunk(SGM, subSGF, genericEnv, signature, thunk, args, resultArg, + resultArgTy, baseArg, baseArgTy, argPtr, + !args.empty() ? signature->getParameters()[1] + : SILParameterInfo()); + + ArgumentScope scope(subSGF, loc); + auto baseSubstValue = + emitKeyPathRValueBase(subSGF, method, loc, baseArg, baseType, subs); + auto preparedArgs = + loadIndexValuesForKeyPathComponent(subSGF, loc, method, args, argPtr); + + ManagedValue resultSubst; + { + RValue resultRValue = subSGF.emitUnappliedKeyPathMethod( + loc, baseSubstValue, baseType, method, methodType, + std::move(preparedArgs), subs, SGFContext()); + + resultSubst = std::move(resultRValue).getAsSingleValue(subSGF, loc); + } + + emitReturn(subSGF, methodType, resultSubst, resultArgTy, resultArg, scope, + loc); + SGM.emitLazyConformancesForFunction(thunk); + return thunk; +} + static void getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, SILLocation loc, @@ -4211,7 +4301,7 @@ KeyPathPatternComponent SILGenModule::emitKeyPathComponentForDecl( ResilienceExpansion expansion, unsigned &baseOperand, bool &needsGenericContext, SubstitutionMap subs, ValueDecl *decl, ArrayRef indexHashables, CanType baseTy, - DeclContext *useDC, bool forPropertyDescriptor) { + DeclContext *useDC, bool forPropertyDescriptor, bool isApplied) { if (auto *storage = dyn_cast(decl)) { // ABI-compatible overrides do not have property descriptors, so we need // to reference the overridden declaration instead. @@ -4225,34 +4315,55 @@ KeyPathPatternComponent SILGenModule::emitKeyPathComponentForDecl( SubstitutionMap externalSubs; CanType componentTy; auto methodTy = decl->getInterfaceType()->castTo(); - // Otherwise, component type is method type without Self. - auto methodResTy = decl->getInterfaceType()->castTo(); - if (auto genMethodTy = methodResTy->getAs()) - methodResTy = genMethodTy->substGenericArgs(subs); - auto methodInterfaceTy = cast( - methodResTy->mapTypeOutOfContext()->getCanonicalType()); - componentTy = methodInterfaceTy.getResult(); + if (isApplied) { + // If method is applied, set component type to method result type. + auto methodResultTy = + methodTy->getResult()->castTo()->getResult(); + if (auto genMethodTy = methodResultTy->getAs()) + methodResultTy = genMethodTy->substGenericArgs(subs); + componentTy = methodResultTy->mapTypeOutOfContext()->getCanonicalType(); + } else { + // Otherwise, component type is method type without Self. + if (auto genMethodTy = methodTy->getAs()) + methodTy = genMethodTy->substGenericArgs(subs); + auto methodInterfaceTy = cast( + methodTy->mapTypeOutOfContext()->getCanonicalType()); + componentTy = methodInterfaceTy.getResult(); + } - SmallVector argTypes; - lowerKeyPathMemberIndexTypes(*this, argTypes, decl, subs, - needsGenericContext); + if (decl->getAttrs().hasAttribute()) { + // The component type for an @objc optional requirement needs to be + // wrapped in an optional + componentTy = OptionalType::get(componentTy)->getCanonicalType(); + } + SmallVector argTypes; SmallVector argPatterns; SILFunction *argEquals = nullptr, *argHash = nullptr; - lowerKeyPathMemberIndexPatterns(argPatterns, argTypes, indexHashables, - baseOperand); - - getOrCreateKeyPathEqualsAndHash(*this, loc, - needsGenericContext ? genericEnv : nullptr, - expansion, argPatterns, argEquals, argHash); + if (isApplied) { + lowerKeyPathMemberIndexTypes(*this, argTypes, decl, subs, + needsGenericContext); + lowerKeyPathMemberIndexPatterns(argPatterns, argTypes, indexHashables, + baseOperand); + getOrCreateKeyPathEqualsAndHash( + *this, loc, needsGenericContext ? genericEnv : nullptr, expansion, + argPatterns, argEquals, argHash); + } auto representative = SILDeclRef(storage, SILDeclRef::Kind::Func, /*isForeign*/ storage->isImportAsMember()); auto id = getFunction(representative, NotForDefinition); - auto func = getOrCreateKeyPathAppliedMethod( - *this, storage, subs, needsGenericContext ? genericEnv : nullptr, - expansion, argTypes, baseTy, componentTy); + SILFunction *func = nullptr; + if (isApplied) { + func = getOrCreateKeyPathAppliedMethod( + *this, storage, subs, needsGenericContext ? genericEnv : nullptr, + expansion, argTypes, baseTy, componentTy); + } else { + func = getOrCreateUnappliedKeypathMethod( + *this, storage, subs, needsGenericContext ? genericEnv : nullptr, + expansion, argTypes, baseTy, componentTy); + } auto argPatternsCopy = getASTContext().AllocateCopy(argPatterns); return KeyPathPatternComponent::forMethod(id, func, argPatternsCopy, @@ -4517,11 +4628,13 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { auto decl = component.getDeclRef().getDecl(); // If method is applied, get args from subsequent Apply component. + bool isApplied = false; auto argComponent = components[i]; - if (auto func = dyn_cast(decl); + if (auto func = dyn_cast(decl); func && i + 1 < components.size() && components[i + 1].getKind() == KeyPathExpr::Component::Kind::Apply) { argComponent = components[i + 1]; + isApplied = true; } unsigned numOperands = operands.size(); @@ -4530,10 +4643,11 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { SGF.F.getResilienceExpansion(), numOperands, needsGenericContext, component.getDeclRef().getSubstitutions(), decl, argComponent.getIndexHashableConformances(), baseTy, SGF.FunctionDC, - /*for descriptor*/ false)); + /*for descriptor*/ false, /*is applied func*/ isApplied)); baseTy = loweredComponents.back().getComponentType(); - if (kind == KeyPathExpr::Component::Kind::Member && - !dyn_cast(decl)) + if ((kind == KeyPathExpr::Component::Kind::Member && + !dyn_cast(decl)) || + (dyn_cast(decl) && !isApplied)) break; auto loweredArgs = SGF.emitKeyPathOperands( diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index a3ec76dc58a8a..1bc9a44c7cadb 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -1967,6 +1967,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction PreparedArguments &&methodArgs, SubstitutionMap subs, SGFContext C); + RValue emitUnappliedKeyPathMethod(SILLocation loc, ManagedValue base, + CanType baseType, + AbstractFunctionDecl *method, Type methodTy, + PreparedArguments &&methodArgs, + SubstitutionMap subs, SGFContext C); + ManagedValue emitAsyncLetStart(SILLocation loc, SILValue taskOptions, AbstractClosureExpr *asyncLetEntryPoint, From 807c32315900a4d448e68e57d63f3b59cef6d29c Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Wed, 25 Dec 2024 11:22:50 -0800 Subject: [PATCH 24/29] [SILGenExpr] Lower initializers. --- lib/SILGen/SILGenExpr.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 2d6cb84626cdc..133b6c7411e27 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -3243,7 +3243,8 @@ static PreparedArguments loadIndexValuesForKeyPathComponent( SILGenFunction &SGF, SILLocation loc, ValueDecl *storage, ArrayRef indexes, SILValue pointer) { // If not a subscript or method, do nothing. - if (!(isa(storage) || isa(storage))) + if (!(isa(storage) || isa(storage) || + isa(storage))) return PreparedArguments(); SmallVector indexParams; @@ -4277,7 +4278,7 @@ static void lowerKeyPathMemberIndexTypes( } needsGenericContext |= methodSubstTy->hasArchetype(); processIndicesOrParameters(method->getParameters(), &sig); - } + } } static void lowerKeyPathMemberIndexPatterns( @@ -4350,8 +4351,16 @@ KeyPathPatternComponent SILGenModule::emitKeyPathComponentForDecl( argPatterns, argEquals, argHash); } - auto representative = SILDeclRef(storage, SILDeclRef::Kind::Func, - /*isForeign*/ storage->isImportAsMember()); + SILDeclRef::Kind kind; + if (isa(storage)) { + kind = SILDeclRef::Kind::Func; + } else if (isa(storage)) { + kind = SILDeclRef::Kind::Allocator; + } else { + llvm_unreachable("Unsupported decl kind"); + } + SILDeclRef representative(storage, kind, + /*isForeign*/ storage->isImportAsMember()); auto id = getFunction(representative, NotForDefinition); SILFunction *func = nullptr; @@ -4646,8 +4655,9 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { /*for descriptor*/ false, /*is applied func*/ isApplied)); baseTy = loweredComponents.back().getComponentType(); if ((kind == KeyPathExpr::Component::Kind::Member && - !dyn_cast(decl)) || - (dyn_cast(decl) && !isApplied)) + !dyn_cast(decl) && !dyn_cast(decl)) || + ((dyn_cast(decl) || dyn_cast(decl)) && + !isApplied)) break; auto loweredArgs = SGF.emitKeyPathOperands( From 39b48bb218f34cc590f6e5565c43aef25f93827b Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Tue, 26 Nov 2024 01:05:41 -0800 Subject: [PATCH 25/29] [SIL] Handle KeyPathComponentKind::Method in helpers. --- lib/IRGen/GenKeyPath.cpp | 2 +- lib/SIL/IR/SILInstructions.cpp | 11 ++++++++--- lib/SIL/IR/SILPrinter.cpp | 3 ++- lib/SIL/Verifier/SILVerifier.cpp | 4 +++- .../SILCombiner/SILCombinerApplyVisitors.cpp | 1 + lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp | 1 + lib/SILOptimizer/Utils/KeyPathProjector.cpp | 1 + lib/Serialization/SerializeSIL.cpp | 3 +++ 8 files changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/IRGen/GenKeyPath.cpp b/lib/IRGen/GenKeyPath.cpp index 98a65640cb31d..ca4c16a0ae08c 100644 --- a/lib/IRGen/GenKeyPath.cpp +++ b/lib/IRGen/GenKeyPath.cpp @@ -811,7 +811,7 @@ emitKeyPathComponent(IRGenModule &IGM, KeyPathComponentHeader::forExternalComponent(externalSubArgs.size()) .getData()); auto descriptor = IGM.getAddrOfLLVMVariableOrGOTEquivalent( - LinkEntity::forPropertyDescriptor(externalDecl)); + LinkEntity::forPropertyDescriptor(externalDecl)); fields.addRelativeAddress(descriptor); for (auto *arg : externalSubArgs) fields.addRelativeAddress(arg); diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index db414626b28bc..cede43949d683 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -2933,6 +2933,7 @@ bool KeyPathPatternComponent::isComputedSettablePropertyMutating() const { switch (getKind()) { case Kind::StoredProperty: case Kind::GettableProperty: + case Kind::Method: case Kind::OptionalChain: case Kind::OptionalWrap: case Kind::OptionalForce: @@ -2960,6 +2961,7 @@ forEachRefcountableReference(const KeyPathPatternComponent &component, case KeyPathPatternComponent::Kind::SettableProperty: forFunction(component.getComputedPropertyForSettable()); LLVM_FALLTHROUGH; + case KeyPathPatternComponent::Kind::Method: case KeyPathPatternComponent::Kind::GettableProperty: forFunction(component.getComputedPropertyForGettable()); @@ -3014,7 +3016,8 @@ KeyPathPattern::get(SILModule &M, CanGenericSignature signature, case KeyPathPatternComponent::Kind::OptionalForce: case KeyPathPatternComponent::Kind::TupleElement: break; - + + case KeyPathPatternComponent::Kind::Method: case KeyPathPatternComponent::Kind::GettableProperty: case KeyPathPatternComponent::Kind::SettableProperty: for (auto &index : component.getArguments()) { @@ -3096,7 +3099,8 @@ void KeyPathPattern::Profile(llvm::FoldingSetNodeID &ID, case KeyPathPatternComponent::Kind::TupleElement: ID.AddInteger(component.getTupleIndex()); break; - + + case KeyPathPatternComponent::Kind::Method: case KeyPathPatternComponent::Kind::SettableProperty: ID.AddPointer(component.getComputedPropertyForSettable()); LLVM_FALLTHROUGH; @@ -3221,7 +3225,8 @@ visitReferencedFunctionsAndMethods( case KeyPathPatternComponent::Kind::SettableProperty: functionCallBack(getComputedPropertyForSettable()); LLVM_FALLTHROUGH; - case KeyPathPatternComponent::Kind::GettableProperty: { + case KeyPathPatternComponent::Kind::GettableProperty: + case KeyPathPatternComponent::Kind::Method: { functionCallBack(getComputedPropertyForGettable()); auto id = getComputedPropertyId(); switch (id.getKind()) { diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 9f2001e28f814..0903f7eae8be7 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -3099,7 +3099,8 @@ class SILPrinter : public SILInstructionVisitor { break; } case KeyPathPatternComponent::Kind::GettableProperty: - case KeyPathPatternComponent::Kind::SettableProperty: { + case KeyPathPatternComponent::Kind::SettableProperty: + case KeyPathPatternComponent::Kind::Method: { *this << (kind == KeyPathPatternComponent::Kind::GettableProperty ? "gettable_property $" : "settable_property $") << component.getComponentType() << ", " diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 08b1653599d8e..dd69edcac0021 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -394,7 +394,8 @@ void verifyKeyPathComponent(SILModule &M, } case KeyPathPatternComponent::Kind::GettableProperty: - case KeyPathPatternComponent::Kind::SettableProperty: { + case KeyPathPatternComponent::Kind::SettableProperty: + case KeyPathPatternComponent::Kind::Method: { if (forPropertyDescriptor) { require(component.getArguments().empty() && !component.getIndexEquals() && !component.getIndexHash(), @@ -5898,6 +5899,7 @@ class SILVerifier : public SILVerifierBase { switch (component.getKind()) { case KeyPathPatternComponent::Kind::GettableProperty: case KeyPathPatternComponent::Kind::SettableProperty: + case KeyPathPatternComponent::Kind::Method: hasIndices = !component.getArguments().empty(); break; diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 8ea61364bcad2..8833bcac7ef34 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -423,6 +423,7 @@ bool swift::tryOptimizeKeypathOffsetOf(ApplyInst *AI, break; case KeyPathPatternComponent::Kind::GettableProperty: case KeyPathPatternComponent::Kind::SettableProperty: + case KeyPathPatternComponent::Kind::Method: // We cannot predict the offset of fields in resilient types, because it's // unknown if a resilient field is a computed or stored property. if (component.getExternalDecl()) diff --git a/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp b/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp index 4ce1bda22ee72..1ae61d99d7f53 100644 --- a/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp +++ b/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp @@ -230,6 +230,7 @@ bool GlobalAccessRemoval::visitInstruction(SILInstruction *I) { break; case KeyPathPatternComponent::Kind::GettableProperty: case KeyPathPatternComponent::Kind::SettableProperty: + case KeyPathPatternComponent::Kind::Method: case KeyPathPatternComponent::Kind::OptionalChain: case KeyPathPatternComponent::Kind::OptionalForce: case KeyPathPatternComponent::Kind::OptionalWrap: diff --git a/lib/SILOptimizer/Utils/KeyPathProjector.cpp b/lib/SILOptimizer/Utils/KeyPathProjector.cpp index 22da71d94ca54..b7da792aaf374 100644 --- a/lib/SILOptimizer/Utils/KeyPathProjector.cpp +++ b/lib/SILOptimizer/Utils/KeyPathProjector.cpp @@ -641,6 +641,7 @@ class CompleteKeyPathProjector : public KeyPathProjector { (comp, std::move(parent), loc, builder); break; case KeyPathPatternComponent::Kind::GettableProperty: + case KeyPathPatternComponent::Kind::Method: projector = std::make_unique (keyPath, comp, std::move(parent), keyPath->getSubstitutions(), beginAccess, loc, builder); diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index adba6080dd97d..53b6b09f4a616 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -902,6 +902,9 @@ SILSerializer::writeKeyPathPatternComponent( }; switch (component.getKind()) { + case KeyPathPatternComponent::Kind::Method: + printf("SerializeSIL:writeKeyPathPatternComponent"); + break; case KeyPathPatternComponent::Kind::StoredProperty: handleComponentCommon(KeyPathComponentKindEncoding::StoredProperty); ListOfValues.push_back(S.addDeclRef(component.getStoredPropertyDecl())); From 834bd0e0695f9777d2d46b5a610248373c9ae13e Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Thu, 19 Dec 2024 20:17:20 -0800 Subject: [PATCH 26/29] [IRGen] Prevent descriptor emission. --- lib/IRGen/GenKeyPath.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/IRGen/GenKeyPath.cpp b/lib/IRGen/GenKeyPath.cpp index ca4c16a0ae08c..7d022357c2169 100644 --- a/lib/IRGen/GenKeyPath.cpp +++ b/lib/IRGen/GenKeyPath.cpp @@ -753,7 +753,8 @@ emitKeyPathComponent(IRGenModule &IGM, llvm_unreachable("not struct or class"); } case KeyPathPatternComponent::Kind::GettableProperty: - case KeyPathPatternComponent::Kind::SettableProperty: { + case KeyPathPatternComponent::Kind::SettableProperty: + case KeyPathPatternComponent::Kind::Method: { // If the component references an external property, encode that in a // header before the local attempt header, so that we can consult the // external descriptor at instantiation time. @@ -808,11 +809,13 @@ emitKeyPathComponent(IRGenModule &IGM, }); } fields.addInt32( - KeyPathComponentHeader::forExternalComponent(externalSubArgs.size()) - .getData()); - auto descriptor = IGM.getAddrOfLLVMVariableOrGOTEquivalent( - LinkEntity::forPropertyDescriptor(externalDecl)); - fields.addRelativeAddress(descriptor); + KeyPathComponentHeader::forExternalComponent(externalSubArgs.size()) + .getData()); + if (auto *decl = dyn_cast(externalDecl)) { + auto descriptor = IGM.getAddrOfLLVMVariableOrGOTEquivalent( + LinkEntity::forPropertyDescriptor(decl)); + fields.addRelativeAddress(descriptor); + } for (auto *arg : externalSubArgs) fields.addRelativeAddress(arg); } @@ -1165,6 +1168,7 @@ IRGenModule::getAddrOfKeyPathPattern(KeyPathPattern *pattern, switch (component.getKind()) { case KeyPathPatternComponent::Kind::GettableProperty: case KeyPathPatternComponent::Kind::SettableProperty: + case KeyPathPatternComponent::Kind::Method: for (auto &index : component.getArguments()) { operands[index.Operand].LoweredType = index.LoweredType; operands[index.Operand].LastUser = &component; From 78e7f6a72d16c89acedd78c7f0ac95efb072c37d Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Sun, 8 Dec 2024 18:48:42 -0800 Subject: [PATCH 27/29] [Tests] Update tests. --- test/Constraints/rdar68155466.swift | 9 +++++---- test/attr/attr_dynamic_member_lookup.swift | 10 +++++----- test/expr/unary/keypath/keypath.swift | 20 ++++++++++---------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/test/Constraints/rdar68155466.swift b/test/Constraints/rdar68155466.swift index 39dbccca53334..61b0686244565 100644 --- a/test/Constraints/rdar68155466.swift +++ b/test/Constraints/rdar68155466.swift @@ -10,7 +10,8 @@ import Foundation } // FIXME: the diagnostic below ideally should have been emitted (rdar://106241733) -struct Loop< // note {{required by generic struct 'Loop' where 'ID' = '() -> Int'}} +struct Loop< +// expected-note@-1 {{required by generic struct 'Loop' where 'ID' = '() -> Int'}} Data : RandomAccessCollection, ID : Hashable, Content @@ -25,6 +26,6 @@ func data() -> [A] { return [] } -_ = Loop(data(), id: \.uniqueID) { $0 } // expected-error {{key path cannot refer to instance method 'uniqueID()'}} -// FIXME: the diagnostics below ideally should have been emitted (rdar://106241733) -// error@-1 {{type '() -> Int' cannot conform to 'Hashable'}} note@-1 {{only concrete types such as structs, enums and classes can conform to protocols}} +_ = Loop(data(), id: \.uniqueID) { $0 } +// expected-error@-1 {{type '() -> Int' cannot conform to 'Hashable'}} +// expected-note@-2 {{only concrete types such as structs, enums and classes can conform to protocols}} diff --git a/test/attr/attr_dynamic_member_lookup.swift b/test/attr/attr_dynamic_member_lookup.swift index 2c1ba0fed5541..f980153cfa727 100644 --- a/test/attr/attr_dynamic_member_lookup.swift +++ b/test/attr/attr_dynamic_member_lookup.swift @@ -668,8 +668,8 @@ func test_chain_of_recursive_lookups(_ lens: Lens>>) { _ = \Lens>.obj.x } -// KeyPath Dynamic Member Lookup can't refer to methods, mutating setters and static members -// because of the KeyPath limitations +// KeyPath Dynamic Member Lookup can't refer mutating setters because of the +// KeyPath limitations func invalid_refs_through_dynamic_lookup() { struct S { static var foo: Int = 42 @@ -683,9 +683,9 @@ func invalid_refs_through_dynamic_lookup() { func test(_ lens: A) { _ = lens.foo // expected-error {{static member 'foo' cannot be used on instance of type 'S'}} - _ = lens.bar() // expected-error {{dynamic key path member lookup cannot refer to instance method 'bar()'}} - _ = lens.bar().faz + 1 // expected-error {{dynamic key path member lookup cannot refer to instance method 'bar()'}} - _ = lens.baz("hello") // expected-error {{dynamic key path member lookup cannot refer to static method 'baz'}} + _ = lens.bar() + _ = lens.bar().faz + 1 + _ = lens.baz("hello") // expected-error {{static member 'baz' cannot be used on instance of type 'S'}} } } diff --git a/test/expr/unary/keypath/keypath.swift b/test/expr/unary/keypath/keypath.swift index 4e84e2df85358..0e76f42740661 100644 --- a/test/expr/unary/keypath/keypath.swift +++ b/test/expr/unary/keypath/keypath.swift @@ -824,13 +824,13 @@ func test_keypath_with_method_refs() { static func bar() -> Int { return 0 } } - let _: KeyPath = \.foo // expected-error {{key path cannot refer to instance method 'foo()'}} + let _: KeyPath = \.foo // expected-error@-1 {{cannot assign value of type 'KeyPath Int>' to type 'KeyPath'}} // expected-note@-2 {{arguments to generic parameter 'Value' ('() -> Int' and 'Int') are expected to be equal}} - let _: KeyPath = \.bar // expected-error {{key path cannot refer to static method 'bar()'}} + let _: KeyPath = \.bar // expected-error {{static member 'bar()' cannot be used on instance of type 'S'}} // expected-error@-1 {{cannot assign value of type 'KeyPath Int>' to type 'KeyPath'}} // expected-note@-2 {{arguments to generic parameter 'Value' ('() -> Int' and 'Int') are expected to be equal}} - let _ = \S.Type.bar // expected-error {{key path cannot refer to static method 'bar()'}} + let _ = \S.Type.bar struct A { func foo() -> B { return B() } @@ -841,10 +841,10 @@ func test_keypath_with_method_refs() { var bar: Int = 42 } - let _: KeyPath = \.foo.bar // expected-error {{key path cannot refer to instance method 'foo()'}} - let _: KeyPath = \.faz.bar // expected-error {{key path cannot refer to static method 'faz()'}} - let _ = \A.foo.bar // expected-error {{key path cannot refer to instance method 'foo()'}} - let _ = \A.Type.faz.bar // expected-error {{key path cannot refer to static method 'faz()'}} + let _: KeyPath = \.foo.bar // expected-error {{type of expression is ambiguous without a type annotation}} + let _: KeyPath = \.faz.bar // expected-error {{static member 'faz()' cannot be used on instance of type 'A'}} + let _ = \A.foo.bar // expected-error {{type of expression is ambiguous without a type annotation}} + let _ = \A.Type.faz.bar // expected-error {{type of expression is ambiguous without a type annotation}} } // https://github.com/apple/swift/issues/54961 @@ -856,7 +856,7 @@ protocol Zonk { typealias Blatz = (gloop: String, zoop: Zonk?) func f_54961(fleep: [Blatz]) { - fleep.compactMap(\.zoop?.wargle) // expected-error {{key path cannot refer to instance method 'wargle()'}} + let _ = fleep.compactMap(\.zoop?.wargle) } // https://github.com/apple/swift/issues/52867 @@ -1091,8 +1091,8 @@ func testSyntaxErrors() { // https://github.com/apple/swift/issues/56996 func f_56996() { - _ = \Int.byteSwapped.signum() // expected-error {{invalid component of Swift key path}} - _ = \Int.byteSwapped.init() // expected-error {{invalid component of Swift key path}} + _ = \Int.byteSwapped.signum() + _ = \Int.byteSwapped.init() // expected-error {{static member 'init()' cannot be used on instance of type 'Int'}} _ = \Int // expected-error {{key path must have at least one component}} _ = \Int? // expected-error {{key path must have at least one component}} _ = \Int. // expected-error {{invalid component of Swift key path}} From 15c219cd70804911db8f755ab780459f912678e6 Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Mon, 16 Dec 2024 16:51:06 -0800 Subject: [PATCH 28/29] [Tests] Add tests. --- test/Constraints/keypath.swift | 6 +- test/Demangle/Inputs/manglings.txt | 2 + test/Interpreter/keypath.swift | 223 +++++++++++++++++- test/Interpreter/static_keypaths.swift | 23 ++ test/SILGen/keypaths.swift | 83 ++++++- .../Parse/forward-slash-regex.swift | 3 +- test/attr/attr_dynamic_member_lookup.swift | 56 +++++ .../keypath/keypath-unsupported-methods.swift | 48 ++++ test/expr/unary/keypath/keypath.swift | 110 ++++++++- 9 files changed, 529 insertions(+), 25 deletions(-) create mode 100644 test/expr/unary/keypath/keypath-unsupported-methods.swift diff --git a/test/Constraints/keypath.swift b/test/Constraints/keypath.swift index 7b90c7d24a7b0..719e46a45f717 100644 --- a/test/Constraints/keypath.swift +++ b/test/Constraints/keypath.swift @@ -41,8 +41,10 @@ let some = Some(keyPath: \Demo.here) func testFunc() { let _: (S) -> Int = \.i _ = ([S]()).map(\.i) - _ = \S.init // expected-error {{key path cannot refer to initializer 'init()'}} - _ = ([S]()).map(\.init) // expected-error {{key path cannot refer to initializer 'init()'}} + _ = \S.Type.init + _ = \S.init // expected-error {{static member 'init()' cannot be used on instance of type 'S'}} + _ = ([S.Type]()).map(\.init) + _ = ([S]()).map(\.init) // expected-error {{static member 'init()' cannot be used on instance of type 'S'}} let kp = \S.i let _: KeyPath = kp // works, because type defaults to KeyPath nominal diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index d10ecd9fdd841..6e012a78bb4fd 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -368,6 +368,8 @@ $sSUss17FixedWidthIntegerRzrlEyxqd__cSzRd__lufCSu_SiTg5 ---> generic specializat $s4test7genFuncyyx_q_tr0_lFSi_SbTtt1g5 ---> generic specialization of test.genFunc(A, B) -> () $sSD5IndexVy__GD ---> $sSD5IndexVy__GD $s4test3StrCACycfC ---> {T:$s4test3StrCACycfc} test.Str.__allocating_init() -> test.Str +@$s8keypaths1KV3valACSi_tcfcACmTkmu : $@convention(keypath_accessor_getter) (@in_guaranteed @thick K.Type) -> @out @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for ) +@$s8keypaths1KVACycfcACmTkMA : $@convention(keypath_accessor_getter) (@in_guaranteed @thick K.Type) -> @out K) $s18keypaths_inlinable13KeypathStructV8computedSSvpACTKq ---> key path getter for keypaths_inlinable.KeypathStruct.computed : Swift.String : keypaths_inlinable.KeypathStruct, serialized $s18resilient_protocol24ResilientDerivedProtocolPxAA0c4BaseE0Tn --> associated conformance descriptor for resilient_protocol.ResilientDerivedProtocol.A: resilient_protocol.ResilientBaseProtocol $s3red4testyAA3ResOyxSayq_GAEs5ErrorAAq_sAFHD1__HCg_GADyxq_GsAFR_r0_lF ---> red.test(red.Res) -> red.Res diff --git a/test/Interpreter/keypath.swift b/test/Interpreter/keypath.swift index 19d691caac640..3a7655ce525d3 100644 --- a/test/Interpreter/keypath.swift +++ b/test/Interpreter/keypath.swift @@ -5,9 +5,30 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -class MyLabel { +class MyLabel: Hashable { var text = "label" static var isVisible = true + func x(val value: Int) -> Int { return value } + static func y(val value: Int) -> Int { return value } + func saveClosure(_ closure: @escaping () -> Void) { + storedClosure = closure + } + func executeStoredClosure() { + storedClosure?() + } + private var storedClosure: (() -> Void)? + + required init() {} + required init(customText: String) { + text = customText + } + + static func == (lhs: MyLabel, rhs: MyLabel) -> Bool { + return lhs === rhs + } + func hash(into hasher: inout Hasher) { + hasher.combine(ObjectIdentifier(self)) + } } class Controller { @@ -57,11 +78,6 @@ class Controller { } } -struct S { - var a: Int - static let b: Double = 100.0 -} - struct Container { var v : V init(_ v: V) { @@ -70,16 +86,30 @@ struct Container { func useKeyPath(_ keyPath: KeyPath) -> String { return (v[keyPath: keyPath] as! MyLabel).text } + func invokeKeyPathMethod( + _ keyPath: KeyPath, + method: KeyPath R>, + arg: Int + ) -> R { + let instance = v[keyPath: keyPath] + return instance[keyPath: method](arg) + } } extension Container where V: Controller { func test() -> String { return useKeyPath(\.label) } + func testKeyPathMethod() -> Int { + let result = invokeKeyPathMethod(\.label, method: \MyLabel.x(val:), arg: 10) + return result + } } // CHECK: label print(Container(Controller()).test()) +// CHECK: 10 +print(Container(Controller()).testKeyPathMethod()) struct MetatypeContainer { var v : V.Type @@ -92,10 +122,38 @@ struct MetatypeContainer { } return false } + func getKeyPathMethodVal() -> Int { + if let labelType = v as? MyLabel.Type { + return labelType.y(val: 20) + } + return 0 + } + func createInstanceWithDefaultInit() -> MyLabel? { + if let labelType = v as? MyLabel.Type { + return labelType.init() + } + return nil + } + func createInstanceWithCustomInit(customText: String) -> MyLabel? { + if let labelType = v as? MyLabel.Type { + return labelType.init(customText: customText) + } + return nil + } } // CHECK: true print(MetatypeContainer(MyLabel.self).useMetatypeKeyPath()) +// CHECK: 20 +print(MetatypeContainer(MyLabel.self).getKeyPathMethodVal()) +// CHECK: label +if let instance = MetatypeContainer(MyLabel.self).createInstanceWithDefaultInit() { + print(instance.text) +} +// CHECK: Custom Label +if let customInstance = MetatypeContainer(MyLabel.self).createInstanceWithCustomInit(customText: "Custom Label") { + print(customInstance.text) +} public class GenericController { init(_ u: U) { @@ -116,13 +174,28 @@ public func generic_class_constrained_keypath(_ c: V) where V : GenericCon // CHECK: label generic_class_constrained_keypath(GenericController(5)) +struct S { + var year = 2024 + static let millenium: Int = 3 + init() {} + init(val value: Int = 2024) { year = value } + + var add: (Int, Int) -> Int { return { $0 + $1 } } + func add(this: Int) -> Int { this + this} + func add(that: Int) -> Int { that + that } + static func subtract(_ val: Int) -> Int { return millenium - val } + nonisolated func nonisolatedNextYear() -> Int { year + 1 } + consuming func consume() { print(year) } + subscript(index: Int) -> Int { return year + index} +} + // CHECK: {{\\Controller\.secondLabel!\.text|\\Controller\.\)>!\.}} print(\Controller.secondLabel!.text) // CHECK: {{\\Controller\.subscript\(_: String\)|\\Controller\.}} print(\Controller["abc"]) -// CHECK: \S.a -print(\S.a) +// CHECK: \S.year +print(\S.year) // CHECK: {{\\Controller\.subscript\(int: Int, str: String, _: Int\)|\\Controller\.}} print(\Controller[int: 0, str: "", 0]) // CHECK: {{\\Controller\.thirdLabel|\\Controller\.\)>}} @@ -146,13 +219,98 @@ print(\Controller[array: [42], array2: [42]]) // CHECK: {{\\Controller\.(fourthLabel|\)>)!\.}} print(\Controller.fourthLabel!.isVisible) -// CHECK: \S.Type. -print(\S.Type.b) +// CHECK: \S.Type. +print(\S.Type.millenium) // CHECK: {{\\Controller\.(fifthLabel|\)>)\?\.?}} print(\Controller.fifthLabel?.isVisible) // CHECK: \Int.Type. print(\Int.Type.zero) +// CHECK: \S.Type. S)> +print(\S.Type.init) +// CHECK: \S.Type. +print(\S.Type.init()) +// CHECK: \S.Type. S)> +print(\S.Type.init(val:)) +// CHECK: \S.Type. +print(\S.Type.init(val: 2025)) +// CHECK: \S.Type..year +print(\S.Type.init(val: 2025).year) +// CHECK: 2024 +let kp = \S.Type.init()[0] +let result = S.self[keyPath: kp] +print(result) +// CHECK: 7 +let kpAdd = \S.add +let resultAdd = S()[keyPath: kpAdd](3, 4) +print(resultAdd) +// CHECK: \S. Int)> +print(\S.add(this:)) +// CHECK: \S. +print(\S.add(that: 1)) +// CHECK: \S.Type. Int)> +print(\S.Type.subtract) +// CHECK: \S.Type. +print(\S.Type.subtract(1)) +// CHECK: \S. Int)> +print(\S.nonisolatedNextYear) +// CHECK: \S. +print(\S.nonisolatedNextYear()) +// CHECK: \S.Type.. +print(\S.Type.init(val:2025).nonisolatedNextYear()) +// CHECK: 2026 +let kpMethodProperty = \S.Type.init(val:2025).nonisolatedNextYear().description +let resultMethodProperty = S.self[keyPath: kpMethodProperty] +print(resultMethodProperty) +// CHECK: \S.Type... +print(\S.Type.init(val:2025).nonisolatedNextYear().signum()) +// // CHECK: \S. +print(\S.consume()) + +// CHECK: false +print(\S.add(that: 1) == \S.add(this: 1)) +// CHECK: false +print(\S.add(that: 1) == \S.add(this: 2)) +// CHECK: false +print(\S.Type.init(val: 2024) == \S.Type.init(val: 2025)) +// CHECK: false +print(\S.Type.init(val: 2024).nonisolatedNextYear() == \S.Type.init(val: 2025)) +// CHECK: true +print(\S.Type.init(val: 2024).add(this: 1) != \S.Type.init(val: 2025)) + +class E: Hashable { + static func == (lhs: E, rhs: E) -> Bool { return lhs === rhs } + func hash(into hasher: inout Hasher) { + hasher.combine(ObjectIdentifier(self)) + } +} +struct BaseType { + func foo(hashableParam e: E) {} +} +let hashableInstance = E() +// CHECK: \BaseType. +print(\BaseType.foo(hashableParam: hashableInstance)) + +protocol Describable { + func describe() -> String +} +struct C: Describable { + var name: String + func describe() -> String { return "\(name)" } +} +// CHECK: \C. String)> +print(\C.describe) + +// CHECK: false +print(\S.Type.init(val:2025) == \S.Type.init(val:2026)) +// CHECK: false +print(\S.Type.init(val:2025).nonisolatedNextYear() == \S.Type.init(val:2026).nonisolatedNextYear()) +// CHECK: true +print(\S.Type.init(val:2025).nonisolatedNextYear() == \S.Type.init(val:2025).nonisolatedNextYear()) +// CHECK: false +print(\MyLabel.x(val:10) == \MyLabel.x(val:20)) +// CHECK: true +print(\MyLabel.Type.y(val:10) == \MyLabel.Type.y(val:10)) do { struct S { @@ -208,3 +366,48 @@ do { // CHECK: true print(StaticExample().isVisible) } + +do { + @dynamicMemberLookup + struct InstanceDynamicMemberLookup { + var obj: T + + subscript(dynamicMember member: KeyPath U>) -> (Int) -> U { + get { obj[keyPath: member] } + } + } + + // CHECK: 50 + let instanceDynamicLookup = InstanceDynamicMemberLookup(obj: MyLabel()) + print(instanceDynamicLookup.x(50)) +} + +extension MyLabel { + static var defaultInitializer: () -> MyLabel { return MyLabel.init } + static var customInitializer: (String) -> MyLabel { return MyLabel.init(customText:) } +} + +do { + @dynamicMemberLookup + struct StaticDynamicMemberLookup { + subscript(dynamicMember keyPath: KeyPath U>) -> (Int) -> U { + return T.self[keyPath: keyPath] + } + subscript(dynamicMember keyPath: KeyPath U>) -> () -> U { + return T.self[keyPath: keyPath] + } + subscript(dynamicMember keyPath: KeyPath U>) -> (String) -> U { + return T.self[keyPath: keyPath] + } + } + + // CHECK: 60 + let staticDynamicLookup = StaticDynamicMemberLookup() + print(staticDynamicLookup.y(60)) + // CHECK: label + let defaultInstance = staticDynamicLookup.defaultInitializer() + print(defaultInstance.text) + // CHECK: Custom Label + let customInstance = staticDynamicLookup.customInitializer("Custom Label") + print(customInstance.text) +} diff --git a/test/Interpreter/static_keypaths.swift b/test/Interpreter/static_keypaths.swift index 7c48dc0d3e2f6..3f645c0217ee0 100644 --- a/test/Interpreter/static_keypaths.swift +++ b/test/Interpreter/static_keypaths.swift @@ -30,6 +30,13 @@ public struct AStruct { public static var property2: Int = 2 private(set) public static var property3: Int = 1 private(set) public static var property4: Int = 4 + public static func x(val value: Int) -> Int { return value } + public static func y(val value: Int) -> Int { return value } + + public init(val value: Int = 2024) { + year = value + } + public var year: Int } //--- LibB.swift @@ -41,6 +48,10 @@ public let keyPath3FromLibB = \AStruct.Type.property3 public let keyPath4FromLibB = \AStruct.Type.property4 public var keyPath5FromLibB = \AStruct.Type.property1 // WritableKeyPath with public setter public var keyPath6FromLibB = \Int.Type.zero +public let keyPath7FromLibB = \AStruct.Type.x(val: 10) +public let keyPath8FromLibB = \AStruct.Type.y(val: 10) +public let keyPath9FromLibB = \AStruct.Type.init +public let keyPath10FromLibB = \AStruct.Type.init(val: 2025) //--- LibC.swift import LibA @@ -51,6 +62,9 @@ public let keyPath3FromLibC = \AStruct.Type.property3 // Read-only with private public let keyPath4FromLibC = \AStruct.Type.property4 public var keyPath5FromLibC = \Int.Type.zero public var keyPath6FromLibC = \Int.Type.max +public let keyPath7FromLibC = \AStruct.Type.x(val: 10) +public let keyPath8FromLibC = \AStruct.Type.init +public let keyPath9FromLibC = \AStruct.Type.init(val: 2026) //--- main.swift import LibB @@ -73,3 +87,12 @@ print(keyPath5FromLibB == keyPath3FromLibC) print(keyPath6FromLibB == keyPath5FromLibC) // Check: false print(keyPath6FromLibB == keyPath6FromLibC) + +// CHECK: true +print(keyPath7FromLibB == keyPath7FromLibC) +// CHECK: false +print(keyPath8FromLibB == keyPath7FromLibC) +// CHECK: true +print(keyPath9FromLibB == keyPath8FromLibC) +// CHECK: true +print(keyPath10FromLibB != keyPath9FromLibC) diff --git a/test/SILGen/keypaths.swift b/test/SILGen/keypaths.swift index b4a83efa78910..df5c1915e29cc 100644 --- a/test/SILGen/keypaths.swift +++ b/test/SILGen/keypaths.swift @@ -1,6 +1,5 @@ // RUN: %target-swift-emit-silgen -Xllvm -sil-print-types -target %target-swift-5.1-abi-triple -parse-stdlib -module-name keypaths %s | %FileCheck %s - import Swift struct S { @@ -639,6 +638,88 @@ struct TestKeyPathWithSomeType : DefineSomeType { } } +class J: Hashable { + static func == (lhs: J, rhs: J) -> Bool { return lhs === rhs } + func hash(into hasher: inout Hasher) { + hasher.combine(ObjectIdentifier(self)) + } +} + +struct K { + var year = 2024 + static let millenium: Int = 3 + init() {} + init(val value: Int = 2024) { year = value } + + var add: (Int, Int) -> Int { return { $0 + $1 } } + func add(this: Int) -> Int { this + this} + func add(that: Int) -> Int { that + that } + static func subtract(_ val: Int) -> Int { return millenium - val } + nonisolated func nonisolatedNextYear() -> Int { year + 1 } + func doubleValue(_ value: inout Int) { value *= 2 } + func foo(hashableParam j: J) {} + subscript(index: Int) -> Int { return year + index} +} + +protocol Describable { + func describe() -> String +} + +struct L: Describable { + var name: String + func describe() -> String { return "\(name)" } +} + +// CHECK-LABEL: // test_method_and_initializer_keypaths() +// CHECK-LABEL: sil hidden [ossa] @{{.*}} : $@convention(thin) () -> () { +func test_method_and_initializer_keypaths() { + // CHECK: %0 = keypath $WritableKeyPath K> + // CHECK-SAME: root $K.Type; gettable_property $() -> K, id @$s8keypaths1KVACycfC : $@convention(method) (@thin K.Type) -> K, getter @$s8keypaths1KVACycfcACmTkmu : $@convention(keypath_accessor_getter) (@in_guaranteed @thick K.Type) -> @out @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for ) + let _ = \K.Type.init + // CHECK: keypath $WritableKeyPath, (root $K.Type; gettable_property $K, id @$s8keypaths1KVACycfC : $@convention(method) (@thin K.Type) -> K, getter @$s8keypaths1KVACycfcACmTkMA : $@convention(keypath_accessor_getter) (@in_guaranteed @thick K.Type) -> @out K) + let _ = \K.Type.init() + // CHECK: keypath $WritableKeyPath K> + // CHECK-SAME: root $K.Type; gettable_property $(Int) -> K, id @$s8keypaths1KV3valACSi_tcfC : $@convention(method) (Int, @thin K.Type) -> K, getter @$s8keypaths1KV3valACSi_tcfcACmTkmu : $@convention(keypath_accessor_getter) (@in_guaranteed @thick K.Type) -> @out @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for ) + let _ = \K.Type.init(val:) + // CHECK: keypath $WritableKeyPath, (root $K.Type; gettable_property $K, id @$s8keypaths1KV3valACSi_tcfC : $@convention(method) (Int, @thin K.Type) -> K, getter @$s8keypaths1KV3valACSi_tcfcACmTkMA : $@convention(keypath_accessor_getter) (@in_guaranteed @thick K.Type, @in_guaranteed Int) -> @out K, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(keypath_accessor_equals) (@in_guaranteed Int, @in_guaranteed Int) -> Bool, indices_hash @$sSiTh : $@convention(keypath_accessor_hash) (@in_guaranteed Int) -> Int) + let _ = \K.Type.init(val: 2025) + // CHECK: keypath $WritableKeyPath, (root $K.Type; gettable_property $K, id @$s8keypaths1KV3valACSi_tcfC : $@convention(method) (Int, @thin K.Type) -> K, getter @$s8keypaths1KV3valACSi_tcfcACmTkMA : $@convention(keypath_accessor_getter) (@in_guaranteed @thick K.Type, @in_guaranteed Int) -> @out K, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(keypath_accessor_equals) (@in_guaranteed Int, @in_guaranteed Int) -> Bool, indices_hash @$sSiTh : $@convention(keypath_accessor_hash) (@in_guaranteed Int) -> Int; stored_property #K.year : $Int) + let _ = \K.Type.init(val: 2025).year + // CHECK: keypath $KeyPath, (root $K.Type; gettable_property $K, id @$s8keypaths1KVACycfC : $@convention(method) (@thin K.Type) -> K, getter @$s8keypaths1KVACycfcACmTkMA : $@convention(keypath_accessor_getter) (@in_guaranteed @thick K.Type) -> @out K; gettable_property $Int, id @$s8keypaths1KVyS2icig : $@convention(method) (Int, K) -> Int, getter @$s8keypaths1KVyS2icipACTK : $@convention(keypath_accessor_getter) (@in_guaranteed K, @in_guaranteed Int) -> @out Int, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(keypath_accessor_equals) (@in_guaranteed Int, @in_guaranteed Int) -> Bool, indices_hash @$sSiTh : $@convention(keypath_accessor_hash) (@in_guaranteed Int) -> Int) + let _ = \K.Type.init()[0] + // CHECK: keypath $KeyPath Int>, (root $K; gettable_property $(Int, Int) -> Int, id @$s8keypaths1KV3addyS2i_Sitcvg : $@convention(method) (K) -> @owned @callee_guaranteed (Int, Int) -> Int, getter @$s8keypaths1KV3addyS2i_SitcvpACTK : $@convention(keypath_accessor_getter) (@in_guaranteed K) -> @out @callee_guaranteed @substituted <τ_0_0, τ_0_1, τ_0_2> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> @out τ_0_2 for ) + let _ = \K.add + // CHECK: keypath $WritableKeyPath Int> + // CHECK-SAME: root $K; gettable_property $(Int) -> Int, id @$s8keypaths1KV3add4thisS2i_tF : $@convention(method) (Int, K) -> Int, getter @$s8keypaths1KV3add4thisS2i_tFACTkmu : $@convention(keypath_accessor_getter) (@in_guaranteed K) -> @out @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for ) + let _ = \K.add(this:) + // CHECK: keypath $WritableKeyPath, (root $K; gettable_property $Int, id @$s8keypaths1KV3add4thatS2i_tF : $@convention(method) (Int, K) -> Int, getter @$s8keypaths1KV3add4thatS2i_tFACTkMA : $@convention(keypath_accessor_getter) (@in_guaranteed K, @in_guaranteed Int) -> @out Int, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(keypath_accessor_equals) (@in_guaranteed Int, @in_guaranteed Int) -> Bool, indices_hash @$sSiTh : $@convention(keypath_accessor_hash) (@in_guaranteed Int) -> Int) + let _ = \K.add(that: 1) + // CHECK: keypath $WritableKeyPath Int> + // CHECK-SAME: root $K.Type; gettable_property $(Int) -> Int, id @$s8keypaths1KV8subtractyS2iFZ : $@convention(method) (Int, @thin K.Type) -> Int, getter @$s8keypaths1KV8subtractyS2iFZACmTkmu : $@convention(keypath_accessor_getter) (@in_guaranteed @thick K.Type) -> @out @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for ) + let _ = \K.Type.subtract + // CHECK: keypath $WritableKeyPath, (root $K.Type; gettable_property $Int, id @$s8keypaths1KV8subtractyS2iFZ : $@convention(method) (Int, @thin K.Type) -> Int, getter @$s8keypaths1KV8subtractyS2iFZACmTkMA : $@convention(keypath_accessor_getter) (@in_guaranteed @thick K.Type, @in_guaranteed Int) -> @out Int, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(keypath_accessor_equals) (@in_guaranteed Int, @in_guaranteed Int) -> Bool, indices_hash @$sSiTh : $@convention(keypath_accessor_hash) (@in_guaranteed Int) -> Int) + let _ = \K.Type.subtract(1) + // CHECK: keypath $WritableKeyPath Int> + // CHECK-SAME: root $K; gettable_property $() -> Int, id @$s8keypaths1KV19nonisolatedNextYearSiyF : $@convention(method) (K) -> Int, getter @$s8keypaths1KV19nonisolatedNextYearSiyFACTkmu : $@convention(keypath_accessor_getter) (@in_guaranteed K) -> @out @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for ) + let _ = \K.nonisolatedNextYear + // CHECK: keypath $WritableKeyPath, (root $K; gettable_property $Int, id @$s8keypaths1KV19nonisolatedNextYearSiyF : $@convention(method) (K) -> Int, getter @$s8keypaths1KV19nonisolatedNextYearSiyFACTkMA : $@convention(keypath_accessor_getter) (@in_guaranteed K) -> @out Int) + let _ = \K.nonisolatedNextYear() + // CHECK: keypath $WritableKeyPath, (root $K.Type; gettable_property $K, id @$s8keypaths1KV3valACSi_tcfC : $@convention(method) (Int, @thin K.Type) -> K, getter @$s8keypaths1KV3valACSi_tcfcACmTkMA : $@convention(keypath_accessor_getter) (@in_guaranteed @thick K.Type, @in_guaranteed Int) -> @out K, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(keypath_accessor_equals) (@in_guaranteed Int, @in_guaranteed Int) -> Bool, indices_hash @$sSiTh : $@convention(keypath_accessor_hash) (@in_guaranteed Int) -> Int; gettable_property $Int, id @$s8keypaths1KV19nonisolatedNextYearSiyF : $@convention(method) (K) -> Int, getter @$s8keypaths1KV19nonisolatedNextYearSiyFACTkMA : $@convention(keypath_accessor_getter) (@in_guaranteed K) -> @out Int) + let _ = \K.Type.init(val:2025).nonisolatedNextYear() + // CHECK: keypath $KeyPath, (root $K.Type; gettable_property $K, id @$s8keypaths1KV3valACSi_tcfC : $@convention(method) (Int, @thin K.Type) -> K, getter @$s8keypaths1KV3valACSi_tcfcACmTkMA : $@convention(keypath_accessor_getter) (@in_guaranteed @thick K.Type, @in_guaranteed Int) -> @out K, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(keypath_accessor_equals) (@in_guaranteed Int, @in_guaranteed Int) -> Bool, indices_hash @$sSiTh : $@convention(keypath_accessor_hash) (@in_guaranteed Int) -> Int; gettable_property $Int, id @$s8keypaths1KV19nonisolatedNextYearSiyF : $@convention(method) (K) -> Int, getter @$s8keypaths1KV19nonisolatedNextYearSiyFACTkMA : $@convention(keypath_accessor_getter) (@in_guaranteed K) -> @out Int; gettable_property $String, id @$sSzsE11descriptionSSvg : $@convention(method) <τ_0_0 where τ_0_0 : BinaryInteger> (@in_guaranteed τ_0_0) -> @owned String, getter @$sSzsE11descriptionSSvpSiTK : $@convention(keypath_accessor_getter) (@in_guaranteed Int) -> @out String, external #BinaryInteger.description) + let _ = \K.Type.init(val:2025).nonisolatedNextYear().description + // CHECK: keypath $WritableKeyPath, (root $K.Type; gettable_property $K, id @$s8keypaths1KV3valACSi_tcfC : $@convention(method) (Int, @thin K.Type) -> K, getter @$s8keypaths1KV3valACSi_tcfcACmTkMA : $@convention(keypath_accessor_getter) (@in_guaranteed @thick K.Type, @in_guaranteed Int) -> @out K, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(keypath_accessor_equals) (@in_guaranteed Int, @in_guaranteed Int) -> Bool, indices_hash @$sSiTh : $@convention(keypath_accessor_hash) (@in_guaranteed Int) -> Int; gettable_property $Int, id @$s8keypaths1KV19nonisolatedNextYearSiyF : $@convention(method) (K) -> Int, getter @$s8keypaths1KV19nonisolatedNextYearSiyFACTkMA : $@convention(keypath_accessor_getter) (@in_guaranteed K) -> @out Int; gettable_property $Int, id @$sSi6signumSiyF : $@convention(method) (Int) -> Int, getter @$sSi6signumSiyFSiTkMA : $@convention(keypath_accessor_getter) (@in_guaranteed Int) -> @out Int) + let _ = \K.Type.init(val:2025).nonisolatedNextYear().signum() + // CHECK: keypath $WritableKeyPath, (root $K; gettable_property $(), id @$s8keypaths1KV3foo13hashableParamyAA1JC_tF : $@convention(method) (@guaranteed J, K) -> (), getter @$s8keypaths1KV3foo13hashableParamyAA1JC_tFACTkMA : $@convention(keypath_accessor_getter) (@in_guaranteed K, @in_guaranteed J) -> @out (), indices [%$0 : $J : $J], indices_equals @$s8keypaths1JCTH : $@convention(keypath_accessor_equals) (@in_guaranteed J, @in_guaranteed J) -> Bool, indices_hash @$s8keypaths1JCTh : $@convention(keypath_accessor_hash) (@in_guaranteed J) -> Int) + let hashableInstance = J() + let _ = \K.foo(hashableParam: hashableInstance) + // CHECK: keypath $WritableKeyPath String> + // CHECK-SAME: root $L; gettable_property $() -> String, id @$s8keypaths1LV8describeSSyF : $@convention(method) (@guaranteed L) -> @owned String, getter @$s8keypaths1LV8describeSSyFACTkmu : $@convention(keypath_accessor_getter) (@in_guaranteed L) -> @out @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for ) + let _ = \L.describe + // CHECK: keypath $WritableKeyPath, (root $L; gettable_property $String, id @$s8keypaths1LV8describeSSyF : $@convention(method) (@guaranteed L) -> @owned String, getter @$s8keypaths1LV8describeSSyFACTkMA : $@convention(keypath_accessor_getter) (@in_guaranteed L) -> @out String) + let _ = \L.describe() +} + struct N { static let kelvin = 293 } diff --git a/test/StringProcessing/Parse/forward-slash-regex.swift b/test/StringProcessing/Parse/forward-slash-regex.swift index eba89e59c2911..ede49b43b176b 100644 --- a/test/StringProcessing/Parse/forward-slash-regex.swift +++ b/test/StringProcessing/Parse/forward-slash-regex.swift @@ -407,7 +407,8 @@ _ = /\()/ // expected-error@-1 {{'/' is not a prefix unary operator}} // expected-error@-2 {{'/' is not a postfix unary operator}} // expected-error@-3 {{invalid component of Swift key path}} - +// expected-error@-4 {{type of expression is ambiguous without a type annotation}} + do { let _: Regex = (/whatever\)/ // expected-note@-1 {{to match this opening '('}} diff --git a/test/attr/attr_dynamic_member_lookup.swift b/test/attr/attr_dynamic_member_lookup.swift index f980153cfa727..b3d7e7623d1d6 100644 --- a/test/attr/attr_dynamic_member_lookup.swift +++ b/test/attr/attr_dynamic_member_lookup.swift @@ -475,6 +475,44 @@ var namedTupleLens = Lens<(question: String, answer: Int)>((question: "ultimate _ = namedTupleLens.question.count _ = namedTupleLens.answer +struct MethodAndInitializerTest { + let value: Int + init(value: Int) { self.value = value } + func instanceMethod() -> String { return "InstanceMethod" } + static func staticMethod() -> Int { return 42 } +} + +@dynamicMemberLookup +struct MethodAndInitializerLens { + var value: T + var type: T.Type + + subscript(dynamicMember member: KeyPath) -> U { + return value[keyPath: member] + } + + subscript(dynamicMember member: (T) -> () -> U) -> () -> U { + return { member(self.value)() } + } + + subscript(dynamicMember member: (T.Type) -> () -> U) -> () -> U { + return { member(self.type)() } + } +} + +func test_method_and_init_lens() { + let instance = MethodAndInitializerTest(value: 10) + let methodLens = MethodAndInitializerLens(value: instance, type: MethodAndInitializerTest.self) + + let _ = methodLens[dynamicMember: MethodAndInitializerTest.instanceMethod]() + + let staticMethodClosure = methodLens[dynamicMember: { $0.staticMethod }] + let _ = staticMethodClosure() + + let initializer = MethodAndInitializerTest.init + let _ = MethodAndInitializerLens(value: initializer(20), type: MethodAndInitializerTest.self) +} + @dynamicMemberLookup class A { var value: T @@ -488,6 +526,19 @@ class A { } } +@dynamicMemberLookup +class AMetatype { + var value: T.Type + + init(_ v: T.Type) { + self.value = v + } + + subscript(dynamicMember member: KeyPath) -> U { + get { return value[keyPath: member] } + } +} + // Let's make sure that keypath dynamic member lookup // works with inheritance @@ -687,6 +738,11 @@ func invalid_refs_through_dynamic_lookup() { _ = lens.bar().faz + 1 _ = lens.baz("hello") // expected-error {{static member 'baz' cannot be used on instance of type 'S'}} } + + func testStatic(_ lens: AMetatype) { + _ = lens.foo + _ = lens.baz("hello") + } } // https://github.com/apple/swift/issues/52997 diff --git a/test/expr/unary/keypath/keypath-unsupported-methods.swift b/test/expr/unary/keypath/keypath-unsupported-methods.swift new file mode 100644 index 0000000000000..07d6b9aa4ff54 --- /dev/null +++ b/test/expr/unary/keypath/keypath-unsupported-methods.swift @@ -0,0 +1,48 @@ +// RUN: %target-typecheck-verify-swift -disable-experimental-parser-round-trip + +func test_keypath_with_method_refs() { + struct S { + func foo() -> Int { return 42 } + static func bar() -> Int { return 0 } + } + + let _: KeyPath = \.foo // expected-error {{key path cannot refer to instance method 'foo()'}} + // expected-error@-1 {{cannot assign value of type 'KeyPath Int>' to type 'KeyPath'}} + // expected-note@-2 {{arguments to generic parameter 'Value' ('() -> Int' and 'Int') are expected to be equal}} + let _: KeyPath = \.bar // expected-error {{key path cannot refer to static method 'bar()'}} + // expected-error@-1 {{cannot assign value of type 'KeyPath Int>' to type 'KeyPath'}} + // expected-note@-2 {{arguments to generic parameter 'Value' ('() -> Int' and 'Int') are expected to be equal}} + let _ = \S.Type.bar // expected-error {{key path cannot refer to static method 'bar()'}} + + struct A { + func foo() -> B { return B() } + static func faz() -> B { return B() } + } + + struct B { + var bar: Int = 42 + } + + let _: KeyPath = \.foo.bar // expected-error {{key path cannot refer to instance method 'foo()'}} + let _: KeyPath = \.faz.bar // expected-error {{key path cannot refer to static method 'faz()'}} + let _ = \A.foo.bar // expected-error {{key path cannot refer to instance method 'foo()'}} + let _ = \A.Type.faz.bar // expected-error {{key path cannot refer to static method 'faz()'}} +} + +// https://github.com/apple/swift/issues/54961 +// Compiler crash on invalid method reference in key path. + +protocol Zonk { + func wargle() +} +typealias Blatz = (gloop: String, zoop: Zonk?) + +func f_54961(fleep: [Blatz]) { + fleep.compactMap(\.zoop?.wargle) // expected-error {{key path cannot refer to instance method 'wargle()'}} +} + +// https://github.com/apple/swift/issues/56996 +func f_56996() { + _ = \Int.byteSwapped.signum() // expected-error {{key path cannot refer to instance method 'signum()'}} + _ = \Int.byteSwapped.init() // expected-error {{key path cannot refer to initializer 'init()'}} +} diff --git a/test/expr/unary/keypath/keypath.swift b/test/expr/unary/keypath/keypath.swift index 0e76f42740661..8bad4c9b13e5d 100644 --- a/test/expr/unary/keypath/keypath.swift +++ b/test/expr/unary/keypath/keypath.swift @@ -31,6 +31,8 @@ struct A: Hashable { let optLetProperty: Prop? subscript(sub: Sub) -> A { get { return self } set { } } + func foo(_ b: NonHashableSub) {} + static func foo(_ b: Sub) {} static func ==(_: A, _: A) -> Bool { fatalError() } func hash(into hasher: inout Hasher) { fatalError() } @@ -115,6 +117,11 @@ func testKeyPath(sub: Sub, optSub: OptSub, var l = \C.value expect(&l, toHaveType: Exactly, A>>.self) + + var hashableCapture = \A.Type.foo(Sub()) + expect(&hashableCapture, toHaveType: Exactly>.self) + // expected-error@+1 {{method argument of type 'NonHashableSub' in a key path must be Hashable}} + _ = \A.foo(NonHashableSub()) // expected-error@+1{{generic parameter 'T' could not be inferred}} _ = \C.value @@ -486,6 +493,7 @@ class CC { func testKeyPathOptional() { _ = \AA.c?.i _ = \AA.c!.i + _ = \AA.c?.i.hashValue // https://github.com/apple/swift/issues/48750 let path: KeyPath! = \CC.i @@ -819,18 +827,90 @@ func test_keypath_with_mutating_getter() { } func test_keypath_with_method_refs() { + enum ValidationError: Error { + case invalidYear + } + struct S { - func foo() -> Int { return 42 } - static func bar() -> Int { return 0 } + static let millenium = 3 + var year = 2024 + init() {} + init(val value: Int = 2024) { year = value } + + var add: (Int, Int) -> Int { return { $0 + $1 } } + func add(this: Int) -> Int { this + this} + func add(that: Int) -> Int { that + that } + static func subtract(_ val: Int) -> Int { return millenium - val } + nonisolated func nonisolatedNextYear() -> Int { return year + 1 } + consuming func consume() { print(year) } + func validateYear() throws { + if year < 0 { throw ValidationError.invalidYear } + } + func doubleValue(_ value: inout Int) { value *= 2 } + mutating func updateYear(to newYear: Int) { self.year = newYear } + func calculateFutureYear(after seconds: UInt64) async -> Int { + try? await Task.sleep(nanoseconds: seconds) + return year + 10 + } + func validateAndCalculateFutureYear(after seconds: UInt64) async throws -> Int { + try validateYear() + try await Task.sleep(nanoseconds: seconds) + return year + 10 + } + subscript(index: Int) -> Int { return year + index } } - let _: KeyPath = \.foo - // expected-error@-1 {{cannot assign value of type 'KeyPath Int>' to type 'KeyPath'}} - // expected-note@-2 {{arguments to generic parameter 'Value' ('() -> Int' and 'Int') are expected to be equal}} - let _: KeyPath = \.bar // expected-error {{static member 'bar()' cannot be used on instance of type 'S'}} - // expected-error@-1 {{cannot assign value of type 'KeyPath Int>' to type 'KeyPath'}} - // expected-note@-2 {{arguments to generic parameter 'Value' ('() -> Int' and 'Int') are expected to be equal}} - let _ = \S.Type.bar + let _: KeyPath Int> = \.add + let _: KeyPath Int> = \.add() + // expected-error@-1 {{cannot assign value of type 'KeyPath' to type 'KeyPath Int>'}} + // expected-note@-2 {{arguments to generic parameter 'Value' ('Int' and '(Int, Int) -> Int') are expected to be equal}} + let _: KeyPath = \.add() // expected-error {{type of expression is ambiguous without a type annotation}} + let _: KeyPath Int> = \.add(this:) + let _: KeyPath = \.add(that: 1) + let _: KeyPath Int> = \.subtract // expected-error {{static member 'subtract' cannot be used on instance of type 'S'}} + let _ = \S.Type.subtract(1) + let _: KeyPath Int> = \S.nonisolatedNextYear + let _: KeyPath = \S.nonisolatedNextYear() + do { + try S()[keyPath: \S.validateYear]() // expected-error {{cannot form key path to instance method with 'throws' or 'async'}} + } catch { + print("Validation failed: \(error)") + } + var value = 2025 + let _ = \S.doubleValue(&value) // expected-error {{cannot pass an inout argument to a keypath method}} + let _: KeyPath = \S.updateYear(to: 2025) // expected-error {{key path cannot refer to mutating method 'updateYear(to:)}} + let _: KeyPath = \S.calculateFutureYear(after: 5) // expected-error {{cannot form key path to instance method with 'throws' or 'async'}} + let _: KeyPath = \S.validateAndCalculateFutureYear(after: 5) // expected-error {{cannot form key path to instance method with 'throws' or 'async'}} + let _: KeyPath S> = \.init // expected-error {{static member 'init()' cannot be used on instance of type 'S'}} + let _: KeyPath S> = \.init(val:) // expected-error {{static member 'init(val:)' cannot be used on instance of type 'S'}} + let _: KeyPath = \.init(val: 2025) // expected-error {{static member 'init(val:)' cannot be used on instance of type 'S'}} + let _: KeyPath S> = \S.Type.init + let _: KeyPath S> = \S.Type.init(val:) + let _: KeyPath = \S.Type.init(val: 2025) + let _: KeyPath = \.init(val:2025).year + let _ = \S.Type.init(val: 2025).nonisolatedNextYear() + let _ = \S.Type.init()[0] + let _ = \S.Type.init(val: 2025).nonisolatedNextYear().signum() + let _ = \S.Type.init(val: 2025).nonisolatedNextYear().description + let _: KeyPath = \S.consume() + let _: AnyKeyPath = \S.add(this:) + let _: PartialKeyPath = \S.add + + class E: Hashable { + static func == (lhs: E, rhs: E) -> Bool { return lhs === rhs } + func hash(into hasher: inout Hasher) { hasher.combine(ObjectIdentifier(self)) } + private var storedClosure: (() -> Void)? + func saveClosure(_ closure: @escaping () -> Void) { storedClosure = closure } + } + class NonhashableE {} + struct BaseType { + func foo(_ e: E) {} + func foo(_ e: NonhashableE) {} + } + let hashableInstance = E() + let nonhashableInstance = NonhashableE() + let _ = \BaseType.foo(hashableInstance) + let _ = \BaseType.foo(nonhashableInstance) // expected-error {{method argument of type 'NonhashableE' in a key path must be Hashable}} struct A { func foo() -> B { return B() } @@ -839,17 +919,25 @@ func test_keypath_with_method_refs() { struct B { var bar: Int = 42 + func baz() -> Int { return 42 } + subscript(index: Int) -> Int { return index } } let _: KeyPath = \.foo.bar // expected-error {{type of expression is ambiguous without a type annotation}} let _: KeyPath = \.faz.bar // expected-error {{static member 'faz()' cannot be used on instance of type 'A'}} let _ = \A.foo.bar // expected-error {{type of expression is ambiguous without a type annotation}} let _ = \A.Type.faz.bar // expected-error {{type of expression is ambiguous without a type annotation}} + let _: KeyPath = \.foo().bar + let _: KeyPath = \.faz().bar + let _ = \A.foo().bar + let _ = \A.Type.faz().bar + let _: KeyPath = \.faz().bar + let _: KeyPath = \.foo().baz() + let _: KeyPath = \.foo().baz() + let _: KeyPath = \A.Type.faz()[0] } // https://github.com/apple/swift/issues/54961 -// Compiler crash on invalid method reference in key path. - protocol Zonk { func wargle() } From 98cd675eb9c4eefa28ae436065ee719a977ba40f Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Mon, 20 Jan 2025 10:44:34 -0800 Subject: [PATCH 29/29] Guard feature behind experimental flag. --- include/swift/Basic/Features.def | 1 + include/swift/Sema/CSFix.h | 9 +++++ lib/AST/FeatureSet.cpp | 1 + lib/Sema/CSDiagnostics.cpp | 6 +++ lib/Sema/CSDiagnostics.h | 27 +++++++++++++ lib/Sema/CSFix.cpp | 46 ++++++++++++++++------ lib/Sema/CSSimplify.cpp | 3 ++ test/Constraints/keypath.swift | 3 +- test/Constraints/rdar68155466.swift | 3 +- test/Interpreter/keypath.swift | 3 +- test/Interpreter/static_keypaths.swift | 7 ++-- test/SILGen/keypaths.swift | 3 +- test/attr/attr_dynamic_member_lookup.swift | 3 +- test/expr/unary/keypath/keypath.swift | 3 +- 14 files changed, 98 insertions(+), 20 deletions(-) diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index d806fe280ae2d..8221074a0c95d 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -294,6 +294,7 @@ EXPERIMENTAL_FEATURE(MacrosOnImports, true) EXPERIMENTAL_FEATURE(TupleConformances, false) EXPERIMENTAL_FEATURE(FullTypedThrows, false) EXPERIMENTAL_FEATURE(SameElementRequirements, false) +EXPERIMENTAL_FEATURE(KeyPathWithMethodMembers, false) // Whether to enable @_used and @_section attributes EXPERIMENTAL_FEATURE(SymbolLinkageMarkers, true) diff --git a/include/swift/Sema/CSFix.h b/include/swift/Sema/CSFix.h index c95a28f50f1ed..b016abaf4d1b4 100644 --- a/include/swift/Sema/CSFix.h +++ b/include/swift/Sema/CSFix.h @@ -2009,6 +2009,11 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix { // a key path component. MutatingGetter, // Allow a reference to a mutating method. + Method, + // Allow a reference to a initializer instance as a key path + // component. + Initializer, + // Allow a reference to an enum case as a key path component. MutatingMethod, // Allow a reference to an async or throwing method. AsyncOrThrowsMethod, @@ -2035,6 +2040,10 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix { case RefKind::MutatingGetter: return "allow reference to a member with mutating getter as a key " "path component"; + case RefKind::Method: + return "allow reference to a method as a key path component"; + case RefKind::Initializer: + return "allow reference to an init method as a key path component"; case RefKind::EnumCase: return "allow reference to an enum case as a key path component"; case RefKind::MutatingMethod: diff --git a/lib/AST/FeatureSet.cpp b/lib/AST/FeatureSet.cpp index 8371f4e9ca9cf..c26ae5958f810 100644 --- a/lib/AST/FeatureSet.cpp +++ b/lib/AST/FeatureSet.cpp @@ -125,6 +125,7 @@ UNINTERESTING_FEATURE(StructLetDestructuring) UNINTERESTING_FEATURE(MacrosOnImports) UNINTERESTING_FEATURE(AsyncCallerExecution) UNINTERESTING_FEATURE(ExtensibleEnums) +UNINTERESTING_FEATURE(KeyPathWithMethodMembers) static bool usesFeatureNonescapableTypes(Decl *decl) { auto containsNonEscapable = diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index db5a82770ccad..a12454dd9705b 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -6418,6 +6418,12 @@ bool InvalidMemberWithMutatingGetterInKeyPath::diagnoseAsError() { return true; } +bool UnsupportedMethodRefInKeyPath::diagnoseAsError() { + emitDiagnostic(diag::expr_keypath_not_property, getMember(), + isForKeyPathDynamicMemberLookup()); + return true; +} + bool InvalidMutatingMethodRefInKeyPath::diagnoseAsError() { emitDiagnostic(diag::expr_keypath_mutating_method, getMember(), isForKeyPathDynamicMemberLookup()); diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 1433525e5ce77..ff1346f404091 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1807,6 +1807,33 @@ class InvalidEnumCaseRefInKeyPath final : public InvalidMemberRefInKeyPath { bool diagnoseAsError() override; }; +/// Diagnose an attempt to reference a method or initializer as a key path +/// component. +/// +/// Only diagnosed if `-KeyPathWithMethodMember` feature flag is not set. +/// +/// ```swift +/// struct S { +/// init() { } +/// func foo() -> Int { return 42 } +/// static func bar() -> Int { return 0 } +/// } +/// +/// _ = \S.foo +/// _ = \S.Type.bar +/// _ = \S.init +/// ``` +class UnsupportedMethodRefInKeyPath final : public InvalidMemberRefInKeyPath { +public: + UnsupportedMethodRefInKeyPath(const Solution &solution, ValueDecl *method, + ConstraintLocator *locator) + : InvalidMemberRefInKeyPath(solution, method, locator) { + assert(isa(method) || isa(method)); + } + + bool diagnoseAsError() override; +}; + /// Diagnose an attempt to reference a mutating method as a key path component /// e.g. /// diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index fe9ad22a08dcb..677a1241d39bd 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -1251,6 +1251,11 @@ bool AllowInvalidRefInKeyPath::diagnose(const Solution &solution, getLocator()); return failure.diagnose(asNote); } + case RefKind::Method: + case RefKind::Initializer: { + UnsupportedMethodRefInKeyPath failure(solution, Member, getLocator()); + return failure.diagnose(asNote); + } case RefKind::MutatingMethod: { InvalidMutatingMethodRefInKeyPath failure(solution, Member, getLocator()); return failure.diagnose(asNote); @@ -1323,22 +1328,41 @@ AllowInvalidRefInKeyPath::forRef(ConstraintSystem &cs, Type baseType, cs, baseType, RefKind::MutatingGetter, member, locator); } - // Referencing mutating, throws or async method members is not currently - // allowed. - if (auto method = dyn_cast(member)) { - if (method->isAsyncContext()) - return AllowInvalidRefInKeyPath::create( - cs, baseType, RefKind::AsyncOrThrowsMethod, member, locator); - if (auto methodType = method->getInterfaceType()->getAs()) { - if (methodType->getResult()->getAs()->isThrowing()) + if (cs.getASTContext().LangOpts.hasFeature( + Feature::KeyPathWithMethodMembers)) { + // Referencing mutating, throws or async method members is not currently + // allowed. + if (auto method = dyn_cast(member)) { + if (method->isAsyncContext()) return AllowInvalidRefInKeyPath::create( cs, baseType, RefKind::AsyncOrThrowsMethod, member, locator); + if (auto methodType = + method->getInterfaceType()->getAs()) { + if (methodType->getResult()->getAs()->isThrowing()) + return AllowInvalidRefInKeyPath::create( + cs, baseType, RefKind::AsyncOrThrowsMethod, member, locator); + } + if (method->isMutating()) + return AllowInvalidRefInKeyPath::create( + cs, baseType, RefKind::MutatingMethod, member, locator); + return nullptr; } - if (method->isMutating()) - return AllowInvalidRefInKeyPath::create( - cs, baseType, RefKind::MutatingMethod, member, locator); + + if (isa(member)) + return nullptr; } + // Referencing (instance or static) methods in key path is + // not currently allowed. + if (isa(member)) + return AllowInvalidRefInKeyPath::create(cs, baseType, RefKind::Method, + member, locator); + + // Referencing initializers in key path is not currently allowed. + if (isa(member)) + return AllowInvalidRefInKeyPath::create(cs, baseType, RefKind::Initializer, + member, locator); + return nullptr; } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 51891f7752971..ff3eee981e2f6 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -10826,6 +10826,9 @@ static ConstraintFix *validateInitializerRef(ConstraintSystem &cs, // which means MetatypeType has to be added after finding a type variable. if (baseLocator->isLastElement()) baseType = MetatypeType::get(baseType); + } else if (auto *keyPathExpr = getAsExpr(anchor)) { + // Key path can't refer to initializers e.g. `\Type.init` + return AllowInvalidRefInKeyPath::forRef(cs, baseType, init, locator); } if (!baseType) diff --git a/test/Constraints/keypath.swift b/test/Constraints/keypath.swift index 719e46a45f717..9a6dc0cce9e57 100644 --- a/test/Constraints/keypath.swift +++ b/test/Constraints/keypath.swift @@ -1,4 +1,5 @@ -// RUN: %target-swift-frontend -typecheck -verify %S/Inputs/keypath.swift -primary-file %s +// RUN: %target-swift-frontend -enable-experimental-feature KeyPathWithMethodMembers -typecheck -verify %S/Inputs/keypath.swift -primary-file %s +// REQUIRES: swift_feature_KeyPathWithMethodMembers struct S { let i: Int diff --git a/test/Constraints/rdar68155466.swift b/test/Constraints/rdar68155466.swift index 61b0686244565..59def0cf2aa87 100644 --- a/test/Constraints/rdar68155466.swift +++ b/test/Constraints/rdar68155466.swift @@ -1,5 +1,6 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-experimental-feature KeyPathWithMethodMembers -typecheck -verify %s // REQUIRES: objc_interop +// REQUIRES: swift_feature_KeyPathWithMethodMembers import Foundation diff --git a/test/Interpreter/keypath.swift b/test/Interpreter/keypath.swift index 3a7655ce525d3..c2e9cc483b848 100644 --- a/test/Interpreter/keypath.swift +++ b/test/Interpreter/keypath.swift @@ -1,6 +1,7 @@ -// RUN: %target-run-simple-swift | %FileCheck %s +// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-feature -Xfrontend KeyPathWithMethodMembers) | %FileCheck %s // REQUIRES: executable_test +// REQUIRES: swift_feature_KeyPathWithMethodMembers // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime diff --git a/test/Interpreter/static_keypaths.swift b/test/Interpreter/static_keypaths.swift index 3f645c0217ee0..fb4565dd5bf1c 100644 --- a/test/Interpreter/static_keypaths.swift +++ b/test/Interpreter/static_keypaths.swift @@ -2,13 +2,13 @@ // RUN: split-file %s %t/src /// Build LibA -// RUN: %host-build-swift %t/src/LibA.swift -swift-version 5 -emit-module -emit-library -enable-library-evolution -module-name LibA -o %t/%target-library-name(LibA) -emit-module-interface-path %t/LibA.swiftinterface +// RUN: %host-build-swift %t/src/LibA.swift -swift-version 5 -enable-experimental-feature KeyPathWithMethodMembers -emit-module -emit-library -enable-library-evolution -module-name LibA -o %t/%target-library-name(LibA) -emit-module-interface-path %t/LibA.swiftinterface // Build LibB -// RUN: %target-build-swift %t/src/LibB.swift -I %t -L %t -l LibA -swift-version 5 -emit-module -emit-library -module-name LibB -o %t/%target-library-name(LibB) +// RUN: %target-build-swift %t/src/LibB.swift -I %t -L %t -l LibA -swift-version 5 -enable-experimental-feature KeyPathWithMethodMembers -emit-module -emit-library -module-name LibB -o %t/%target-library-name(LibB) // Build LibC -// RUN: %target-build-swift %t/src/LibC.swift -I %t -L %t -l LibA -swift-version 5 -emit-module -emit-library -module-name LibC -o %t/%target-library-name(LibC) +// RUN: %target-build-swift %t/src/LibC.swift -I %t -L %t -l LibA -swift-version 5 -enable-experimental-feature KeyPathWithMethodMembers -emit-module -emit-library -module-name LibC -o %t/%target-library-name(LibC) // Build & run main.swift // RUN: %target-build-swift -I %t -L %t -l LibA -l LibB -l LibC %t/src/main.swift -o %t/a.out @@ -20,6 +20,7 @@ // REQUIRES: executable_test // REQUIRES: OS=macosx +// REQUIRES: swift_feature_KeyPathWithMethodMembers // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime diff --git a/test/SILGen/keypaths.swift b/test/SILGen/keypaths.swift index df5c1915e29cc..2fab1f5de3a51 100644 --- a/test/SILGen/keypaths.swift +++ b/test/SILGen/keypaths.swift @@ -1,4 +1,5 @@ -// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types -target %target-swift-5.1-abi-triple -parse-stdlib -module-name keypaths %s | %FileCheck %s +// RUN: %target-swift-emit-silgen -enable-experimental-feature KeyPathWithMethodMembers -Xllvm -sil-print-types -target %target-swift-5.1-abi-triple -parse-stdlib -module-name keypaths %s | %FileCheck %s +// REQUIRES: swift_feature_KeyPathWithMethodMembers import Swift diff --git a/test/attr/attr_dynamic_member_lookup.swift b/test/attr/attr_dynamic_member_lookup.swift index b3d7e7623d1d6..19445b6e2407c 100644 --- a/test/attr/attr_dynamic_member_lookup.swift +++ b/test/attr/attr_dynamic_member_lookup.swift @@ -1,4 +1,5 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -enable-experimental-feature KeyPathWithMethodMembers +// REQUIRES: swift_feature_KeyPathWithMethodMembers var global = 42 diff --git a/test/expr/unary/keypath/keypath.swift b/test/expr/unary/keypath/keypath.swift index 8bad4c9b13e5d..363d3426c7a3f 100644 --- a/test/expr/unary/keypath/keypath.swift +++ b/test/expr/unary/keypath/keypath.swift @@ -1,4 +1,5 @@ -// RUN: %target-swift-frontend -typecheck -parse-as-library %s -verify +// RUN: %target-swift-frontend -enable-experimental-feature KeyPathWithMethodMembers -typecheck -parse-as-library %s -verify +// REQUIRES: swift_feature_KeyPathWithMethodMembers struct Sub: Hashable { static func ==(_: Sub, _: Sub) -> Bool { return true }