Skip to content

Commit a987861

Browse files
authored
Merge pull request #65806 from xedin/rdar-108977234
[CSSimplify] Detect and diagnose conformance failures related to AnyHashable conversion
2 parents fdf6d62 + c9c6311 commit a987861

13 files changed

+122
-60
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/AST/DiagnosticsClangImporter.h"
2525
#include "swift/AST/ExistentialLayout.h"
2626
#include "swift/AST/Expr.h"
27+
#include "swift/AST/GenericEnvironment.h"
2728
#include "swift/AST/GenericSignature.h"
2829
#include "swift/AST/ImportCache.h"
2930
#include "swift/AST/Initializer.h"
@@ -88,6 +89,54 @@ Type FailureDiagnostic::getRawType(ASTNode node) const {
8889
return S.getType(node);
8990
}
9091

92+
Type FailureDiagnostic::resolveType(Type rawType, bool reconstituteSugar,
93+
bool wantRValue) const {
94+
rawType = rawType.transform([&](Type type) -> Type {
95+
if (auto *typeVar = type->getAs<TypeVariableType>()) {
96+
auto resolvedType = S.simplifyType(typeVar);
97+
98+
if (!resolvedType->hasUnresolvedType())
99+
return resolvedType;
100+
101+
// If type variable was simplified to an unresolved pack expansion
102+
// type, let's examine its original pattern type because it could
103+
// contain type variables replaceable with their generic parameter
104+
// types.
105+
if (auto *expansion = resolvedType->getAs<PackExpansionType>()) {
106+
auto *locator = typeVar->getImpl().getLocator();
107+
auto *openedExpansionTy =
108+
locator->castLastElementTo<LocatorPathElt::PackExpansionType>()
109+
.getOpenedType();
110+
auto patternType = resolveType(openedExpansionTy->getPatternType());
111+
return PackExpansionType::get(patternType, expansion->getCountType());
112+
}
113+
114+
Type GP = typeVar->getImpl().getGenericParameter();
115+
return resolvedType->is<UnresolvedType>() && GP ? GP : resolvedType;
116+
}
117+
118+
if (type->hasElementArchetype()) {
119+
auto *env = getDC()->getGenericEnvironmentOfContext();
120+
return env->mapElementTypeIntoPackContext(type);
121+
}
122+
123+
if (auto *packType = type->getAs<PackType>()) {
124+
if (packType->getNumElements() == 1) {
125+
auto eltType = resolveType(packType->getElementType(0));
126+
if (auto expansion = eltType->getAs<PackExpansionType>())
127+
return expansion->getPatternType();
128+
}
129+
}
130+
131+
return type->isPlaceholder() ? Type(type->getASTContext().TheUnresolvedType)
132+
: type;
133+
});
134+
135+
if (reconstituteSugar)
136+
rawType = rawType->reconstituteSugar(/*recursive*/ true);
137+
return wantRValue ? rawType->getRValueType() : rawType;
138+
}
139+
91140
template <typename... ArgTypes>
92141
InFlightDiagnostic
93142
FailureDiagnostic::emitDiagnostic(ArgTypes &&... Args) const {

lib/Sema/CSDiagnostics.h

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -98,48 +98,7 @@ class FailureDiagnostic {
9898

9999
/// Resolve type variables present in the raw type, if any.
100100
Type resolveType(Type rawType, bool reconstituteSugar = false,
101-
bool wantRValue = true) const {
102-
rawType = rawType.transform([&](Type type) -> Type {
103-
if (auto *typeVar = type->getAs<TypeVariableType>()) {
104-
auto resolvedType = S.simplifyType(typeVar);
105-
106-
if (!resolvedType->hasUnresolvedType())
107-
return resolvedType;
108-
109-
// If type variable was simplified to an unresolved pack expansion
110-
// type, let's examine its original pattern type because it could
111-
// contain type variables replaceable with their generic parameter
112-
// types.
113-
if (auto *expansion = resolvedType->getAs<PackExpansionType>()) {
114-
auto *locator = typeVar->getImpl().getLocator();
115-
auto *openedExpansionTy =
116-
locator->castLastElementTo<LocatorPathElt::PackExpansionType>()
117-
.getOpenedType();
118-
auto patternType = resolveType(openedExpansionTy->getPatternType());
119-
return PackExpansionType::get(patternType, expansion->getCountType());
120-
}
121-
122-
Type GP = typeVar->getImpl().getGenericParameter();
123-
return resolvedType->is<UnresolvedType>() && GP ? GP : resolvedType;
124-
}
125-
126-
if (auto *packType = type->getAs<PackType>()) {
127-
if (packType->getNumElements() == 1) {
128-
auto eltType = resolveType(packType->getElementType(0));
129-
if (auto expansion = eltType->getAs<PackExpansionType>())
130-
return expansion->getPatternType();
131-
}
132-
}
133-
134-
return type->isPlaceholder()
135-
? Type(type->getASTContext().TheUnresolvedType)
136-
: type;
137-
});
138-
139-
if (reconstituteSugar)
140-
rawType = rawType->reconstituteSugar(/*recursive*/ true);
141-
return wantRValue ? rawType->getRValueType() : rawType;
142-
}
101+
bool wantRValue = true) const;
143102

144103
template <typename... ArgTypes>
145104
InFlightDiagnostic emitDiagnostic(ArgTypes &&... Args) const;

lib/Sema/CSSimplify.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2785,6 +2785,23 @@ assessRequirementFailureImpact(ConstraintSystem &cs, Type requirementType,
27852785
impact += choiceImpact - 1;
27862786
}
27872787
}
2788+
2789+
// If this requirement is associated with a call that is itself
2790+
// incorrect, let's increase impact to indicate that this failure
2791+
// has a compounding effect on viability of the overload choice it
2792+
// comes from.
2793+
if (locator.endsWith<LocatorPathElt::AnyRequirement>()) {
2794+
if (auto *expr = getAsExpr(anchor)) {
2795+
if (auto *call = getAsExpr<ApplyExpr>(cs.getParentExpr(expr))) {
2796+
if (call->getFn() == expr &&
2797+
llvm::any_of(cs.getFixes(), [&](const auto &fix) {
2798+
return getAsExpr(fix->getAnchor()) == call;
2799+
}))
2800+
impact += 2;
2801+
}
2802+
}
2803+
}
2804+
27882805
return impact;
27892806
}
27902807

@@ -8436,6 +8453,15 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
84368453
return SolutionKind::Solved;
84378454
}
84388455

8456+
// Conformance constraint that is introduced by an implicit conversion
8457+
// for example to `AnyHashable`.
8458+
if (kind == ConstraintKind::ConformsTo &&
8459+
loc->isLastElement<LocatorPathElt::ApplyArgToParam>()) {
8460+
auto *fix = AllowArgumentMismatch::create(*this, type, protocolTy, loc);
8461+
return recordFix(fix, /*impact=*/2) ? SolutionKind::Error
8462+
: SolutionKind::Solved;
8463+
}
8464+
84398465
// If this is an implicit Hashable conformance check generated for each
84408466
// index argument of the keypath subscript component, we could just treat
84418467
// it as though it conforms.

lib/Sema/ConstraintSystem.cpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4338,9 +4338,28 @@ static void diagnoseOperatorAmbiguity(ConstraintSystem &cs,
43384338
if (overloadType->hasTypeVariable())
43394339
continue;
43404340

4341-
if (auto *fnType = overloadType->getAs<FunctionType>())
4342-
parameters.insert(
4343-
FunctionType::getParamListAsString(fnType->getParams()));
4341+
auto overloadFnTy = overloadType->getAs<FunctionType>();
4342+
if (!overloadFnTy)
4343+
continue;
4344+
4345+
// If arguments to all parameters have been fixed then there is nothing
4346+
// to note about in this overload.
4347+
std::set<unsigned> fixedParams;
4348+
llvm::for_each(solution.Fixes, [&](const ConstraintFix *fix) {
4349+
auto *locator = fix->getLocator();
4350+
if (getAsExpr(locator->getAnchor()) != applyExpr)
4351+
return;
4352+
4353+
if (auto argLoc = locator->findLast<LocatorPathElt::ApplyArgToParam>()) {
4354+
fixedParams.insert(argLoc->getParamIdx());
4355+
}
4356+
});
4357+
4358+
if (fixedParams.size() == overloadFnTy->getNumParams())
4359+
continue;
4360+
4361+
parameters.insert(
4362+
FunctionType::getParamListAsString(overloadFnTy->getParams()));
43444363
}
43454364

43464365
// All of the overload choices had generic parameters like `Self`.

test/Constraints/bridging.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,6 @@ func rdar19831698() {
275275
var v71 = true + 1.0 // expected-error{{binary operator '+' cannot be applied to operands of type 'Bool' and 'Double'}}
276276
// expected-note@-1{{overloads for '+'}}
277277
var v72 = true + true // expected-error{{binary operator '+' cannot be applied to two 'Bool' operands}}
278-
// expected-note@-1{{overloads for '+'}}
279278
var v73 = true + [] // expected-error@:13 {{cannot convert value of type 'Bool' to expected argument type 'Array<Bool>'}}
280279
var v75 = true + "str" // expected-error@:13 {{cannot convert value of type 'Bool' to expected argument type 'String'}}
281280
}

test/Constraints/diagnostics.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -661,10 +661,7 @@ example21890157.property = "confusing" // expected-error {{value of optional ty
661661

662662

663663
struct UnaryOp {}
664-
665664
_ = -UnaryOp() // expected-error {{unary operator '-' cannot be applied to an operand of type 'UnaryOp'}}
666-
// expected-note@-1 {{overloads for '-' exist with these partially matching parameter lists: (Double), (Float)}}
667-
668665

669666
// <rdar://problem/23433271> Swift compiler segfault in failure diagnosis
670667
func f23433271(_ x : UnsafePointer<Int>) {}
@@ -1551,3 +1548,12 @@ func rdar86611718(list: [Int]) {
15511548
String(list.count())
15521549
// expected-error@-1 {{cannot call value of non-function type 'Int'}}
15531550
}
1551+
1552+
// rdar://108977234 - failed to produce diagnostic when argument to AnyHashable parameter doesn't conform to Hashable protocol
1553+
do {
1554+
struct NonHashable {}
1555+
1556+
func test(result: inout [AnyHashable], value: NonHashable) {
1557+
result.append(value) // expected-error {{argument type 'NonHashable' does not conform to expected type 'Hashable'}}
1558+
}
1559+
}

test/Constraints/fixes.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,5 +363,4 @@ func testKeyPathSubscriptArgFixes(_ fn: @escaping () -> Int) {
363363
// https://github.com/apple/swift/issues/54865
364364
func f_54865(a: Any, _ str: String?) {
365365
a == str // expected-error {{binary operator '==' cannot be applied to operands of type 'Any' and 'String?'}}
366-
// expected-note@-1 {{overloads for '==' exist with these partially matching parameter lists: (String, String)}}
367366
}

test/Constraints/generics.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,7 @@ func test_requirement_failures_in_ambiguous_context() {
10171017
func f1<T: Equatable>(_: T, _: T) {} // expected-note {{where 'T' = 'A'}}
10181018

10191019
f1(A(), B()) // expected-error {{local function 'f1' requires that 'A' conform to 'Equatable'}}
1020+
// expected-error@-1 {{cannot convert value of type 'B' to expected argument type 'A'}}
10201021

10211022
func f2<T: P_56173, U: P_56173>(_: T, _: U) {}
10221023
// expected-note@-1 {{candidate requires that 'B' conform to 'P_56173' (requirement specified as 'U' : 'P_56173')}}

test/Constraints/operator.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,9 @@ func rdar46459603() {
219219
var arr = ["key": e]
220220

221221
_ = arr.values == [e]
222-
// expected-error@-1 {{binary operator '==' cannot be applied to operands of type 'Dictionary<String, E>.Values' and '[E]'}}
222+
// expected-error@-1 {{referencing operator function '==' on 'Equatable' requires that 'Dictionary<String, E>.Values' conform to 'Equatable'}}
223+
// expected-error@-2 {{cannot convert value of type '[E]' to expected argument type 'Dictionary<String, E>.Values'}}
224+
223225
_ = [arr.values] == [[e]]
224226
// expected-error@-1 {{referencing operator function '==' on 'Array' requires that 'E' conform to 'Equatable'}} expected-note@-1 {{binary operator '==' cannot be synthesized for enums with associated values}}
225227
// expected-error@-2 {{cannot convert value of type 'Dictionary<String, E>.Values' to expected element type '[E]'}}

test/Constraints/optional.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,6 @@ func test_force_unwrap_not_being_too_eager() {
436436
func invalidOptionalChaining(a: Any) {
437437
a == "="? // expected-error {{cannot use optional chaining on non-optional value of type 'String'}}
438438
// expected-error@-1 {{binary operator '==' cannot be applied to operands of type 'Any' and 'String?'}}
439-
// expected-note@-2 {{overloads for '==' exist}}
440439
}
441440

442441
/// https://github.com/apple/swift/issues/54739

test/Constraints/pack-expansion-expressions.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,18 +64,16 @@ func outerArchetype<each T, U>(t: repeat each T, u: U) where repeat each T: P {
6464
func sameElement<each T, U>(t: repeat each T, u: U) where repeat each T: P, repeat each T == U {
6565
// expected-error@-1{{same-element requirements are not yet supported}}
6666

67-
// FIXME: Opened element archetypes in diagnostics
6867
let _: (repeat each T) = (repeat (each t).f(u))
69-
// expected-error@-1 {{cannot convert value of type 'U' to expected argument type 'τ_1_0'}}
68+
// expected-error@-1 {{cannot convert value of type 'U' to expected argument type 'each T'}}
7069
}
7170

7271
func forEachEach<each C, U>(c: repeat each C, function: (U) -> Void)
7372
where repeat each C: Collection, repeat (each C).Element == U {
7473
// expected-error@-1{{same-element requirements are not yet supported}}
7574

76-
// FIXME: Opened element archetypes in diagnostics
7775
_ = (repeat (each c).forEach(function))
78-
// expected-error@-1 {{cannot convert value of type '(U) -> Void' to expected argument type '(τ_1_0.Element) throws -> Void'}}
76+
// expected-error@-1 {{cannot convert value of type '(U) -> Void' to expected argument type '(each C.Element) throws -> Void'}}
7977
}
8078

8179
func typeReprPacks<each T: ExpressibleByIntegerLiteral>(_ t: repeat each T) {
@@ -332,6 +330,15 @@ func test_pack_expansions_with_closures() {
332330
takesVariadicFunction { y in fn(x, y) } // Ok
333331
takesVariadicFunction { y, z in fn(y, z) } // Ok
334332
}
333+
334+
// rdar://108977234 - invalid error non-pack type instead of missing `Hashable` conformance
335+
func testEscapingCapture<each T>(_ t: repeat each T) -> () -> [AnyHashable] {
336+
return {
337+
var result = [AnyHashable]()
338+
repeat result.append(each t) // expected-error {{argument type 'each T' does not conform to expected type 'Hashable'}}
339+
return result
340+
}
341+
}
335342
}
336343

337344
// rdar://107151854 - crash on invalid due to specialized pack expansion

test/StringProcessing/Parse/forward-slash-regex.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,6 @@ _ = /x/.../y/
131131

132132
_ = /x/...
133133
// expected-error@-1 {{unary operator '...' cannot be applied to an operand of type 'Regex<Substring>'}}
134-
// expected-note@-2 {{overloads for '...' exist with these partially matching parameter lists}}
135134

136135
do {
137136
_ = /x /...

test/stdlib/UnicodeScalarDiagnostics.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,8 @@ func test_UnicodeScalarDoesNotImplementArithmetic(_ us: UnicodeScalar, i: Int) {
1010
isString(&a1)
1111
// We don't check for the overload choices list on the overload note match because they may change on different platforms.
1212
let a2 = "a" - "b" // expected-error {{binary operator '-' cannot be applied to two 'String' operands}}
13-
// expected-note@-1 {{overloads for '-' exist with these partially matching parameter lists:}}
1413
let a3 = "a" * "b" // expected-error {{binary operator '*' cannot be applied to two 'String' operands}}
15-
// expected-note@-1 {{overloads for '*' exist with these partially matching parameter lists:}}
1614
let a4 = "a" / "b" // expected-error {{binary operator '/' cannot be applied to two 'String' operands}}
17-
// expected-note@-1 {{overloads for '/' exist with these partially matching parameter lists:}}
1815

1916
let b1 = us + us // expected-error {{binary operator '+' cannot be applied to two 'UnicodeScalar' (aka 'Unicode.Scalar') operands}}
2017
let b2 = us - us // expected-error {{binary operator '-' cannot be applied to two 'UnicodeScalar' (aka 'Unicode.Scalar') operands}}

0 commit comments

Comments
 (0)