From 98cd675eb9c4eefa28ae436065ee719a977ba40f Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Mon, 20 Jan 2025 10:44:34 -0800 Subject: [PATCH] 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 d806fe280ae..8221074a0c9 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 c95a28f50f1..b016abaf4d1 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 @@ public: 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 8371f4e9ca9..c26ae5958f8 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 db5a82770cc..a12454dd970 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 1433525e5ce..ff1346f4040 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1807,6 +1807,33 @@ public: 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 fe9ad22a08d..677a1241d39 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 51891f77529..ff3eee981e2 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 719e46a45f7..9a6dc0cce9e 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 61b06862445..59def0cf2aa 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 3a7655ce525..c2e9cc483b8 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 3f645c0217e..fb4565dd5bf 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 df5c1915e29..2fab1f5de3a 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 b3d7e7623d1..19445b6e240 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 8bad4c9b13e..363d3426c7a 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 }