Guard feature behind experimental flag.

This commit is contained in:
Amritpan Kaur
2025-01-20 10:44:34 -08:00
parent 15c219cd70
commit 98cd675eb9
14 changed files with 98 additions and 20 deletions

View File

@@ -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)

View File

@@ -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:

View File

@@ -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 =

View File

@@ -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());

View File

@@ -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<FuncDecl>(method) || isa<ConstructorDecl>(method));
}
bool diagnoseAsError() override;
};
/// Diagnose an attempt to reference a mutating method as a key path component
/// e.g.
///

View File

@@ -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,13 +1328,16 @@ AllowInvalidRefInKeyPath::forRef(ConstraintSystem &cs, Type baseType,
cs, baseType, RefKind::MutatingGetter, member, locator);
}
if (cs.getASTContext().LangOpts.hasFeature(
Feature::KeyPathWithMethodMembers)) {
// Referencing mutating, throws or async method members is not currently
// allowed.
if (auto method = dyn_cast<FuncDecl>(member)) {
if (method->isAsyncContext())
return AllowInvalidRefInKeyPath::create(
cs, baseType, RefKind::AsyncOrThrowsMethod, member, locator);
if (auto methodType = method->getInterfaceType()->getAs<AnyFunctionType>()) {
if (auto methodType =
method->getInterfaceType()->getAs<AnyFunctionType>()) {
if (methodType->getResult()->getAs<AnyFunctionType>()->isThrowing())
return AllowInvalidRefInKeyPath::create(
cs, baseType, RefKind::AsyncOrThrowsMethod, member, locator);
@@ -1337,8 +1345,24 @@ AllowInvalidRefInKeyPath::forRef(ConstraintSystem &cs, Type baseType,
if (method->isMutating())
return AllowInvalidRefInKeyPath::create(
cs, baseType, RefKind::MutatingMethod, member, locator);
return nullptr;
}
if (isa<ConstructorDecl>(member))
return nullptr;
}
// Referencing (instance or static) methods in key path is
// not currently allowed.
if (isa<FuncDecl>(member))
return AllowInvalidRefInKeyPath::create(cs, baseType, RefKind::Method,
member, locator);
// Referencing initializers in key path is not currently allowed.
if (isa<ConstructorDecl>(member))
return AllowInvalidRefInKeyPath::create(cs, baseType, RefKind::Initializer,
member, locator);
return nullptr;
}

View File

@@ -10826,6 +10826,9 @@ static ConstraintFix *validateInitializerRef(ConstraintSystem &cs,
// which means MetatypeType has to be added after finding a type variable.
if (baseLocator->isLastElement<LocatorPathElt::MemberRefBase>())
baseType = MetatypeType::get(baseType);
} else if (auto *keyPathExpr = getAsExpr<KeyPathExpr>(anchor)) {
// Key path can't refer to initializers e.g. `\Type.init`
return AllowInvalidRefInKeyPath::forRef(cs, baseType, init, locator);
}
if (!baseType)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 }