Skip to content

Sema: Fix deep equality matching for parameterized protocol types [5.7] #60516

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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
7 changes: 5 additions & 2 deletions include/swift/Sema/ConstraintLocatorPathElts.def
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,11 @@ CUSTOM_LOCATOR_PATH_ELT(ContextualType)
/// A result of an expression involving dynamic lookup.
SIMPLE_LOCATOR_PATH_ELT(DynamicLookupResult)

/// The superclass of a protocol existential type.
SIMPLE_LOCATOR_PATH_ELT(ExistentialSuperclassType)
/// The superclass of a protocol composition type.
SIMPLE_LOCATOR_PATH_ELT(ProtocolCompositionSuperclassType)

/// The constraint of an existential type.
SIMPLE_LOCATOR_PATH_ELT(ExistentialConstraintType)

/// The argument type of a function.
SIMPLE_LOCATOR_PATH_ELT(FunctionArgument)
Expand Down
53 changes: 40 additions & 13 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3328,7 +3328,41 @@ ConstraintSystem::matchDeepEqualityTypes(Type type1, Type type2,
}

// Handle existential types.
if (type1->isExistentialType() && type2->isExistentialType()) {
if (auto *existential1 = type1->getAs<ExistentialType>()) {
auto existential2 = type2->castTo<ExistentialType>();

auto result = matchTypes(existential1->getConstraintType(),
existential2->getConstraintType(),
ConstraintKind::Bind, subflags,
locator.withPathElement(
ConstraintLocator::ExistentialConstraintType));

if (result.isFailure())
return result;

return getTypeMatchSuccess();
}

// Arguments of parameterized protocol types have to match on the nose.
if (auto ppt1 = type1->getAs<ParameterizedProtocolType>()) {
auto ppt2 = type2->castTo<ParameterizedProtocolType>();

auto result = matchTypes(ppt1->getBaseType(),
ppt2->getBaseType(),
ConstraintKind::Bind, subflags,
locator.withPathElement(
ConstraintLocator::ParentType));

if (result.isFailure())
return result;

return matchDeepTypeArguments(*this, subflags,
ppt1->getArgs(),
ppt2->getArgs(),
locator);
}

if (type1->isExistentialType()) {
auto layout1 = type1->getExistentialLayout();
auto layout2 = type2->getExistentialLayout();

Expand All @@ -3351,24 +3385,16 @@ ConstraintSystem::matchDeepEqualityTypes(Type type1, Type type2,
if (!layout1.explicitSuperclass || !layout2.explicitSuperclass)
return getTypeMatchFailure(locator);

auto subLocator = locator.withPathElement(
ConstraintLocator::ProtocolCompositionSuperclassType);
auto result = matchTypes(layout1.explicitSuperclass,
layout2.explicitSuperclass,
ConstraintKind::Bind, subflags,
locator.withPathElement(
ConstraintLocator::ExistentialSuperclassType));
subLocator);
if (result.isFailure())
return result;
}

// Arguments of parameterized protocol types have to match on the nose.
if (auto ppt1 = type1->getAs<ParameterizedProtocolType>()) {
auto ppt2 = type2->castTo<ParameterizedProtocolType>();
return matchDeepTypeArguments(*this, subflags,
ppt1->getArgs(),
ppt2->getArgs(),
locator);
}

return getTypeMatchSuccess();
}

Expand Down Expand Up @@ -6423,7 +6449,8 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
// If we are matching types for equality, we might still have
// type variables inside the protocol composition's superclass
// constraint.
conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality);
if (desugar1->getKind() == desugar2->getKind())
conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality);
break;

default:
Expand Down
11 changes: 8 additions & 3 deletions lib/Sema/ConstraintLocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ unsigned LocatorPathElt::getNewSummaryFlags() const {
case ConstraintLocator::MemberRefBase:
case ConstraintLocator::UnresolvedMember:
case ConstraintLocator::ParentType:
case ConstraintLocator::ExistentialSuperclassType:
case ConstraintLocator::ExistentialConstraintType:
case ConstraintLocator::ProtocolCompositionSuperclassType:
case ConstraintLocator::LValueConversion:
case ConstraintLocator::DynamicType:
case ConstraintLocator::SubscriptMember:
Expand Down Expand Up @@ -391,8 +392,12 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const {
out << "parent type";
break;

case ExistentialSuperclassType:
out << "existential superclass type";
case ProtocolCompositionSuperclassType:
out << "protocol composition superclass type";
break;

case ExistentialConstraintType:
out << "existential constraint type";
break;

case LValueConversion:
Expand Down
3 changes: 2 additions & 1 deletion lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5191,7 +5191,8 @@ void constraints::simplifyLocator(ASTNode &anchor,
case ConstraintLocator::PlaceholderType:
case ConstraintLocator::SequenceElementType:
case ConstraintLocator::ConstructorMemberType:
case ConstraintLocator::ExistentialSuperclassType:
case ConstraintLocator::ExistentialConstraintType:
case ConstraintLocator::ProtocolCompositionSuperclassType:
break;

case ConstraintLocator::GenericArgument:
Expand Down
48 changes: 48 additions & 0 deletions test/Constraints/parameterized_existentials.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// RUN: %target-typecheck-verify-swift -disable-availability-checking

protocol P<A> {
associatedtype A
}

func f1(x: any P) -> any P<Int> {
// FIXME: Bad diagnostic
return x // expected-error {{type of expression is ambiguous without more context}}
}

func f2(x: any P<Int>) -> any P {
return x // okay
}

func f3(x: any P<Int>) -> any P<String> {
// FIXME: Misleading diagnostic
return x // expected-error {{cannot convert return expression of type 'String' to return type 'Int'}}
}

struct G<T> {}
// expected-note@-1 {{arguments to generic parameter 'T' ('any P<Int>' and 'any P') are expected to be equal}}
// expected-note@-2 {{arguments to generic parameter 'T' ('any P' and 'any P<Int>') are expected to be equal}}
// expected-note@-3 {{arguments to generic parameter 'T' ('any P<Int>' and 'any P<String>') are expected to be equal}}

func g1(x: G<any P>) -> G<any P<Int>> {
return x // expected-error {{cannot convert return expression of type 'G<any P>' to return type 'G<any P<Int>>'}}
}

func g2(x: G<any P<Int>>) -> G<any P> {
return x // expected-error {{cannot convert return expression of type 'G<any P<Int>>' to return type 'G<any P>'}}
}

func g3(x: G<any P<Int>>) -> G<any P<String>> {
return x // expected-error {{cannot convert return expression of type 'G<any P<Int>>' to return type 'G<any P<String>>'}}
}

func h1(x: (any P)?) -> (any P<Int>)? {
return x // expected-error {{cannot convert return expression of type '(any P)?' to return type '(any P<Int>)?'}}
}

func h2(x: (any P<Int>)?) -> (any P)? {
return x // okay
}

func h3(x: (any P<Int>)?) -> (any P<String>)? {
return x // expected-error {{cannot convert return expression of type '(any P<Int>)?' to return type '(any P<String>)?'}}
}