Skip to content

Commit d18a7fd

Browse files
committed
Sema: Require CS to certify conversions associated w/ existential member accesses
1 parent 52d613e commit d18a7fd

File tree

8 files changed

+184
-74
lines changed

8 files changed

+184
-74
lines changed

include/swift/Sema/ConstraintLocatorPathElts.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,11 @@ SIMPLE_LOCATOR_PATH_ELT(CoercionOperand)
287287
/// A fallback type for some AST location (i.e. key path literal).
288288
SIMPLE_LOCATOR_PATH_ELT(FallbackType)
289289

290+
/// An implicit conversion (upcast) associated with an existential member access
291+
/// performed to abstract the member reference type away from context-specific
292+
/// types like `Self` that are not well-formed in the access context.
293+
SIMPLE_LOCATOR_PATH_ELT(ExistentialMemberAccessConversion)
294+
290295
#undef LOCATOR_PATH_ELT
291296
#undef CUSTOM_LOCATOR_PATH_ELT
292297
#undef SIMPLE_LOCATOR_PATH_ELT

include/swift/Sema/ConstraintSystem.h

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4442,8 +4442,7 @@ class ConstraintSystem {
44424442
/// determine the reference type of the member reference.
44434443
Type getMemberReferenceTypeFromOpenedType(
44444444
Type &openedType, Type baseObjTy, ValueDecl *value, DeclContext *outerDC,
4445-
ConstraintLocator *locator, bool hasAppliedSelf,
4446-
bool isStaticMemberRefOnProtocol, bool isDynamicResult,
4445+
ConstraintLocator *locator, bool hasAppliedSelf, bool isDynamicLookup,
44474446
OpenedTypeMap &replacements);
44484447

44494448
/// Retrieve the type of a reference to the given value declaration,
@@ -4453,16 +4452,14 @@ class ConstraintSystem {
44534452
/// this routine "opens up" the type by replacing each instance of a generic
44544453
/// parameter with a fresh type variable.
44554454
///
4456-
/// \param isDynamicResult Indicates that this declaration was found via
4455+
/// \param isDynamicLookup Indicates that this declaration was found via
44574456
/// dynamic lookup.
44584457
///
44594458
/// \returns a description of the type of this declaration reference.
44604459
DeclReferenceType getTypeOfMemberReference(
4461-
Type baseTy, ValueDecl *decl, DeclContext *useDC,
4462-
bool isDynamicResult,
4463-
FunctionRefKind functionRefKind,
4464-
ConstraintLocator *locator,
4465-
OpenedTypeMap *replacements = nullptr);
4460+
Type baseTy, ValueDecl *decl, DeclContext *useDC, bool isDynamicLookup,
4461+
FunctionRefKind functionRefKind, ConstraintLocator *locator,
4462+
OpenedTypeMap *replacements = nullptr);
44664463

44674464
/// Retrieve a list of generic parameter types solver has "opened" (replaced
44684465
/// with a type variable) at the given location.

lib/Sema/CSDiagnostics.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,17 +1323,18 @@ class UnintendedExtraGenericParamMemberFailure final
13231323
bool diagnoseAsError() override;
13241324
};
13251325

1326-
/// Diagnose cases where a member only accessible on generic constraints
1327-
/// requiring conformance to a protocol is used on a value of the
1328-
/// existential protocol type e.g.
1326+
/// Diagnose cases where a protocol member cannot be accessed with an
1327+
/// existential, e.g. due to occurrences of `Self` in non-covariant position in
1328+
/// the type of the member reference:
13291329
///
13301330
/// ```swift
1331+
/// struct G<T> {}
13311332
/// protocol P {
1332-
/// var foo: Self { get }
1333+
/// func foo() -> G<Self>
13331334
/// }
13341335
///
1335-
/// func bar<X : P>(p: X) {
1336-
/// p.foo
1336+
/// func bar(p: any P) {
1337+
/// p.foo()
13371338
/// }
13381339
/// ```
13391340
class InvalidMemberRefOnExistential final : public InvalidMemberRefFailure {

lib/Sema/CSSimplify.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5620,6 +5620,23 @@ bool ConstraintSystem::repairFailures(
56205620
}
56215621
}
56225622

5623+
// If there is a conversion associated with an existential member access
5624+
// along the path, the problem is that the constraint system does not support
5625+
// the (formally sane) upcast required to access the member.
5626+
if (llvm::find_if(path, [](const LocatorPathElt &elt) -> bool {
5627+
return elt.is<LocatorPathElt::ExistentialMemberAccessConversion>();
5628+
}) != path.end()) {
5629+
if (auto overload = findSelectedOverloadFor(castToExpr(anchor))) {
5630+
auto &choice = overload->choice;
5631+
conversionsOrFixes.push_back(AllowMemberRefOnExistential::create(
5632+
*this, choice.getBaseType(), choice.getDecl(),
5633+
DeclNameRef(choice.getDecl()->getName()),
5634+
getConstraintLocator(locator)));
5635+
5636+
return true;
5637+
}
5638+
}
5639+
56235640
auto elt = path.back();
56245641
switch (elt.getKind()) {
56255642
case ConstraintLocator::LValueConversion: {
@@ -15153,7 +15170,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1515315170
case FixKind::GenericArgumentsMismatch:
1515415171
case FixKind::AllowConcreteTypeSpecialization:
1515515172
case FixKind::IgnoreGenericSpecializationArityMismatch:
15156-
case FixKind::IgnoreKeyPathSubscriptIndexMismatch: {
15173+
case FixKind::IgnoreKeyPathSubscriptIndexMismatch:
15174+
case FixKind::AllowMemberRefOnExistential: {
1515715175
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
1515815176
}
1515915177
case FixKind::IgnoreThrownErrorMismatch: {
@@ -15376,7 +15394,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1537615394
case FixKind::RemoveCall:
1537715395
case FixKind::RemoveUnwrap:
1537815396
case FixKind::DefineMemberBasedOnUse:
15379-
case FixKind::AllowMemberRefOnExistential:
1538015397
case FixKind::AllowTypeOrInstanceMember:
1538115398
case FixKind::AllowInvalidPartialApplication:
1538215399
case FixKind::AllowInvalidInitRef:

lib/Sema/ConstraintLocator.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const {
112112
case ConstraintLocator::ThrownErrorType:
113113
case ConstraintLocator::FallbackType:
114114
case ConstraintLocator::KeyPathSubscriptIndex:
115+
case ConstraintLocator::ExistentialMemberAccessConversion:
115116
return 0;
116117

117118
case ConstraintLocator::FunctionArgument:
@@ -532,6 +533,9 @@ void LocatorPathElt::dump(raw_ostream &out) const {
532533
out << "key path subscript index parameter";
533534
break;
534535
}
536+
case ConstraintLocator::ExistentialMemberAccessConversion:
537+
out << "existential member access conversion";
538+
break;
535539
}
536540
}
537541

lib/Sema/ConstraintSystem.cpp

Lines changed: 74 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1316,6 +1316,14 @@ Type ConstraintSystem::getFixedTypeRecursive(Type type, TypeMatchOptions &flags,
13161316
return getFixedTypeRecursive(simplified, flags, wantRValue);
13171317
}
13181318

1319+
if (auto metatype = type->getAs<AnyMetatypeType>()) {
1320+
auto simplified = simplifyType(type);
1321+
if (simplified.getPointer() == type.getPointer())
1322+
return type;
1323+
1324+
return getFixedTypeRecursive(simplified, flags, wantRValue);
1325+
}
1326+
13191327
if (auto typeVar = type->getAs<TypeVariableType>()) {
13201328
if (auto fixed = getFixedType(typeVar))
13211329
return getFixedTypeRecursive(fixed, flags, wantRValue);
@@ -2420,10 +2428,25 @@ Type constraints::typeEraseOpenedArchetypesWithRoot(
24202428
/*force=*/true);
24212429
}
24222430

2431+
static bool isExistentialMemberAccessWithExplicitBaseExpression(
2432+
Type baseInstanceTy, ValueDecl *member, ConstraintLocator *locator,
2433+
bool isDynamicLookup) {
2434+
if (isDynamicLookup) {
2435+
return false;
2436+
}
2437+
2438+
// '.x' does not have an explicit base expression.
2439+
if (locator->isLastElement<LocatorPathElt::UnresolvedMember>()) {
2440+
return false;
2441+
}
2442+
2443+
return baseInstanceTy->isExistentialType() &&
2444+
member->getDeclContext()->getSelfProtocolDecl();
2445+
}
2446+
24232447
Type ConstraintSystem::getMemberReferenceTypeFromOpenedType(
24242448
Type &openedType, Type baseObjTy, ValueDecl *value, DeclContext *outerDC,
2425-
ConstraintLocator *locator, bool hasAppliedSelf,
2426-
bool isStaticMemberRefOnProtocol, bool isDynamicResult,
2449+
ConstraintLocator *locator, bool hasAppliedSelf, bool isDynamicLookup,
24272450
OpenedTypeMap &replacements) {
24282451
Type type = openedType;
24292452

@@ -2450,7 +2473,7 @@ Type ConstraintSystem::getMemberReferenceTypeFromOpenedType(
24502473

24512474
// Check if we need to apply a layer of optionality to the uncurried type.
24522475
if (!isRequirementOrWitness(locator)) {
2453-
if (isDynamicResult || value->getAttrs().hasAttribute<OptionalAttr>()) {
2476+
if (isDynamicLookup || value->getAttrs().hasAttribute<OptionalAttr>()) {
24542477
const auto applyOptionality = [&](FunctionType *fnTy) -> Type {
24552478
Type resultTy;
24562479
// Optional and dynamic subscripts are a special case, because the
@@ -2490,12 +2513,13 @@ Type ConstraintSystem::getMemberReferenceTypeFromOpenedType(
24902513
type = type->replaceSelfParameterType(baseObjTy);
24912514
}
24922515

2493-
// Superficially, protocol members with an existential base are accessed
2494-
// directly on the existential, and not an opened archetype, and we may have
2495-
// to adjust the type of the reference (e.g. covariant 'Self' type-erasure) to
2496-
// support certain accesses.
2497-
if (!isStaticMemberRefOnProtocol && !isDynamicResult &&
2498-
baseObjTy->isExistentialType() && outerDC->getSelfProtocolDecl() &&
2516+
// From the user perspective, protocol members that are accessed with an
2517+
// existential base are accessed directly on the existential, and not an
2518+
// opened archetype, so the type of the member reference must be abstracted
2519+
// away (upcast) from context-specific types like `Self` in covariant
2520+
// position.
2521+
if (isExistentialMemberAccessWithExplicitBaseExpression(
2522+
baseObjTy, value, locator, isDynamicLookup) &&
24992523
// If there are no type variables, there were no references to 'Self'.
25002524
type->hasTypeVariable()) {
25012525
const auto selfGP = cast<GenericTypeParamType>(
@@ -2504,13 +2528,6 @@ Type ConstraintSystem::getMemberReferenceTypeFromOpenedType(
25042528

25052529
type = typeEraseOpenedExistentialReference(type, baseObjTy, openedTypeVar,
25062530
TypePosition::Covariant);
2507-
2508-
Type contextualTy;
2509-
2510-
if (auto *anchor = getAsExpr(simplifyLocatorToAnchor(locator))) {
2511-
contextualTy =
2512-
getContextualType(getParentExpr(anchor), /*forConstraint=*/false);
2513-
}
25142531
}
25152532

25162533
// Construct an idealized parameter type of the initializer associated
@@ -2591,12 +2608,9 @@ bool ConstraintSystem::isPartialApplication(ConstraintLocator *locator) {
25912608
return level < (baseTy->is<MetatypeType>() ? 1 : 2);
25922609
}
25932610

2594-
DeclReferenceType
2595-
ConstraintSystem::getTypeOfMemberReference(
2596-
Type baseTy, ValueDecl *value, DeclContext *useDC,
2597-
bool isDynamicResult,
2598-
FunctionRefKind functionRefKind,
2599-
ConstraintLocator *locator,
2611+
DeclReferenceType ConstraintSystem::getTypeOfMemberReference(
2612+
Type baseTy, ValueDecl *value, DeclContext *useDC, bool isDynamicLookup,
2613+
FunctionRefKind functionRefKind, ConstraintLocator *locator,
26002614
OpenedTypeMap *replacementsPtr) {
26012615
// Figure out the instance type used for the base.
26022616
Type resolvedBaseTy = getFixedTypeRecursive(baseTy, /*wantRValue=*/true);
@@ -2619,10 +2633,11 @@ ConstraintSystem::getTypeOfMemberReference(
26192633
// metatype and `bar` is static member declared in a protocol or its
26202634
// extension.
26212635
bool isStaticMemberRefOnProtocol = false;
2622-
if (resolvedBaseTy->is<MetatypeType>() && baseObjTy->isExistentialType() &&
2623-
value->isStatic()) {
2624-
isStaticMemberRefOnProtocol =
2625-
locator->isLastElement<LocatorPathElt::UnresolvedMember>();
2636+
if (baseObjTy->isExistentialType() && value->isStatic() &&
2637+
locator->isLastElement<LocatorPathElt::UnresolvedMember>()) {
2638+
assert(resolvedBaseTy->is<MetatypeType>() &&
2639+
"Assumed base of unresolved member access must be a metatype");
2640+
isStaticMemberRefOnProtocol = true;
26262641
}
26272642

26282643
if (auto *typeDecl = dyn_cast<TypeDecl>(value)) {
@@ -2791,7 +2806,7 @@ ConstraintSystem::getTypeOfMemberReference(
27912806
// if it didn't conform.
27922807
addConstraint(ConstraintKind::Bind, baseOpenedTy, selfObjTy,
27932808
getConstraintLocator(locator));
2794-
} else if (!isDynamicResult) {
2809+
} else if (!isDynamicLookup) {
27952810
addSelfConstraint(*this, baseOpenedTy, selfObjTy, locator);
27962811
}
27972812

@@ -2857,14 +2872,14 @@ ConstraintSystem::getTypeOfMemberReference(
28572872
// Compute the type of the reference.
28582873
Type type = getMemberReferenceTypeFromOpenedType(
28592874
openedType, baseObjTy, value, outerDC, locator, hasAppliedSelf,
2860-
isStaticMemberRefOnProtocol, isDynamicResult, replacements);
2875+
isDynamicLookup, replacements);
28612876

28622877
// Do the same thing for the original type, if there can be any difference.
28632878
Type origType = type;
28642879
if (openedType.getPointer() != origOpenedType.getPointer()) {
28652880
origType = getMemberReferenceTypeFromOpenedType(
28662881
origOpenedType, baseObjTy, value, outerDC, locator, hasAppliedSelf,
2867-
isStaticMemberRefOnProtocol, isDynamicResult, replacements);
2882+
isDynamicLookup, replacements);
28682883
}
28692884

28702885
// If we opened up any type variables, record the replacements.
@@ -4035,6 +4050,35 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
40354050
}
40364051

40374052
if (auto *decl = choice.getDeclOrNull()) {
4053+
// If this is an existential member access and adjustments were made to the
4054+
// member reference type, require that the constraint system is happy with
4055+
// the ensuing conversion.
4056+
if (auto baseTy = choice.getBaseType()) {
4057+
baseTy = getFixedTypeRecursive(baseTy, /*wantRValue=*/true);
4058+
const auto instanceTy = baseTy->getMetatypeInstanceType();
4059+
4060+
if (isExistentialMemberAccessWithExplicitBaseExpression(
4061+
instanceTy, decl, locator,
4062+
/*isDynamicLookup=*/choice.getKind() ==
4063+
OverloadChoiceKind::DeclViaDynamic)) {
4064+
4065+
// Strip curried 'self' parameters.
4066+
auto fromTy = openedType->castTo<AnyFunctionType>()->getResult();
4067+
auto toTy = refType;
4068+
if (!doesMemberRefApplyCurriedSelf(baseTy, decl)) {
4069+
toTy = toTy->castTo<AnyFunctionType>()->getResult();
4070+
}
4071+
4072+
if (!fromTy->isEqual(toTy)) {
4073+
ConstraintLocatorBuilder conversionLocator = locator;
4074+
conversionLocator = conversionLocator.withPathElement(
4075+
ConstraintLocator::ExistentialMemberAccessConversion);
4076+
addConstraint(ConstraintKind::Conversion, fromTy, toTy,
4077+
conversionLocator);
4078+
}
4079+
}
4080+
}
4081+
40384082
// If the declaration is unavailable, note that in the score.
40394083
if (isDeclUnavailable(decl, locator))
40404084
increaseScore(SK_Unavailable, locator);
@@ -6293,6 +6337,7 @@ void constraints::simplifyLocator(ASTNode &anchor,
62936337
case ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice:
62946338
case ConstraintLocator::FallbackType:
62956339
case ConstraintLocator::KeyPathSubscriptIndex:
6340+
case ConstraintLocator::ExistentialMemberAccessConversion:
62966341
break;
62976342
}
62986343

test/Constraints/issue-67337.swift

Lines changed: 0 additions & 28 deletions
This file was deleted.

0 commit comments

Comments
 (0)