Skip to content

Commit 1e22859

Browse files
committed
Merge pull request #1229 from gregomni/generic-func-args
[Sema] Extend callee diagnoses to non-conforming complex args including generics.
2 parents 91dc1cf + 10370a5 commit 1e22859

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)