Skip to content

Commit 57de9c4

Browse files
author
ematejska
authored
Merge pull request #11236 from xedin/rdar-33477726-4.0
[4.0] [QoI] Improve diagnostics of unsatisfied generic requirements
2 parents 1d33945 + becbcf3 commit 57de9c4

File tree

8 files changed

+180
-45
lines changed

8 files changed

+180
-45
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,6 +1387,14 @@ NOTE(type_does_not_inherit_requirement,none,
13871387
ERROR(types_not_equal,none,
13881388
"%0 requires the types %1 and %2 be equivalent",
13891389
(Type, Type, Type))
1390+
NOTE(candidate_types_equal_requirement,none,
1391+
"candidate requires that the types %0 and %1 be equivalent "
1392+
"(requirement specified as %2 == %3%4)",
1393+
(Type, Type, Type, Type, StringRef))
1394+
NOTE(candidate_types_inheritance_requirement,none,
1395+
"candidate requires that %1 inherit from %2 "
1396+
"(requirement specified as %2 : %3%4)",
1397+
(Type, Type, Type, Type, StringRef))
13901398
NOTE(types_not_equal_requirement,none,
13911399
"requirement specified as %0 == %1%2", (Type, Type, StringRef))
13921400
ERROR(non_class_cannot_conform_to_class_protocol,none,

lib/Sema/CSDiag.cpp

Lines changed: 92 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2152,8 +2152,8 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
21522152

21532153
/// Produce diagnostic for failures related to unfulfilled requirements
21542154
/// of the generic parameters used as arguments.
2155-
bool diagnoseArgumentGenericRequirements(TypeChecker &TC, Expr *fnExpr,
2156-
Expr *argExpr,
2155+
bool diagnoseArgumentGenericRequirements(TypeChecker &TC, Expr *callExpr,
2156+
Expr *fnExpr, Expr *argExpr,
21572157
CalleeCandidateInfo &candidates,
21582158
ArrayRef<Identifier> argLabels);
21592159

@@ -5886,16 +5886,18 @@ bool FailureDiagnosis::diagnoseMethodAttributeFailures(
58865886
}
58875887

58885888
bool FailureDiagnosis::diagnoseArgumentGenericRequirements(
5889-
TypeChecker &TC, Expr *fnExpr, Expr *argExpr,
5889+
TypeChecker &TC, Expr *callExpr, Expr *fnExpr, Expr *argExpr,
58905890
CalleeCandidateInfo &candidates, ArrayRef<Identifier> argLabels) {
58915891
if (candidates.closeness != CC_ExactMatch || candidates.size() != 1)
58925892
return false;
58935893

5894-
auto DRE = dyn_cast<DeclRefExpr>(fnExpr);
5895-
if (!DRE)
5896-
return false;
5894+
AbstractFunctionDecl *AFD = nullptr;
5895+
if (auto *DRE = dyn_cast<DeclRefExpr>(fnExpr)) {
5896+
AFD = dyn_cast<AbstractFunctionDecl>(DRE->getDecl());
5897+
} else if (auto *candidate = candidates[0].getDecl()) {
5898+
AFD = dyn_cast<AbstractFunctionDecl>(candidate);
5899+
}
58975900

5898-
auto AFD = dyn_cast<AbstractFunctionDecl>(DRE->getDecl());
58995901
if (!AFD || !AFD->isGeneric() || !AFD->hasInterfaceType())
59005902
return false;
59015903

@@ -5920,7 +5922,9 @@ bool FailureDiagnosis::diagnoseArgumentGenericRequirements(
59205922
// requirements e.g. <A, B where A.Element == B.Element>.
59215923
for (unsigned i = 0, e = bindings.size(); i != e; ++i) {
59225924
auto param = params[i];
5923-
auto archetype = param.Ty->getAs<ArchetypeType>();
5925+
auto paramType = param.Ty->getInOutObjectType();
5926+
5927+
auto archetype = paramType->getAs<ArchetypeType>();
59245928
if (!archetype)
59255929
continue;
59265930

@@ -5948,18 +5952,88 @@ bool FailureDiagnosis::diagnoseArgumentGenericRequirements(
59485952
return false;
59495953

59505954
class RequirementsListener : public GenericRequirementsCheckListener {
5955+
TypeChecker &TC;
5956+
AbstractFunctionDecl *Candidate;
5957+
TypeSubstitutionFn Substitutions;
5958+
5959+
Expr *CallExpr;
5960+
Expr *FnExpr;
5961+
Expr *ArgExpr;
5962+
59515963
public:
5964+
RequirementsListener(TypeChecker &TC, AbstractFunctionDecl *AFD,
5965+
TypeSubstitutionFn subs,
5966+
Expr *callExpr, Expr *fnExpr, Expr *argExpr)
5967+
: TC(TC), Candidate(AFD), Substitutions(subs), CallExpr(callExpr),
5968+
FnExpr(fnExpr), ArgExpr(argExpr) {}
5969+
59525970
bool shouldCheck(RequirementKind kind, Type first, Type second) override {
59535971
// This means that we have encountered requirement which references
59545972
// generic parameter not used in the arguments, we can't diagnose it here.
59555973
return !(first->hasTypeParameter() || first->isTypeVariableOrMember());
59565974
}
5975+
5976+
bool diagnoseUnsatisfiedRequirement(const Requirement &req, Type first,
5977+
Type second) override {
5978+
Diag<Type, Type, Type, Type, StringRef> note;
5979+
switch (req.getKind()) {
5980+
case RequirementKind::Conformance:
5981+
case RequirementKind::Layout:
5982+
return false;
5983+
5984+
case RequirementKind::Superclass:
5985+
note = diag::candidate_types_inheritance_requirement;
5986+
break;
5987+
5988+
case RequirementKind::SameType:
5989+
note = diag::candidate_types_equal_requirement;
5990+
break;
5991+
}
5992+
5993+
SmallVector<char, 8> scratch;
5994+
auto overloadName = Candidate->getFullName().getString(scratch);
5995+
5996+
if (isa<BinaryExpr>(CallExpr) && isa<TupleExpr>(ArgExpr)) {
5997+
auto argTuple = cast<TupleExpr>(ArgExpr);
5998+
auto lhsExpr = argTuple->getElement(0),
5999+
rhsExpr = argTuple->getElement(1);
6000+
auto lhsType = lhsExpr->getType()->getRValueType();
6001+
auto rhsType = rhsExpr->getType()->getRValueType();
6002+
6003+
TC.diagnose(FnExpr->getLoc(), diag::cannot_apply_binop_to_args,
6004+
overloadName, lhsType, rhsType)
6005+
.highlight(lhsExpr->getSourceRange())
6006+
.highlight(rhsExpr->getSourceRange());
6007+
} else if (isa<PrefixUnaryExpr>(CallExpr) ||
6008+
isa<PostfixUnaryExpr>(CallExpr)) {
6009+
TC.diagnose(ArgExpr->getLoc(), diag::cannot_apply_unop_to_arg,
6010+
overloadName, ArgExpr->getType());
6011+
} else {
6012+
bool isInitializer = isa<ConstructorDecl>(Candidate);
6013+
TC.diagnose(ArgExpr->getLoc(), diag::cannot_call_with_params,
6014+
overloadName, getTypeListString(ArgExpr->getType()),
6015+
isInitializer);
6016+
}
6017+
6018+
auto rawFirstType = req.getFirstType();
6019+
auto rawSecondType = req.getSecondType();
6020+
auto *genericSig = Candidate->getGenericSignature();
6021+
6022+
TC.diagnose(Candidate, note, first, second,
6023+
rawFirstType, rawSecondType,
6024+
genericSig->gatherGenericParamBindingsText(
6025+
{rawFirstType, rawSecondType}, Substitutions));
6026+
return true;
6027+
}
59576028
};
59586029

5959-
auto dc = env->getOwningDeclContext();
5960-
RequirementsListener genericReqListener;
6030+
auto *dc = env->getOwningDeclContext();
6031+
auto substitutionFn = QueryTypeSubstitutionMap{substitutions};
6032+
RequirementsListener genericReqListener(TC, AFD, substitutionFn,
6033+
callExpr, fnExpr, argExpr);
6034+
59616035
auto result = TC.checkGenericArguments(
5962-
dc, argExpr->getLoc(), AFD->getLoc(), AFD->getInterfaceType(),
6036+
dc, callExpr->getLoc(), fnExpr->getLoc(), AFD->getInterfaceType(),
59636037
env->getGenericSignature(), QueryTypeSubstitutionMap{substitutions},
59646038
LookUpConformanceInModule{dc->getParentModule()}, nullptr,
59656039
ConformanceCheckFlags::SuppressDependencyTracking, &genericReqListener);
@@ -6662,6 +6736,10 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
66626736
}
66636737
}
66646738

6739+
if (diagnoseArgumentGenericRequirements(CS->TC, callExpr, fnExpr, argExpr,
6740+
calleeInfo, argLabels))
6741+
return true;
6742+
66656743
if (isContextualConversionFailure(argTuple))
66666744
return false;
66676745

@@ -6695,13 +6773,13 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
66956773
return true;
66966774
}
66976775

6698-
// If all of the arguments are a perfect match, so let's check if there
6776+
// If all of the arguments are a perfect match, let's check if there
66996777
// are problems with requirements placed on generic parameters, because
67006778
// CalleeCandidateInfo validates only conformance of the parameters
67016779
// to their protocol types (if any) but it doesn't check additional
67026780
// requirements placed on e.g. nested types or between parameters.
6703-
if (diagnoseArgumentGenericRequirements(CS->TC, fnExpr, argExpr, calleeInfo,
6704-
argLabels))
6781+
if (diagnoseArgumentGenericRequirements(CS->TC, callExpr, fnExpr, argExpr,
6782+
calleeInfo, argLabels))
67056783
return true;
67066784

67076785
if (isContextualConversionFailure(argExpr))

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1639,6 +1639,11 @@ void GenericRequirementsCheckListener::satisfiedConformance(
16391639
ProtocolConformanceRef conformance) {
16401640
}
16411641

1642+
bool GenericRequirementsCheckListener::diagnoseUnsatisfiedRequirement(
1643+
const Requirement &req, Type first, Type second) {
1644+
return false;
1645+
}
1646+
16421647
bool TypeChecker::
16431648
solveForExpression(Expr *&expr, DeclContext *dc, Type convertType,
16441649
FreeTypeVariableBinding allowFreeTypeVariables,

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,9 +1300,13 @@ RequirementCheckResult TypeChecker::checkGenericArguments(
13001300
secondType = req->getSecondType();
13011301
}
13021302

1303+
bool requirementFailure = false;
13031304
if (listener && !listener->shouldCheck(kind, firstType, secondType))
13041305
continue;
13051306

1307+
Diag<Type, Type, Type> diagnostic;
1308+
Diag<Type, Type, StringRef> diagnosticNote;
1309+
13061310
switch (kind) {
13071311
case RequirementKind::Conformance: {
13081312
// Protocol conformance requirements.
@@ -1311,6 +1315,8 @@ RequirementCheckResult TypeChecker::checkGenericArguments(
13111315
// or non-private dependency.
13121316
// FIXME: Do we really need "used" at this point?
13131317
// FIXME: Poor location information. How much better can we do here?
1318+
// FIXME: This call should support listener to be able to properly
1319+
// diagnose problems with conformances.
13141320
auto result =
13151321
conformsToProtocol(firstType, proto->getDecl(), dc,
13161322
conformanceOptions, loc, unsatisfiedDependency);
@@ -1343,37 +1349,37 @@ RequirementCheckResult TypeChecker::checkGenericArguments(
13431349
case RequirementKind::Superclass:
13441350
// Superclass requirements.
13451351
if (!isSubclassOf(firstType, secondType, dc)) {
1346-
if (loc.isValid()) {
1347-
// FIXME: Poor source-location information.
1348-
diagnose(loc, diag::type_does_not_inherit, owner, firstType,
1349-
secondType);
1350-
1351-
diagnose(noteLoc, diag::type_does_not_inherit_requirement,
1352-
rawFirstType, rawSecondType,
1353-
genericSig->gatherGenericParamBindingsText(
1354-
{rawFirstType, rawSecondType}, substitutions));
1355-
}
1356-
1357-
return RequirementCheckResult::Failure;
1352+
diagnostic = diag::type_does_not_inherit;
1353+
diagnosticNote = diag::type_does_not_inherit_requirement;
1354+
requirementFailure = true;
13581355
}
1359-
continue;
1356+
break;
13601357

13611358
case RequirementKind::SameType:
13621359
if (!firstType->isEqual(secondType)) {
1363-
if (loc.isValid()) {
1364-
// FIXME: Better location info for both diagnostics.
1365-
diagnose(loc, diag::types_not_equal, owner, firstType, secondType);
1366-
1367-
diagnose(noteLoc, diag::types_not_equal_requirement, rawFirstType,
1368-
rawSecondType,
1369-
genericSig->gatherGenericParamBindingsText(
1370-
{rawFirstType, rawSecondType}, substitutions));
1371-
}
1372-
1373-
return RequirementCheckResult::Failure;
1360+
diagnostic = diag::types_not_equal;
1361+
diagnosticNote = diag::types_not_equal_requirement;
1362+
requirementFailure = true;
13741363
}
1364+
break;
1365+
}
1366+
1367+
if (!requirementFailure)
13751368
continue;
1369+
1370+
if (listener &&
1371+
listener->diagnoseUnsatisfiedRequirement(rawReq, firstType, secondType))
1372+
return RequirementCheckResult::Failure;
1373+
1374+
if (loc.isValid()) {
1375+
// FIXME: Poor source-location information.
1376+
diagnose(loc, diagnostic, owner, firstType, secondType);
1377+
diagnose(noteLoc, diagnosticNote, rawFirstType, rawSecondType,
1378+
genericSig->gatherGenericParamBindingsText(
1379+
{rawFirstType, rawSecondType}, substitutions));
13761380
}
1381+
1382+
return RequirementCheckResult::Failure;
13771383
}
13781384

13791385
if (valid)

lib/Sema/TypeChecker.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,20 @@ class GenericRequirementsCheckListener {
372372
/// \param conformance The conformance itself.
373373
virtual void satisfiedConformance(Type depTy, Type replacementTy,
374374
ProtocolConformanceRef conformance);
375+
376+
/// Callback to diagnose problem with unsatisfied generic requirement.
377+
///
378+
/// \param req The unsatisfied generic requirement.
379+
///
380+
/// \param first The left-hand side type assigned to the requirement,
381+
/// possibly represented by its generic substitute.
382+
///
383+
/// \param second The right-hand side type assigned to the requirement,
384+
/// possibly represented by its generic substitute.
385+
///
386+
/// \returns true if problem has been diagnosed, false otherwise.
387+
virtual bool diagnoseUnsatisfiedRequirement(const Requirement &req,
388+
Type first, Type second);
375389
};
376390

377391
/// The result of `checkGenericRequirement`.

test/Constraints/generics.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,10 +248,12 @@ struct S27515965 : P27515965 {
248248

249249
struct V27515965 {
250250
init<T : P27515965>(_ tp: T) where T.R == Float {}
251+
// expected-note@-1 {{candidate requires that the types 'Any' and 'Float' be equivalent (requirement specified as 'T.R' == 'Float' [with T = S27515965])}}
251252
}
252253

253254
func test(x: S27515965) -> V27515965 {
254-
return V27515965(x) // expected-error {{generic parameter 'T' could not be inferred}}
255+
return V27515965(x)
256+
// expected-error@-1 {{cannot invoke initializer for type 'init(_:)' with an argument list of type '(S27515965)'}}
255257
}
256258

257259
protocol BaseProto {}

test/Generics/deduction.swift

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,12 @@ func callRangeOfIsBefore(_ ia: [Int], da: [Double]) {
216216
}
217217

218218
func testEqualIterElementTypes<A: IteratorProtocol, B: IteratorProtocol>(_ a: A, _ b: B) where A.Element == B.Element {}
219-
// expected-note@-1 {{requirement specified as 'A.Element' == 'B.Element' [with A = IndexingIterator<[Int]>, B = IndexingIterator<[Double]>]}}
219+
// expected-note@-1 {{candidate requires that the types 'Int' and 'Double' be equivalent (requirement specified as 'A.Element' == 'B.Element' [with A = IndexingIterator<[Int]>, B = IndexingIterator<[Double]>])}}
220220
func compareIterators() {
221221
var a: [Int] = []
222222
var b: [Double] = []
223-
testEqualIterElementTypes(a.makeIterator(), b.makeIterator()) // expected-error {{'<A, B where A : IteratorProtocol, B : IteratorProtocol, A.Element == B.Element> (A, B) -> ()' requires the types 'Int' and 'Double' be equivalent}}
223+
testEqualIterElementTypes(a.makeIterator(), b.makeIterator())
224+
// expected-error@-1 {{cannot invoke 'testEqualIterElementTypes(_:_:)' with an argument list of type '(IndexingIterator<[Int]>, IndexingIterator<[Double]>)'}}
224225
}
225226

226227
protocol P_GI {
@@ -233,9 +234,9 @@ class C_GI : P_GI {
233234

234235
class GI_Diff {}
235236
func genericInheritsA<T>(_ x: T) where T : P_GI, T.Y : GI_Diff {}
236-
// expected-note@-1 {{requirement specified as 'T.Y' : 'GI_Diff' [with T = C_GI]}}
237-
genericInheritsA(C_GI()) // expected-error {{<T where T : P_GI, T.Y : GI_Diff> (T) -> ()' requires that 'C_GI.Y' (aka 'Double') inherit from 'GI_Diff'}}
238-
237+
// expected-note@-1 {{candidate requires that 'GI_Diff' inherit from 'T.Y' (requirement specified as 'T.Y' : 'GI_Diff' [with T = C_GI])}}
238+
genericInheritsA(C_GI())
239+
// expected-error@-1 {{cannot invoke 'genericInheritsA(_:)' with an argument list of type '(C_GI)'}}
239240

240241
//===----------------------------------------------------------------------===//
241242
// Deduction for member operators
@@ -318,3 +319,24 @@ func foo() {
318319
let l = min(3, oi) // expected-error{{value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?}}
319320
}
320321

322+
infix operator +&
323+
func +&<R, S>(lhs: inout R, rhs: S) where R : RangeReplaceableCollection, S : Sequence, R.Element == S.Element {}
324+
// expected-note@-1 {{candidate requires that the types 'String' and 'String.Element' (aka 'Character') be equivalent (requirement specified as 'R.Element' == 'S.Element' [with R = [String], S = String])}}
325+
326+
func rdar33477726_1() {
327+
var arr: [String] = []
328+
arr +& "hello"
329+
// expected-error@-1 {{binary operator '+&(_:_:)' cannot be applied to operands of type '[String]' and 'String'}}
330+
}
331+
332+
func rdar33477726_2<R, S>(_: R, _: S) where R: Sequence, S == R.Element {}
333+
// expected-note@-1 {{candidate requires that the types 'Int' and 'String.Element' (aka 'Character') be equivalent (requirement specified as 'S' == 'R.Element' [with R = String, S = Int])}}
334+
rdar33477726_2("answer", 42)
335+
// expected-error@-1 {{cannot invoke 'rdar33477726_2(_:_:)' with an argument list of type '(String, Int)'}}
336+
337+
prefix operator +-
338+
prefix func +-<T>(_: T) where T: Sequence, T.Element == Int {}
339+
// expected-note@-1 {{candidate requires that the types 'String.Element' (aka 'Character') and 'Int' be equivalent (requirement specified as 'T.Element' == 'Int' [with T = String])}}
340+
341+
+-"hello"
342+
// expected-error@-1 {{unary operator '+-(_:)' cannot be applied to an operand of type 'String'}}

test/decl/protocol/req/recursion.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public struct S<A: P> where A.T == S<A> {
4848
// expected-error@-2 {{generic struct 'S' references itself}}
4949
func f(a: A.T) {
5050
g(a: id(t: a))
51-
// expected-error@-1 {{cannot convert value of type 'A.T' to expected argument type 'S<_>'}}
51+
// expected-error@-1 {{generic parameter 'T' could not be inferred}}
5252
_ = A.T.self
5353
}
5454

0 commit comments

Comments
 (0)