Skip to content

Commit 10370a5

Browse files
committed
Extend callee diagnoses to non-conforming complex args including generics.
Previously, type checking arguments worked fine if the entire arg was UnresolvedType, but if the type just contained UnresolvedType, the constraint system always failed via explicitly constraining to unresolved. Now in TypeCheckConstraints, if the solution allows for free variables that are UnresolvedType, then also convert any incoming UnresolvedTypes into variables. At worst, in the solution these just get converted back into the same Unresolved that they started with. This change allows for incorrect tuple/function type possibilities to make it back out to CSDiag, where they can be more precisely diagnosed with callee info. The rest of the changes are to correctly figure out the failure info when evaluating more types of Types. New diagnosis for a partial part of an arg type not confroming. Tests added for that. Expected errors changed in several places where we now get real types in the diagnosis instead of '(_)' unresolved.
1 parent 92fc7db commit 10370a5

File tree

6 files changed

+94
-41
lines changed

6 files changed

+94
-41
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,9 @@ ERROR(cannot_convert_argument_value,none,
287287
(Type,Type))
288288
ERROR(cannot_convert_argument_value_protocol,none,
289289
"argument type %0 does not conform to expected type %1", (Type, Type))
290+
ERROR(cannot_convert_partial_argument_value_protocol,none,
291+
"in argument type %0, %1 does not conform to expected type %2", (Type, Type, Type))
292+
290293
ERROR(cannot_convert_argument_value_nil,none,
291294
"nil is not compatible with expected argument type %0", (Type))
292295

lib/Sema/CSDiag.cpp

Lines changed: 69 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,9 +1121,8 @@ CalleeCandidateInfo::evaluateCloseness(DeclContext *dc, Type candArgListType,
11211121
continue;
11221122

11231123
// FIXME: Right now, a "matching" overload is one with a parameter whose
1124-
// type is identical to the argument type, or substitutable via
1125-
// rudimentary handling of functions with a single archetype in one or
1126-
// more parameters.
1124+
// type is identical to the argument type, or substitutable via handling
1125+
// of functions with a single archetype in one or more parameters.
11271126
// We can still do something more sophisticated with this.
11281127
// FIXME: Use TC.isConvertibleTo?
11291128

@@ -1151,13 +1150,9 @@ CalleeCandidateInfo::evaluateCloseness(DeclContext *dc, Type candArgListType,
11511150
if (nonSubstitutableArgs == 0)
11521151
continue;
11531152
++nonSubstitutableArgs;
1154-
// Fallthrough, this is nonsubstitutable, so mismatches as well.
1153+
mismatchesAreNearMisses = false;
11551154
} else {
1156-
if (nonSubstitutableArgs == 0) {
1157-
paramType = matchingArgType;
1158-
// Fallthrough as mismatched arg, comparing nearness to archetype
1159-
// bound type.
1160-
} else if (nonSubstitutableArgs == 1) {
1155+
if (nonSubstitutableArgs == 1) {
11611156
// If we have only one nonSubstitutableArg so far, then this different
11621157
// type might be the one that we should be substituting for instead.
11631158
// Note that failureInfo is already set correctly for that case.
@@ -1167,23 +1162,37 @@ CalleeCandidateInfo::evaluateCloseness(DeclContext *dc, Type candArgListType,
11671162
continue;
11681163
}
11691164
}
1165+
1166+
// This substitution doesn't match a previous substitution. Set up the nearMiss
1167+
// and failureInfo.paramType with the expected substitution inserted.
1168+
// (Note that this transform assumes only a single archetype.)
1169+
mismatchesAreNearMisses &= argumentMismatchIsNearMiss(substitution, matchingArgType);
1170+
paramType = paramType.transform(([&](Type type) -> Type {
1171+
if (type->is<SubstitutableType>())
1172+
return matchingArgType;
1173+
return type;
1174+
}));
11701175
}
11711176
} else {
11721177
matchingArgType = substitution;
11731178
singleArchetype = archetype;
11741179

1175-
if (CS->TC.isSubstitutableFor(substitution, archetype, CS->DC)) {
1180+
if (CS->TC.isSubstitutableFor(substitution, archetype, CS->DC))
11761181
continue;
1177-
}
1182+
1183+
if (auto argOptType = argType->getOptionalObjectType())
1184+
mismatchesAreNearMisses &= CS->TC.isSubstitutableFor(argOptType, archetype, CS->DC);
1185+
else
1186+
mismatchesAreNearMisses = false;
11781187
++nonSubstitutableArgs;
11791188
}
1189+
} else {
1190+
// Keep track of whether this argument was a near miss or not.
1191+
mismatchesAreNearMisses &= argumentMismatchIsNearMiss(argType, paramType);
11801192
}
11811193

11821194
++mismatchingArgs;
1183-
1184-
// Keep track of whether this argument was a near miss or not.
1185-
mismatchesAreNearMisses &= argumentMismatchIsNearMiss(argType, paramType);
1186-
1195+
11871196
failureInfo.argumentNumber = argNo;
11881197
failureInfo.parameterType = paramType;
11891198
if (paramType->hasTypeParameter())
@@ -1196,7 +1205,8 @@ CalleeCandidateInfo::evaluateCloseness(DeclContext *dc, Type candArgListType,
11961205

11971206
// Check to see if the first argument expects an inout argument, but is not
11981207
// an lvalue.
1199-
if (candArgs[0].Ty->is<InOutType>() && !actualArgs[0].Ty->isLValueType())
1208+
Type firstArg = actualArgs[0].Ty;
1209+
if (candArgs[0].Ty->is<InOutType>() && !(firstArg->isLValueType() || firstArg->is<InOutType>()))
12001210
return { CC_NonLValueInOut, {}};
12011211

12021212
// If we have exactly one argument mismatching, classify it specially, so that
@@ -1588,14 +1598,28 @@ bool CalleeCandidateInfo::diagnoseGenericParameterErrors(Expr *badArgExpr) {
15881598

15891599
for (unsigned i = 0, c = archetypes.size(); i < c; i++) {
15901600
auto archetype = archetypes[i];
1591-
auto argType = substitutions[i];
1601+
auto substitution = substitutions[i];
15921602

15931603
// FIXME: Add specific error for not subclass, if the archetype has a superclass?
1604+
1605+
// Check for optional near miss.
1606+
if (auto argOptType = substitution->getOptionalObjectType()) {
1607+
if (CS->TC.isSubstitutableFor(argOptType, archetype, CS->DC)) {
1608+
CS->TC.diagnose(badArgExpr->getLoc(), diag::missing_unwrap_optional, argType);
1609+
foundFailure = true;
1610+
continue;
1611+
}
1612+
}
15941613

15951614
for (auto proto : archetype->getConformsTo()) {
1596-
if (!CS->TC.conformsToProtocol(argType, proto, CS->DC, ConformanceCheckOptions(TR_InExpression))) {
1597-
CS->TC.diagnose(badArgExpr->getLoc(), diag::cannot_convert_argument_value_protocol,
1598-
argType, proto->getDeclaredType());
1615+
if (!CS->TC.conformsToProtocol(substitution, proto, CS->DC, ConformanceCheckOptions(TR_InExpression))) {
1616+
if (substitution->isEqual(argType)) {
1617+
CS->TC.diagnose(badArgExpr->getLoc(), diag::cannot_convert_argument_value_protocol,
1618+
substitution, proto->getDeclaredType());
1619+
} else {
1620+
CS->TC.diagnose(badArgExpr->getLoc(), diag::cannot_convert_partial_argument_value_protocol,
1621+
argType, substitution, proto->getDeclaredType());
1622+
}
15991623
foundFailure = true;
16001624
}
16011625
}
@@ -1647,7 +1671,11 @@ enum TCCFlags {
16471671
/// Re-type-check the given subexpression even if the expression has already
16481672
/// been checked already. The client is asserting that infinite recursion is
16491673
/// not possible because it has relaxed a constraint on the system.
1650-
TCC_ForceRecheck = 0x02
1674+
TCC_ForceRecheck = 0x02,
1675+
1676+
/// tell typeCheckExpression that it is ok to produce an ambiguous result,
1677+
/// it can just fill in holes with UnresolvedType and we'll deal with it.
1678+
TCC_AllowUnresolvedTypeVariables = 0x04
16511679
};
16521680

16531681
typedef OptionSet<TCCFlags> TCCOptions;
@@ -1716,7 +1744,8 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
17161744

17171745
/// Special magic to handle inout exprs and tuples in argument lists.
17181746
Expr *typeCheckArgumentChildIndependently(Expr *argExpr, Type argType,
1719-
const CalleeCandidateInfo &candidates);
1747+
const CalleeCandidateInfo &candidates,
1748+
TCCOptions options = TCCOptions());
17201749

17211750
/// Diagnose common failures due to applications of an argument list to an
17221751
/// ApplyExpr or SubscriptExpr.
@@ -2781,7 +2810,7 @@ typeCheckChildIndependently(Expr *subExpr, Type convertType,
27812810
// If there is no contextual type available, tell typeCheckExpression that it
27822811
// is ok to produce an ambiguous result, it can just fill in holes with
27832812
// UnresolvedType and we'll deal with it.
2784-
if (!convertType)
2813+
if (!convertType || options.contains(TCC_AllowUnresolvedTypeVariables))
27852814
TCEOptions |= TypeCheckExprFlags::AllowUnresolvedTypeVariables;
27862815

27872816
bool hadError = CS->TC.typeCheckExpression(subExpr, CS->DC, convertType,
@@ -3169,7 +3198,8 @@ void ConstraintSystem::diagnoseAssignmentFailure(Expr *dest, Type destTy,
31693198
/// Special magic to handle inout exprs and tuples in argument lists.
31703199
Expr *FailureDiagnosis::
31713200
typeCheckArgumentChildIndependently(Expr *argExpr, Type argType,
3172-
const CalleeCandidateInfo &candidates) {
3201+
const CalleeCandidateInfo &candidates,
3202+
TCCOptions options) {
31733203
// Grab one of the candidates (if present) and get its input list to help
31743204
// identify operators that have implicit inout arguments.
31753205
Type exampleInputType;
@@ -3203,7 +3233,6 @@ typeCheckArgumentChildIndependently(Expr *argExpr, Type argType,
32033233
if (!TE) {
32043234
// If the argument isn't a tuple, it is some scalar value for a
32053235
// single-argument call.
3206-
TCCOptions options;
32073236
if (exampleInputType && exampleInputType->is<InOutType>())
32083237
options |= TCC_AllowLValue;
32093238

@@ -3276,7 +3305,6 @@ typeCheckArgumentChildIndependently(Expr *argExpr, Type argType,
32763305
unsigned inArgNo = sources[i];
32773306
auto actualType = argTypeTT->getElementType(i);
32783307

3279-
TCCOptions options;
32803308
if (actualType->is<InOutType>())
32813309
options |= TCC_AllowLValue;
32823310

@@ -3341,7 +3369,6 @@ typeCheckArgumentChildIndependently(Expr *argExpr, Type argType,
33413369
exampleInputTuple = exampleInputType->getAs<TupleType>();
33423370

33433371
for (unsigned i = 0, e = TE->getNumElements(); i != e; i++) {
3344-
TCCOptions options;
33453372
if (exampleInputTuple && i < exampleInputTuple->getNumElements() &&
33463373
exampleInputTuple->getElementType(i)->is<InOutType>())
33473374
options |= TCC_AllowLValue;
@@ -3761,18 +3788,17 @@ bool FailureDiagnosis::diagnoseParameterErrors(CalleeCandidateInfo &CCI,
37613788
badArgExpr = argExpr;
37623789
}
37633790

3791+
// It could be that the argument doesn't conform to an archetype.
3792+
if (CCI.diagnoseGenericParameterErrors(badArgExpr))
3793+
return true;
3794+
37643795
// Re-type-check the argument with the expected type of the candidate set.
37653796
// This should produce a specific and tailored diagnostic saying that the
37663797
// type mismatches with expectations.
37673798
Type paramType = CCI.failedArgument.parameterType;
37683799
if (!typeCheckChildIndependently(badArgExpr, paramType,
37693800
CTP_CallArgument, options))
37703801
return true;
3771-
3772-
// If that fails, it could be that the argument doesn't conform to an
3773-
// archetype.
3774-
if (CCI.diagnoseGenericParameterErrors(badArgExpr))
3775-
return true;
37763802
}
37773803

37783804
return false;
@@ -4059,8 +4085,9 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
40594085
// Get the expression result of type checking the arguments to the call
40604086
// independently, so we have some idea of what we're working with.
40614087
//
4062-
auto argExpr = typeCheckArgumentChildIndependently(callExpr->getArg(),
4063-
argType, calleeInfo);
4088+
auto argExpr = typeCheckArgumentChildIndependently(callExpr->getArg(), argType,
4089+
calleeInfo,
4090+
TCC_AllowUnresolvedTypeVariables);
40644091
if (!argExpr)
40654092
return true; // already diagnosed.
40664093

@@ -4069,6 +4096,14 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
40694096
if (diagnoseParameterErrors(calleeInfo, callExpr->getFn(), argExpr))
40704097
return true;
40714098

4099+
// Force recheck of the arg expression because we allowed unresolved types
4100+
// before, and that turned out not to help, and now we want any diagnoses
4101+
// from disallowing them.
4102+
argExpr = typeCheckArgumentChildIndependently(callExpr->getArg(), argType,
4103+
calleeInfo, TCC_ForceRecheck);
4104+
if (!argExpr)
4105+
return true; // already diagnosed.
4106+
40724107
// Diagnose some simple and common errors.
40734108
if (calleeInfo.diagnoseSimpleErrors(callExpr->getLoc()))
40744109
return true;

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1141,6 +1141,14 @@ solveForExpression(Expr *&expr, DeclContext *dc, Type convertType,
11411141
auto constraintKind = ConstraintKind::Conversion;
11421142
if (cs.getContextualTypePurpose() == CTP_CallArgument)
11431143
constraintKind = ConstraintKind::ArgumentConversion;
1144+
1145+
if (allowFreeTypeVariables == FreeTypeVariableBinding::UnresolvedType) {
1146+
convertType = convertType.transform([&](Type type) -> Type {
1147+
if (type->is<UnresolvedType>())
1148+
return cs.createTypeVariable(cs.getConstraintLocator(expr), 0);
1149+
return type;
1150+
});
1151+
}
11441152

11451153
cs.addConstraint(constraintKind, expr->getType(), convertType,
11461154
cs.getConstraintLocator(expr), /*isFavored*/ true);
@@ -1318,7 +1326,7 @@ bool TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
13181326
// check for them now. We cannot apply the solution with unresolved TypeVars,
13191327
// because they will leak out into arbitrary places in the resultant AST.
13201328
if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables) &&
1321-
viable.size() != 1) {
1329+
(viable.size() != 1 || (convertType && convertType->hasUnresolvedType()))) {
13221330
expr->setType(ErrorType::get(Context));
13231331
return false;
13241332
}

test/Constraints/diagnostics.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,18 @@ for j in i.wibble(a, a) { // expected-error {{type 'A' does not conform to proto
8787
func f6<T:P2>(g: Void -> T) -> (c: Int, i: T) {
8888
return (c: 0, i: g())
8989
}
90+
9091
func f7() -> (c: Int, v: A) {
9192
let g: Void -> A = { return A() }
9293
return f6(g) // expected-error {{cannot convert return expression of type '(c: Int, i: A)' to return type '(c: Int, v: A)'}}
9394
}
9495

96+
func f8<T:P2>(n: T, _ f: (T) -> T) {}
97+
f8(3, f4) // expected-error {{in argument type '(Int) -> Int', 'Int' does not conform to expected type 'P2'}}
98+
typealias Tup = (Int, Double)
99+
func f9(x: Tup) -> Tup { return x }
100+
f8((1,2.0), f9) // expected-error {{in argument type '(Tup) -> Tup', 'Tup' (aka '(Int, Double)') does not conform to expected type 'P2'}}
101+
95102
// <rdar://problem/19658691> QoI: Incorrect diagnostic for calling nonexistent members on literals
96103
1.doesntExist(0) // expected-error {{value of type 'Int' has no member 'doesntExist'}}
97104
[1, 2, 3].doesntExist(0) // expected-error {{value of type '[Int]' has no member 'doesntExist'}}
@@ -643,7 +650,7 @@ let a = safeAssign // expected-error {{generic parameter 'T' could not be inferr
643650

644651
// <rdar://problem/21692808> QoI: Incorrect 'add ()' fixit with trailing closure
645652
func foo() -> [Int] {
646-
return Array <Int> (count: 1) { // expected-error {{cannot invoke initializer for type 'Array<Int>' with an argument list of type '(count: Int, () -> _)'}}
653+
return Array <Int> (count: 1) { // expected-error {{cannot invoke initializer for type 'Array<Int>' with an argument list of type '(count: Int, () -> Int)'}}
647654
// expected-note @-1 {{expected an argument list of type '(count: Int, repeatedValue: Element)'}}
648655
return 1
649656
}

test/Generics/deduction.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func useSwap(xi: Int, yi: Float) {
5656
mySwap(x, x) // expected-error {{passing value of type 'Int' to an inout parameter requires explicit '&'}} {{10-10=&}}
5757
// expected-error @-1 {{passing value of type 'Int' to an inout parameter requires explicit '&'}} {{13-13=&}}
5858

59-
mySwap(&x, &y) // expected-error{{cannot convert value of type 'inout Int' to expected argument type 'inout _'}}
59+
mySwap(&x, &y) // expected-error{{cannot convert value of type 'Float' to expected argument type 'Int'}}
6060
}
6161

6262
func takeTuples<T, U>(_: (T, U), _: (U, T)) {
@@ -208,14 +208,14 @@ func callMin(x: Int, y: Int, a: Float, b: Float) {
208208
min2(a, b) // expected-error{{argument type 'Float' does not conform to expected type 'IsBefore'}}
209209
}
210210

211-
func rangeOfIsBefore< // expected-note {{in call to function 'rangeOfIsBefore'}}
211+
func rangeOfIsBefore<
212212
R : GeneratorType where R.Element : IsBefore
213213
>(range : R) { }
214214

215215

216216
func callRangeOfIsBefore(ia: [Int], da: [Double]) {
217217
rangeOfIsBefore(ia.generate())
218-
rangeOfIsBefore(da.generate()) // expected-error{{generic parameter 'R' could not be inferred}}
218+
rangeOfIsBefore(da.generate()) // expected-error{{ambiguous reference to member 'generate()'}}
219219
}
220220

221221
//===----------------------------------------------------------------------===//

validation-test/stdlib/StdlibUnittestStaticAssertions.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ struct S2 {}
1616
func test_expectType() {
1717
var s1 = S1()
1818
expectType(S1.self, &s1)
19-
expectType(S2.self, &s1) // expected-error {{cannot convert value of type 'inout S1' to expected argument type 'inout _'}}
19+
expectType(S2.self, &s1) // expected-error {{cannot convert value of type 'S1' to expected argument type 'S2'}}
2020
}
2121

2222
func test_expectEqualType() {
2323
expectEqualType(S1.self, S1.self)
24-
expectEqualType(S1.self, S2.self) // expected-error {{cannot convert value of type 'S2.Type' to expected argument type 'S1'}}
24+
expectEqualType(S1.self, S2.self) // expected-error {{cannot convert value of type 'S2.Type' to expected argument type 'S1.Type'}}
2525
}
2626

0 commit comments

Comments
 (0)