Skip to content

Commit 1febcfa

Browse files
committed
[CSSimplify] Detect and diagnose generic argument mismatches individually
Generic arguments types are not always resolved enough to enable aggregated mismatch fixes, which means that the solver should be able to handle standalone generic argument matching constraints and create a fix per mismatch location to coalesce them during diagnostics. Resolves: rdar://106054263
1 parent c74cf13 commit 1febcfa

File tree

4 files changed

+101
-14
lines changed

4 files changed

+101
-14
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4655,6 +4655,10 @@ class ConstraintSystem {
46554655
/// Indicates that we are attempting a possible type for
46564656
/// a type variable.
46574657
TMF_BindingTypeVariable = 0x04,
4658+
4659+
/// Indicates that the solver is matching one of the
4660+
/// generic argument pairs as part of matching two generic types.
4661+
TMF_MatchingGenericArguments = 0x08,
46584662
};
46594663

46604664
/// Options that govern how type matching should proceed.

lib/Sema/CSSimplify.cpp

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3748,15 +3748,23 @@ ConstraintSystem::matchDeepEqualityTypes(Type type1, Type type2,
37483748
// Match up the generic arguments, exactly.
37493749

37503750
if (shouldAttemptFixes()) {
3751+
auto *baseLoc =
3752+
getConstraintLocator(locator, {LocatorPathElt::GenericType(bound1),
3753+
LocatorPathElt::GenericType(bound2)});
3754+
3755+
auto argMatchingFlags =
3756+
subflags | TMF_ApplyingFix | TMF_MatchingGenericArguments;
3757+
37513758
// Optionals have a lot of special diagnostics and only one
37523759
// generic argument so if we' re dealing with one, don't produce generic
37533760
// arguments mismatch fixes.
37543761
if (bound1->getDecl()->isOptionalDecl())
3755-
return matchDeepTypeArguments(*this, subflags, args1, args2, locator);
3762+
return matchDeepTypeArguments(*this, argMatchingFlags, args1, args2,
3763+
baseLoc);
37563764

37573765
SmallVector<unsigned, 4> mismatches;
37583766
auto result = matchDeepTypeArguments(
3759-
*this, subflags | TMF_ApplyingFix, args1, args2, locator,
3767+
*this, argMatchingFlags, args1, args2, baseLoc,
37603768
[&mismatches](unsigned position) { mismatches.push_back(position); });
37613769

37623770
if (mismatches.empty())
@@ -6369,7 +6377,8 @@ bool ConstraintSystem::repairFailures(
63696377
// failure e.g. `String bind T.Element`, so let's drop the generic argument
63706378
// path element and recurse in repairFailures to check and potentially
63716379
// record the requirement failure fix.
6372-
path.pop_back();
6380+
auto genericArgElt =
6381+
path.pop_back_val().castTo<LocatorPathElt::GenericArgument>();
63736382

63746383
// If we have something like ... -> type req # -> pack element #, we're
63756384
// solving a requirement of the form T : P where T is a type parameter pack
@@ -6381,7 +6390,57 @@ bool ConstraintSystem::repairFailures(
63816390
getConstraintLocator(anchor, path));
63826391
}
63836392

6384-
break;
6393+
// When the solver sets `TMF_MatchingGenericArguments` it means
6394+
// that it's matching generic argument pairs to identify any mismatches
6395+
// as part of larger matching of two generic types. Letting this
6396+
// fail results in a single fix that aggregates all mismatch locations.
6397+
//
6398+
// Types are not always resolved enough to enable that which means
6399+
// that the comparison should be delayed, which brings us here - a
6400+
// standalone constraint that represents such a match, in such cases
6401+
// we create a fix per mismatch location and coalesce them during
6402+
// diagnostics.
6403+
if (flags.contains(TMF_MatchingGenericArguments))
6404+
break;
6405+
6406+
Type fromType;
6407+
Type toType;
6408+
6409+
if (path.size() >= 2) {
6410+
if (path[path.size() - 2].is<LocatorPathElt::GenericType>()) {
6411+
fromType = path[path.size() - 2]
6412+
.castTo<LocatorPathElt::GenericType>()
6413+
.getType();
6414+
}
6415+
6416+
if (path[path.size() - 1].is<LocatorPathElt::GenericType>()) {
6417+
toType = path[path.size() - 1]
6418+
.castTo<LocatorPathElt::GenericType>()
6419+
.getType();
6420+
}
6421+
}
6422+
6423+
if (!fromType || !toType)
6424+
break;
6425+
6426+
// Drop both `GenericType` elements.
6427+
path.pop_back();
6428+
path.pop_back();
6429+
6430+
ConstraintFix *fix = nullptr;
6431+
if (!path.empty() && path.back().is<LocatorPathElt::AnyRequirement>()) {
6432+
fix = fixRequirementFailure(*this, fromType, toType, anchor, path);
6433+
} else {
6434+
fix = GenericArgumentsMismatch::create(
6435+
*this, fromType, toType, {genericArgElt.getIndex()},
6436+
getConstraintLocator(anchor, path));
6437+
}
6438+
6439+
if (!fix)
6440+
break;
6441+
6442+
conversionsOrFixes.push_back(fix);
6443+
return true;
63856444
}
63866445

63876446
case ConstraintLocator::ResultBuilderBodyResult: {
@@ -14023,7 +14082,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1402314082
case FixKind::RenameConflictingPatternVariables:
1402414083
case FixKind::MustBeCopyable:
1402514084
case FixKind::MacroMissingPound:
14026-
case FixKind::AllowGlobalActorMismatch: {
14085+
case FixKind::AllowGlobalActorMismatch:
14086+
case FixKind::GenericArgumentsMismatch: {
1402714087
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
1402814088
}
1402914089
case FixKind::IgnoreInvalidASTNode: {
@@ -14243,7 +14303,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1424314303
case FixKind::TreatKeyPathSubscriptIndexAsHashable:
1424414304
case FixKind::AllowInvalidRefInKeyPath:
1424514305
case FixKind::DefaultGenericArgument:
14246-
case FixKind::GenericArgumentsMismatch:
1424714306
case FixKind::AllowMutatingMemberOnRValueBase:
1424814307
case FixKind::AllowTupleSplatForSingleParameter:
1424914308
case FixKind::AllowNonClassTypeToConvertToAnyObject:

test/Constraints/generics.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,3 +1031,27 @@ func test_requirement_failures_in_ambiguous_context() {
10311031

10321032
f3(A()) // expected-error {{no exact matches in call to local function 'f3'}}
10331033
}
1034+
1035+
// rdar://106054263 - failed to produce a diagnostic upon generic argument mismatch
1036+
func test_mismatches_with_dependent_member_generic_arguments() {
1037+
struct Binding<T, U> {}
1038+
// expected-note@-1 {{arguments to generic parameter 'T' ('Double?' and 'Data.SomeAssociated') are expected to be equal}}
1039+
// expected-note@-2 {{arguments to generic parameter 'U' ('Int' and 'Data.SomeAssociated') are expected to be equal}}
1040+
1041+
struct Data : SomeProtocol {
1042+
typealias SomeAssociated = String
1043+
}
1044+
1045+
func test1<T: SomeProtocol>(_: Binding<T.SomeAssociated, T.SomeAssociated>, _: T) where T.SomeAssociated == String {
1046+
}
1047+
1048+
func test2<T: SomeProtocol>(_: Optional<T.SomeAssociated>, _: T) where T.SomeAssociated == String {
1049+
}
1050+
1051+
test1(Binding<Double?, Int>(), Data())
1052+
// expected-error@-1 {{cannot convert value of type 'Binding<Double?, Int>' to expected argument type 'Binding<Data.SomeAssociated, Data.SomeAssociated>'}}
1053+
1054+
test2(Optional<Int>(nil), Data())
1055+
// expected-error@-1 {{cannot convert value of type 'Optional<Int>' to expected argument type 'Optional<Data.SomeAssociated>'}}
1056+
// expected-note@-2 {{arguments to generic parameter 'Wrapped' ('Int' and 'Data.SomeAssociated') are expected to be equal}}
1057+
}

test/Generics/issue-55666.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,19 @@ struct W<T> {}
66

77
struct S<C1: Collection> {
88
init(){}
9-
// expected-note@+2 {{where 'C1.Element' = 'String', 'W<C2.Element>' = 'Int'}}
10-
// expected-note@+1 {{where 'C1.Element' = 'C1', 'W<C2.Element>' = 'C2.Element'}}
9+
// expected-note@+2 {{where 'C1.Element' = 'W<C1>', 'main.W<C2.Element>' = 'main.W<C2.Element>'}}
10+
// expected-note@+1 {{where 'C1.Element' = 'W<String>', 'W<C2.Element>' = 'W<Array<Int>.Element>'}}
1111
init<C2>(_ c2: W<C2>) where C2: Collection, C1.Element == W<C2.Element> {}
12-
// expected-note@+1 {{where 'C1.Element' = 'String', 'W<C2.Element>' = 'Int'}}
12+
// expected-note@+1 {{where 'C1.Element' = 'W<String>', 'W<C2.Element>' = 'W<Array<Int>.Element>'}}
1313
static func f<C2>(_ c2: W<C2>) where C2: Collection, C1.Element == W<C2.Element> {}
14-
// expected-note@+1 {{where 'C1.Element' = 'String', 'W<C2.Element>' = 'Int'}}
14+
// expected-note@+1 {{where 'C1.Element' = 'W<String>', 'W<C2.Element>' = 'W<Array<Int>.Element>'}}
1515
func instancef<C2>(_ c2: W<C2>) where C2: Collection, C1.Element == W<C2.Element> {}
1616
}
17-
let _ = S<[W<String>]>(W<[Int]>()) // expected-error{{initializer 'init(_:)' requires the types 'String' and 'Int' be equivalent}}
18-
let _ = S<[W<String>]>.f(W<[Int]>()) // expected-error{{static method 'f' requires the types 'String' and 'Int' be equivalent}}
19-
let _ = S<[W<String>]>().instancef(W<[Int]>()) // expected-error{{instance method 'instancef' requires the types 'String' and 'Int' be equivalent}}
17+
let _ = S<[W<String>]>(W<[Int]>()) // expected-error{{initializer 'init(_:)' requires the types 'W<String>' and 'W<Array<Int>.Element>' be equivalent}}
18+
let _ = S<[W<String>]>.f(W<[Int]>()) // expected-error{{static method 'f' requires the types 'W<String>' and 'W<Array<Int>.Element>' be equivalent}}
19+
let _ = S<[W<String>]>().instancef(W<[Int]>()) // expected-error{{instance method 'instancef' requires the types 'W<String>' and 'W<Array<Int>.Element>' be equivalent}}
2020

2121
// Archetypes requirement failure
2222
func genericFunc<C1: Collection, C2: Collection>(_ c2: W<C2>, c1: C1.Type) where C1.Element == W<C2.Element> {
23-
let _ = S<[W<C1>]>(W<C2>()) // expected-error{{initializer 'init(_:)' requires the types 'C1' and 'C2.Element' be equivalent}}
23+
let _ = S<[W<C1>]>(W<C2>()) // expected-error{{initializer 'init(_:)' requires the types 'W<C1>' and 'W<C2.Element>' be equivalent}}
2424
}

0 commit comments

Comments
 (0)