Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,12 @@ class alignas(1 << TypeAlignInBits) TypeBase
/// Check if this is a std.string type from C++.
bool isCxxString();

/// Check if this type is known to represent key paths.
bool isKnownKeyPathType();

/// Check if this type is known to represent immutable key paths.
bool isKnownImmutableKeyPathType();

/// Check if this is either an Array, Set or Dictionary collection type defined
/// at the top level of the Swift module
bool isKnownStdlibCollectionType();
Expand Down
4 changes: 0 additions & 4 deletions include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -6525,10 +6525,6 @@ class TypeVarRefCollector : public ASTWalker {
}
};

/// Determine whether given type is a known one
/// for a key path `{Any, Partial, Writable, ReferenceWritable}KeyPath`.
bool isKnownKeyPathType(Type type);

/// Determine whether the given type is a PartialKeyPath and
/// AnyKeyPath or existential type thererof, for example,
/// `PartialKeyPath<...> & Sendable`.
Expand Down
9 changes: 9 additions & 0 deletions lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1309,6 +1309,15 @@ bool TypeBase::isCxxString() {
ctx.Id_basic_string.is(clangDecl->getName());
}

bool TypeBase::isKnownKeyPathType() {
return isKeyPath() || isWritableKeyPath() || isReferenceWritableKeyPath() ||
isPartialKeyPath() || isAnyKeyPath();
}

bool TypeBase::isKnownImmutableKeyPathType() {
return isKeyPath() || isPartialKeyPath() || isAnyKeyPath();
}

bool TypeBase::isKnownStdlibCollectionType() {
if (isArray() || isDictionary() || isSet()) {
return true;
Expand Down
2 changes: 1 addition & 1 deletion lib/IDE/SelectedOverloadInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ swift::ide::getSelectedOverloadInfo(const Solution &S,
fnType->getParams()[0].getPlainType()->castTo<BoundGenericType>();

auto *keyPathDecl = keyPathTy->getAnyNominal();
assert(isKnownKeyPathType(keyPathTy) &&
assert(keyPathTy->isKnownKeyPathType() &&
"parameter is supposed to be a keypath");

auto KeyPathDynamicLocator = CS.getConstraintLocator(
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2689,7 +2689,7 @@ namespace {
Type keyPathTy = paramType;
if (auto *existential = keyPathTy->getAs<ExistentialType>()) {
keyPathTy = existential->getSuperclass();
assert(isKnownKeyPathType(keyPathTy));
assert(keyPathTy->isKnownKeyPathType());
}

SmallVector<Component, 2> components;
Expand Down
9 changes: 5 additions & 4 deletions lib/Sema/CSBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ void BindingSet::inferTransitiveBindings() {
auto bindingTy = binding.BindingType->lookThroughAllOptionalTypes();

Type inferredRootTy;
if (isKnownKeyPathType(bindingTy)) {
if (bindingTy->isKnownKeyPathType()) {
// AnyKeyPath doesn't have a root type.
if (bindingTy->isAnyKeyPath())
continue;
Expand Down Expand Up @@ -727,7 +727,8 @@ bool BindingSet::finalize(bool transitive) {
for (const auto &binding : Bindings) {
auto bindingTy = binding.BindingType->lookThroughAllOptionalTypes();

assert(isKnownKeyPathType(bindingTy) || bindingTy->is<FunctionType>());
assert(bindingTy->isKnownKeyPathType() ||
bindingTy->is<FunctionType>());

// Functions don't have capability so we can simply add them.
if (auto *fnType = bindingTy->getAs<FunctionType>()) {
Expand Down Expand Up @@ -1694,15 +1695,15 @@ PotentialBindings::inferFromRelational(ConstraintSystem &CS,
if (type->isExistentialType()) {
auto layout = type->getExistentialLayout();
if (auto superclass = layout.explicitSuperclass) {
if (isKnownKeyPathType(superclass)) {
if (superclass->isKnownKeyPathType()) {
type = superclass;
objectTy = superclass;
}
}
}
}

if (!(isKnownKeyPathType(objectTy) || objectTy->is<AnyFunctionType>()))
if (!(objectTy->isKnownKeyPathType() || objectTy->is<AnyFunctionType>()))
return std::nullopt;
}

Expand Down
6 changes: 3 additions & 3 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2262,7 +2262,7 @@ bool AssignmentFailure::diagnoseAsError() {

auto type = getType(immutableExpr);

if (isKnownKeyPathType(type))
if (type->isKnownKeyPathType())
message += " is read-only";
else if (VD->isCaptureList())
message += " is an immutable capture";
Expand Down Expand Up @@ -7487,7 +7487,7 @@ bool ArgumentMismatchFailure::diagnoseAsError() {

// Unresolved key path argument requires a tailored diagnostic
// that doesn't mention a fallback type - `KeyPath<_, _>`.
if (argType->isKeyPath() && !isKnownKeyPathType(paramType)) {
if (argType->isKeyPath() && !paramType->isKnownKeyPathType()) {
auto keyPathTy = argType->castTo<BoundGenericType>();
auto rootTy = keyPathTy->getGenericArgs()[0];
if (rootTy->is<UnresolvedType>()) {
Expand Down Expand Up @@ -7852,7 +7852,7 @@ bool ArgumentMismatchFailure::diagnoseKeyPathAsFunctionResultMismatch() const {
auto argType = getFromType();
auto paramType = getToType();

if (!isKnownKeyPathType(argType))
if (!argType->isKnownKeyPathType())
return false;

auto kpType = argType->castTo<BoundGenericType>();
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5598,7 +5598,7 @@ bool ConstraintSystem::repairFailures(
// fix-up here unless last component has already a invalid type or
// instance fix recorded.
if (isExpr<KeyPathExpr>(anchor)) {
if (isKnownKeyPathType(lhs) && isKnownKeyPathType(rhs)) {
if (lhs->isKnownKeyPathType() && rhs->isKnownKeyPathType()) {
// If we have a conversion happening here, we should let fix happen in
// simplifyRestrictedConstraint.
if (hasAnyRestriction())
Expand Down
6 changes: 0 additions & 6 deletions lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4363,12 +4363,6 @@ Solution::getFunctionArgApplyInfo(ConstraintLocator *locator) const {
fnInterfaceType, fnType, callee);
}

bool constraints::isKnownKeyPathType(Type type) {
return type->isKeyPath() || type->isWritableKeyPath() ||
type->isReferenceWritableKeyPath() || type->isPartialKeyPath() ||
type->isAnyKeyPath();
}

bool constraints::isTypeErasedKeyPathType(Type type) {
assert(type);

Expand Down
63 changes: 41 additions & 22 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "swift/AST/ClangModuleLoader.h"
#include "swift/AST/DeclExportabilityVisitor.h"
#include "swift/AST/DiagnosticsParse.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/NameLookup.h"
Expand Down Expand Up @@ -2232,9 +2233,6 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
/// The context does not involve a key path access.
None,

/// The context is an expression that is coerced to a read-only key path.
ReadOnlyCoercion,

/// The context is a key path application (`x[keyPath: \.member]`).
Application,
};
Expand Down Expand Up @@ -2361,15 +2359,6 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
FCE->getLoc(),
Where.getDeclContext());
}
if (auto DTBE = dyn_cast<DerivedToBaseExpr>(E)) {
if (auto ty = DTBE->getType()) {
if (ty->isKeyPath()) {
walkInKeyPathAccessContext(DTBE->getSubExpr(),
KeyPathAccessContext::ReadOnlyCoercion);
return Action::SkipChildren(E);
}
}
}
if (auto KP = dyn_cast<KeyPathExpr>(E)) {
maybeDiagKeyPath(KP);
}
Expand Down Expand Up @@ -2586,19 +2575,49 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {

StorageAccessKind getStorageAccessKindForKeyPath(KeyPathExpr *KP) const {
switch (KeyPathAccess) {
case KeyPathAccessContext::None:
// Use the key path's type to determine the access kind.
if (!KP->isObjC())
if (auto keyPathType = KP->getKeyPathType())
if (keyPathType->isKeyPath())
case KeyPathAccessContext::None: {
// Obj-C key paths are always considered mutable.
if (KP->isObjC())
return StorageAccessKind::GetSet;

// Check whether key path type indicates immutability, in which case only
// the getter will be used.
if (auto keyPathType = KP->getKeyPathType())
if (keyPathType->isKnownImmutableKeyPathType())
return StorageAccessKind::Get;

// Check if there's a conversion expression containing this one directly
// above it in the stack. If so, and that conversion is to an immutable
// key path type, then we should infer that use of the key path only
// implies use of the getter.
ArrayRef<const Expr *> exprs = ExprStack;
// Must be called while visiting the given key path expression.
ASSERT(exprs.back() == KP);

for (auto *expr : llvm::reverse(exprs.drop_back())) {
// Only traverse through conversions on the stack.
if (!isa<ImplicitConversionExpr>(expr) &&
!isa<OpenExistentialExpr>(expr))
break;

if (auto ty = expr->getType()) {
if (ty->isKnownImmutableKeyPathType())
return StorageAccessKind::Get;

return StorageAccessKind::GetSet;
if (auto existential = dyn_cast<ExistentialType>(ty)) {
if (auto superclass =
existential->getExistentialLayout().getSuperclass()) {
if (superclass->isKnownImmutableKeyPathType())
return StorageAccessKind::Get;
}
}
}
}

case KeyPathAccessContext::ReadOnlyCoercion:
// The type of this key path is being coerced to the type KeyPath<_, _> so
// ignore the actual key path type.
return StorageAccessKind::Get;
// We failed to prove that the key path is only used immutably,
// so diagnose both get and set access.
return StorageAccessKind::GetSet;
}

case KeyPathAccessContext::Application:
// The key path is being applied directly to a base so treat it as if it
Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/TypeOfReference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2392,13 +2392,13 @@ void ConstraintSystem::bindOverloadType(const SelectedOverload &overload,

if (auto *existential = paramTy->getAs<ExistentialType>()) {
paramTy = existential->getSuperclass();
assert(isKnownKeyPathType(paramTy));
assert(paramTy->isKnownKeyPathType());
}

auto keyPathTy = paramTy->castTo<BoundGenericType>();

auto *keyPathDecl = keyPathTy->getAnyNominal();
assert(isKnownKeyPathType(keyPathTy) &&
assert(keyPathTy->isKnownKeyPathType() &&
"parameter is supposed to be a keypath");

auto *keyPathLoc = getConstraintLocator(
Expand Down
48 changes: 41 additions & 7 deletions test/Availability/availability_accessors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ struct BaseStruct<T> {

var unavailableGetter: T {
@available(*, unavailable)
get { fatalError() } // expected-note 69 {{getter for 'unavailableGetter' has been explicitly marked unavailable here}}
get { fatalError() } // expected-note 73 {{getter for 'unavailableGetter' has been explicitly marked unavailable here}}
set { }
}

Expand All @@ -68,7 +68,7 @@ struct BaseStruct<T> {

var unavailableGetterAndSetter: T {
@available(*, unavailable)
get { fatalError() } // expected-note 68 {{getter for 'unavailableGetterAndSetter' has been explicitly marked unavailable here}}
get { fatalError() } // expected-note 71 {{getter for 'unavailableGetterAndSetter' has been explicitly marked unavailable here}}
@available(*, unavailable)
set { fatalError() } // expected-note 33 {{setter for 'unavailableGetterAndSetter' has been explicitly marked unavailable here}}
}
Expand Down Expand Up @@ -116,11 +116,6 @@ struct SubscriptHelper {
return t
}

func takesKeyPath<T, U>(_ t: T, _ keyPath: KeyPath<T, U>) -> () { }
func takesWritableKeyPath<T, U>(_ t: inout T, _ keyPath: WritableKeyPath<T, U>) -> () {
// expected-note@-1 2 {{in call to function 'takesWritableKeyPath'}}
}

func testDiscardedRValueLoads_Struct() {
var x = BaseStruct<StructValue>() // expected-warning {{variable 'x' was never mutated; consider changing to 'let' constant}}

Expand Down Expand Up @@ -548,13 +543,44 @@ func testDiscardedApplyOfFuncWithInOutParam_Class() {
func testKeyPathArguments_Struct() {
var x = BaseStruct<StructValue>()

func takesKeyPath<T, U>(_ t: T, _ keyPath: KeyPath<T, U>) -> () { }

takesKeyPath(x, \.available)
takesKeyPath(x, \.unavailableGetter) // expected-error {{getter for 'unavailableGetter' is unavailable}}
takesKeyPath(x, \.unavailableSetter)
takesKeyPath(x, \.unavailableGetterAndSetter) // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}}
takesKeyPath(x, \.deprecatedGetter) // expected-warning {{getter for 'deprecatedGetter' is deprecated: reading not recommended}}
takesKeyPath(x, \.deprecatedSetter)

func takesAnyKeyPath(_ keyPath: AnyKeyPath) -> () { }

takesAnyKeyPath(\BaseStruct<StructValue>.available)
takesAnyKeyPath(\BaseStruct<StructValue>.unavailableGetter) // expected-error {{getter for 'unavailableGetter' is unavailable}}
takesAnyKeyPath(\BaseStruct<StructValue>.unavailableSetter)
takesAnyKeyPath(\BaseStruct<StructValue>.unavailableGetterAndSetter) // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}}
takesAnyKeyPath(\BaseStruct<StructValue>.deprecatedGetter) // expected-warning {{getter for 'deprecatedGetter' is deprecated: reading not recommended}}
takesAnyKeyPath(\BaseStruct<StructValue>.deprecatedSetter)

func takesPartialKeyPath<T>(_ t: T, _ keyPath: PartialKeyPath<T>) -> () { }
takesPartialKeyPath(x, \.available)
takesPartialKeyPath(x, \.unavailableGetter) // expected-error {{getter for 'unavailableGetter' is unavailable}}
takesPartialKeyPath(x, \.unavailableSetter)
takesPartialKeyPath(x, \.unavailableGetterAndSetter) // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}}
takesPartialKeyPath(x, \.deprecatedGetter) // expected-warning {{getter for 'deprecatedGetter' is deprecated: reading not recommended}}
takesPartialKeyPath(x, \.deprecatedSetter)

func takesSendableKeyPath<T, U>(_ t: T, _ keyPath: KeyPath<T, U> & Sendable) -> () { }

takesSendableKeyPath(x, \.available)
takesSendableKeyPath(x, \.unavailableGetter) // expected-error {{getter for 'unavailableGetter' is unavailable}}
takesSendableKeyPath(x, \.unavailableSetter)
takesSendableKeyPath(x, \.unavailableGetterAndSetter) // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}}
takesSendableKeyPath(x, \.deprecatedGetter) // expected-warning {{getter for 'deprecatedGetter' is deprecated: reading not recommended}}
takesSendableKeyPath(x, \.deprecatedSetter)

func takesWritableKeyPath<T, U>(_ t: inout T, _ keyPath: WritableKeyPath<T, U>) -> () { }
// expected-note@-1 2 {{in call to function 'takesWritableKeyPath'}}

takesWritableKeyPath(&x, \.available)
takesWritableKeyPath(&x, \.unavailableGetter) // expected-error {{getter for 'unavailableGetter' is unavailable}}
// FIXME: Ideally we would diagnose the unavailability of the setter instead
Expand All @@ -566,6 +592,14 @@ func testKeyPathArguments_Struct() {
// expected-error@-1 {{generic parameter 'U' could not be inferred}}
takesWritableKeyPath(&x, \.deprecatedGetter) // expected-warning {{getter for 'deprecatedGetter' is deprecated: reading not recommended}}
takesWritableKeyPath(&x, \.deprecatedSetter) // expected-warning {{setter for 'deprecatedSetter' is deprecated: writing not recommended}}

func takesUnifiedTypes<T>(_: T, _: T) { }

takesUnifiedTypes(\BaseStruct<StructValue>.available, \BaseStruct<StructValue>.available)
takesUnifiedTypes(\BaseStruct<StructValue>.available, \BaseStruct<StructValue>.unavailableGetter) // expected-error {{getter for 'unavailableGetter' is unavailable}}
takesUnifiedTypes(\BaseStruct<StructValue>.available, \BaseStruct<StructValue>.unavailableSetter)
takesUnifiedTypes(\BaseStruct<StructValue>.available, \BaseStruct<StructValue>.deprecatedSetter) // expected-warning {{setter for 'deprecatedSetter' is deprecated: writing not recommended}}
takesUnifiedTypes(\BaseStruct<StructValue>.unavailableSetter, \BaseStruct<StructValue>.deprecatedSetter)
}

var global = BaseStruct<StructValue>()
Expand Down