Skip to content

Commit 342e5f6

Browse files
authored
Merge pull request #64036 from xedin/rdar-106054263
[CSSimplify] Detect and diagnose generic argument mismatches individually
2 parents edb980e + b7745b0 commit 342e5f6

File tree

10 files changed

+163
-20
lines changed

10 files changed

+163
-20
lines changed

include/swift/Sema/CSFix.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,10 @@ class GenericArgumentsMismatch final
11031103
return {getTrailingObjects<unsigned>(), NumMismatches};
11041104
}
11051105

1106+
bool coalesceAndDiagnose(const Solution &solution,
1107+
ArrayRef<ConstraintFix *> secondaryFixes,
1108+
bool asNote = false) const override;
1109+
11061110
bool diagnose(const Solution &solution, bool asNote = false) const override;
11071111

11081112
static GenericArgumentsMismatch *create(ConstraintSystem &cs, Type actual,
@@ -1115,6 +1119,9 @@ class GenericArgumentsMismatch final
11151119
}
11161120

11171121
private:
1122+
bool diagnose(const Solution &solution, ArrayRef<unsigned> mismatches,
1123+
bool asNote = false) const;
1124+
11181125
MutableArrayRef<unsigned> getMismatchesBuf() {
11191126
return {getTrailingObjects<unsigned>(), NumMismatches};
11201127
}

include/swift/Sema/ConstraintLocator.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,20 @@ class LocatorPathElt::TupleType : public StoredPointerElement<TypeBase> {
643643
}
644644
};
645645

646+
class LocatorPathElt::GenericType : public StoredPointerElement<TypeBase> {
647+
public:
648+
GenericType(Type type)
649+
: StoredPointerElement(PathElementKind::GenericType, type.getPointer()) {
650+
assert(type->getDesugaredType()->is<BoundGenericType>());
651+
}
652+
653+
Type getType() const { return getStoredPointer(); }
654+
655+
static bool classof(const LocatorPathElt *elt) {
656+
return elt->getKind() == PathElementKind::GenericType;
657+
}
658+
};
659+
646660
/// Abstract superclass for any kind of tuple element.
647661
class LocatorPathElt::AnyTupleElement : public StoredIntegerElement<1> {
648662
protected:

include/swift/Sema/ConstraintLocatorPathElts.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,10 @@ CUSTOM_LOCATOR_PATH_ELT(SynthesizedArgument)
181181
/// path components.
182182
CUSTOM_LOCATOR_PATH_ELT(TupleType)
183183

184+
/// A generic type, which provides context for subsequent generic
185+
/// argument path components.
186+
CUSTOM_LOCATOR_PATH_ELT(GenericType)
187+
184188
/// Tuple elements.
185189
ABSTRACT_LOCATOR_PATH_ELT(AnyTupleElement)
186190
/// A tuple element referenced by position.

include/swift/Sema/ConstraintSystem.h

Lines changed: 5 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.
@@ -5257,6 +5261,7 @@ class ConstraintSystem {
52575261
/// \return true if at least some of the failures has been repaired
52585262
/// successfully, which allows type matcher to continue.
52595263
bool repairFailures(Type lhs, Type rhs, ConstraintKind matchKind,
5264+
TypeMatchOptions flags,
52605265
SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
52615266
ConstraintLocatorBuilder locator);
52625267

lib/Sema/CSFix.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -732,10 +732,31 @@ AllowFunctionTypeMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs,
732732
AllowFunctionTypeMismatch(cs, lhs, rhs, locator, index);
733733
}
734734

735+
bool GenericArgumentsMismatch::coalesceAndDiagnose(
736+
const Solution &solution, ArrayRef<ConstraintFix *> secondaryFixes,
737+
bool asNote) const {
738+
std::set<unsigned> scratch(getMismatches().begin(), getMismatches().end());
739+
740+
for (auto *fix : secondaryFixes) {
741+
auto *genericArgsFix = fix->castTo<GenericArgumentsMismatch>();
742+
for (auto mismatchIdx : genericArgsFix->getMismatches())
743+
scratch.insert(mismatchIdx);
744+
}
745+
746+
SmallVector<unsigned> mismatches(scratch.begin(), scratch.end());
747+
return diagnose(solution, mismatches, asNote);
748+
}
749+
750+
bool GenericArgumentsMismatch::diagnose(const Solution &solution,
751+
bool asNote) const {
752+
return diagnose(solution, getMismatches(), asNote);
753+
}
754+
735755
bool GenericArgumentsMismatch::diagnose(const Solution &solution,
756+
ArrayRef<unsigned> mismatches,
736757
bool asNote) const {
737758
GenericArgumentsMismatchFailure failure(solution, getFromType(), getToType(),
738-
getMismatches(), getLocator());
759+
mismatches, getLocator());
739760
return failure.diagnose(asNote);
740761
}
741762

lib/Sema/CSSimplify.cpp

Lines changed: 71 additions & 11 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())
@@ -4843,7 +4851,7 @@ static bool repairOutOfOrderArgumentsInBinaryFunction(
48434851
/// \return true if at least some of the failures has been repaired
48444852
/// successfully, which allows type matcher to continue.
48454853
bool ConstraintSystem::repairFailures(
4846-
Type lhs, Type rhs, ConstraintKind matchKind,
4854+
Type lhs, Type rhs, ConstraintKind matchKind, TypeMatchOptions flags,
48474855
SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
48484856
ConstraintLocatorBuilder locator) {
48494857
SmallVector<LocatorPathElt, 4> path;
@@ -5344,7 +5352,7 @@ bool ConstraintSystem::repairFailures(
53445352
// let's re-attempt to repair without l-value conversion in the
53455353
// locator to fix underlying type mismatch.
53465354
if (path.back().is<LocatorPathElt::FunctionResult>()) {
5347-
return repairFailures(lhs, rhs, matchKind, conversionsOrFixes,
5355+
return repairFailures(lhs, rhs, matchKind, flags, conversionsOrFixes,
53485356
getConstraintLocator(anchor, path));
53495357
}
53505358

@@ -6133,7 +6141,7 @@ bool ConstraintSystem::repairFailures(
61336141
if (!path.empty() && path.back().is<LocatorPathElt::PackType>())
61346142
path.pop_back();
61356143

6136-
return repairFailures(lhs, rhs, matchKind, conversionsOrFixes,
6144+
return repairFailures(lhs, rhs, matchKind, flags, conversionsOrFixes,
61376145
getConstraintLocator(anchor, path));
61386146
}
61396147

@@ -6369,19 +6377,70 @@ 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
63766385
if (!path.empty() && path.back().is<LocatorPathElt::PackElement>())
63776386
path.pop_back();
63786387

63796388
if (!path.empty() && path.back().is<LocatorPathElt::AnyRequirement>()) {
6380-
return repairFailures(lhs, rhs, matchKind, conversionsOrFixes,
6389+
return repairFailures(lhs, rhs, matchKind, flags, conversionsOrFixes,
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: {
@@ -7478,7 +7537,8 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
74787537
// Attempt fixes iff it's allowed, both types are concrete and
74797538
// we are not in the middle of attempting one already.
74807539
if (shouldAttemptFixes() && !flags.contains(TMF_ApplyingFix)) {
7481-
if (repairFailures(type1, type2, kind, conversionsOrFixes, locator)) {
7540+
if (repairFailures(type1, type2, kind, flags, conversionsOrFixes,
7541+
locator)) {
74827542
if (conversionsOrFixes.empty())
74837543
return getTypeMatchSuccess();
74847544
}
@@ -14038,7 +14098,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1403814098
case FixKind::MustBeCopyable:
1403914099
case FixKind::AllowInvalidPackElement:
1404014100
case FixKind::MacroMissingPound:
14041-
case FixKind::AllowGlobalActorMismatch: {
14101+
case FixKind::AllowGlobalActorMismatch:
14102+
case FixKind::GenericArgumentsMismatch: {
1404214103
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
1404314104
}
1404414105
case FixKind::IgnoreInvalidASTNode: {
@@ -14258,7 +14319,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1425814319
case FixKind::TreatKeyPathSubscriptIndexAsHashable:
1425914320
case FixKind::AllowInvalidRefInKeyPath:
1426014321
case FixKind::DefaultGenericArgument:
14261-
case FixKind::GenericArgumentsMismatch:
1426214322
case FixKind::AllowMutatingMemberOnRValueBase:
1426314323
case FixKind::AllowTupleSplatForSingleParameter:
1426414324
case FixKind::AllowNonClassTypeToConvertToAnyObject:

lib/Sema/ConstraintLocator.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const {
6767
case ConstraintLocator::GenericParameter:
6868
case ConstraintLocator::GenericArgument:
6969
case ConstraintLocator::TupleType:
70+
case ConstraintLocator::GenericType:
7071
case ConstraintLocator::NamedTupleElement:
7172
case ConstraintLocator::TupleElement:
7273
case ConstraintLocator::ProtocolRequirement:
@@ -242,6 +243,12 @@ void LocatorPathElt::dump(raw_ostream &out) const {
242243
break;
243244
}
244245

246+
case ConstraintLocator::GenericType: {
247+
auto genericTyElt = elt.castTo<LocatorPathElt::GenericType>();
248+
out << "generic type '" << genericTyElt.getType()->getString(PO) << "'";
249+
break;
250+
}
251+
245252
case ConstraintLocator::NamedTupleElement: {
246253
auto tupleElt = elt.castTo<LocatorPathElt::NamedTupleElement>();
247254
out << "named tuple element #" << llvm::utostr(tupleElt.getIndex());

lib/Sema/ConstraintSystem.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5393,6 +5393,7 @@ void constraints::simplifyLocator(ASTNode &anchor,
53935393
continue;
53945394

53955395
case ConstraintLocator::TupleType:
5396+
case ConstraintLocator::GenericType:
53965397
path = path.slice(1);
53975398
continue;
53985399

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)