Generalize the conditions in which we'll accept an ambiguous solution to

a constraint system in "allowFreeTypeVariables" mode.  Previously, we
only allowed a few specific constraints, now we allow any relational and
member constraints.  The later one is a big deal because it means that we
can allow ".Foo" expressions as ambiguous solutions, which CSDiags can
handle well.

This unblocks solving 23942743 and enables some minor improvements across
the board, including diagnosing things like this better:
  Optional(.none)  // now: generic parameter 'T' could not be inferred

That said, it also just permutes some non-awesome diagnostics.
This commit is contained in:
Chris Lattner
2016-01-11 17:02:25 -08:00
parent 42591f7cba
commit b5500b8600
8 changed files with 78 additions and 45 deletions

View File

@@ -2284,9 +2284,16 @@ namespace {
} }
Expr *visitUnresolvedMemberExpr(UnresolvedMemberExpr *expr) { Expr *visitUnresolvedMemberExpr(UnresolvedMemberExpr *expr) {
// Dig out the type of the base, which will be the result // Dig out the type of the base, which will be the result type of this
// type of this expression. // expression. If constraint solving resolved this to an UnresolvedType,
// then we're in an ambiguity tolerant mode used for diagnostic
// generation. Just leave this as an unresolved member reference.
Type resultTy = simplifyType(expr->getType()); Type resultTy = simplifyType(expr->getType());
if (resultTy->is<UnresolvedType>()) {
expr->setType(resultTy);
return expr;
}
Type baseTy = resultTy->getRValueType(); Type baseTy = resultTy->getRValueType();
auto &tc = cs.getTypeChecker(); auto &tc = cs.getTypeChecker();
@@ -2346,16 +2353,25 @@ namespace {
llvm::SmallPtrSet<InjectIntoOptionalExpr *, 4> DiagnosedOptionalInjections; llvm::SmallPtrSet<InjectIntoOptionalExpr *, 4> DiagnosedOptionalInjections;
private: private:
Expr *applyMemberRefExpr(Expr *expr, Expr *applyMemberRefExpr(Expr *expr, Expr *base, SourceLoc dotLoc,
Expr *base, SourceLoc nameLoc, bool implicit) {
SourceLoc dotLoc,
SourceLoc nameLoc,
bool implicit) {
// Determine the declaration selected for this overloaded reference. // Determine the declaration selected for this overloaded reference.
auto memberLocator = cs.getConstraintLocator(expr, auto memberLocator = cs.getConstraintLocator(expr,
ConstraintLocator::Member); ConstraintLocator::Member);
auto selected = getOverloadChoice(memberLocator); auto selectedElt = getOverloadChoiceIfAvailable(memberLocator);
if (!selectedElt) {
// If constraint solving resolved this to an UnresolvedType, then we're
// in an ambiguity tolerant mode used for diagnostic generation. Just
// leave this as whatever type of member reference it already is.
Type resultTy = simplifyType(expr->getType());
assert(resultTy->hasUnresolvedType() &&
"Should have a selected member if we got a type");
expr->setType(resultTy);
return expr;
}
auto selected = *selectedElt;
switch (selected.choice.getKind()) { switch (selected.choice.getKind()) {
case OverloadChoiceKind::DeclViaBridge: { case OverloadChoiceKind::DeclViaBridge: {
// Look through an implicitly unwrapped optional. // Look through an implicitly unwrapped optional.
@@ -2406,14 +2422,12 @@ namespace {
case OverloadChoiceKind::TupleIndex: { case OverloadChoiceKind::TupleIndex: {
auto baseTy = base->getType()->getRValueType(); auto baseTy = base->getType()->getRValueType();
if (auto objTy = cs.lookThroughImplicitlyUnwrappedOptionalType(baseTy)) { if (auto objTy = cs.lookThroughImplicitlyUnwrappedOptionalType(baseTy)){
base = coerceImplicitlyUnwrappedOptionalToValue(base, objTy, base = coerceImplicitlyUnwrappedOptionalToValue(base, objTy,
cs.getConstraintLocator(base)); cs.getConstraintLocator(base));
} }
return new (cs.getASTContext()) TupleElementExpr( return new (cs.getASTContext()) TupleElementExpr(base, dotLoc,
base,
dotLoc,
selected.choice.getTupleIndex(), selected.choice.getTupleIndex(),
nameLoc, nameLoc,
simplifyType(expr->getType())); simplifyType(expr->getType()));

View File

@@ -2778,9 +2778,9 @@ static Type replaceArchetypesAndTypeVarsWithUnresolved(Type ty) {
auto &ctx = ty->getASTContext(); auto &ctx = ty->getASTContext();
return ty.transform([&](Type type) -> Type { return ty.transform([&](Type type) -> Type {
if (type->is<TypeVariableType>()) if (type->is<TypeVariableType>() ||
return ctx.TheUnresolvedType; type->is<ArchetypeType>() ||
if (type->is<ArchetypeType>()) type->isTypeParameter())
return ctx.TheUnresolvedType; return ctx.TheUnresolvedType;
return type; return type;
}); });
@@ -2818,7 +2818,8 @@ typeCheckChildIndependently(Expr *subExpr, Type convertType,
if (FT->isAutoClosure()) if (FT->isAutoClosure())
convertType = FT->getResult(); convertType = FT->getResult();
if (convertType->hasTypeVariable() || convertType->hasArchetype()) if (convertType->hasTypeVariable() || convertType->hasArchetype() ||
convertType->isTypeParameter())
convertType = replaceArchetypesAndTypeVarsWithUnresolved(convertType); convertType = replaceArchetypesAndTypeVarsWithUnresolved(convertType);
// If the conversion type contains no info, drop it. // If the conversion type contains no info, drop it.
@@ -3958,6 +3959,15 @@ bool FailureDiagnosis::visitAssignExpr(AssignExpr *assignExpr) {
destType->getRValueType(), destType->getRValueType(),
CTP_AssignSource); CTP_AssignSource);
if (!srcExpr) return true; if (!srcExpr) return true;
// If we are assigning to _ and have unresolvedtypes on the RHS, then we have
// an ambiguity problem.
if (isa<DiscardAssignmentExpr>(destExpr->getSemanticsProvidingExpr()) &&
srcExpr->getType()->hasUnresolvedType()) {
diagnoseAmbiguity(srcExpr);
return true;
}
return false; return false;
} }
@@ -4947,19 +4957,22 @@ void FailureDiagnosis::diagnoseAmbiguity(Expr *E) {
} }
// Attempt to re-type-check the entire expression, while allowing ambiguity. // Attempt to re-type-check the entire expression, allowing ambiguity, but
auto exprType = getTypeOfTypeCheckedChildIndependently(expr); // ignoring a contextual type.
// If it failed and diagnosed something, then we're done. if (expr == E) {
if (!exprType) return; auto exprType = getTypeOfTypeCheckedChildIndependently(expr);
// If it failed and diagnosed something, then we're done.
if (!exprType) return;
// If we were able to find something more specific than "unknown" (perhaps // If we were able to find something more specific than "unknown" (perhaps
// something like "[_:_]" for a dictionary literal), include it in the // something like "[_:_]" for a dictionary literal), include it in the
// diagnostic. // diagnostic.
if (!isUnresolvedOrTypeVarType(exprType)) { if (!isUnresolvedOrTypeVarType(exprType)) {
diagnose(E->getLoc(), diag::specific_type_of_expression_is_ambiguous, diagnose(E->getLoc(), diag::specific_type_of_expression_is_ambiguous,
exprType) exprType)
.highlight(E->getSourceRange()); .highlight(E->getSourceRange());
return; return;
}
} }
// If there are no posted constraints or failures, then there was // If there are no posted constraints or failures, then there was

View File

@@ -1562,22 +1562,26 @@ bool ConstraintSystem::solveSimplified(
// constraints that could show up here? // constraints that could show up here?
if (allowFreeTypeVariables != FreeTypeVariableBinding::Disallow && if (allowFreeTypeVariables != FreeTypeVariableBinding::Disallow &&
hasFreeTypeVariables()) { hasFreeTypeVariables()) {
bool anyNonConformanceConstraints = false;
for (auto &constraint : InactiveConstraints) {
if (constraint.getKind() == ConstraintKind::ConformsTo ||
constraint.getKind() == ConstraintKind::SelfObjectOfProtocol ||
constraint.getKind() == ConstraintKind::TypeMember)
continue;
anyNonConformanceConstraints = true;
break;
}
// If this solution is worse than the best solution we've seen so far, // If this solution is worse than the best solution we've seen so far,
// skip it. // skip it.
if (worseThanBestSolution()) if (worseThanBestSolution())
return true; return true;
bool anyNonConformanceConstraints = false;
for (auto &constraint : InactiveConstraints) {
switch (constraint.getClassification()) {
case ConstraintClassification::Relational:
case ConstraintClassification::Member:
continue;
default:
break;
}
anyNonConformanceConstraints = true;
break;
}
if (!anyNonConformanceConstraints) { if (!anyNonConformanceConstraints) {
auto solution = finalize(allowFreeTypeVariables); auto solution = finalize(allowFreeTypeVariables);
if (TC.getLangOpts().DebugConstraintSolver) { if (TC.getLangOpts().DebugConstraintSolver) {

View File

@@ -57,7 +57,7 @@ acceptString("\(hello), \(world) #\(i)!")
Optional<Int>(1) // expected-warning{{unused}} Optional<Int>(1) // expected-warning{{unused}}
Optional(1) // expected-warning{{unused}} Optional(1) // expected-warning{{unused}}
_ = .none as Optional<Int> _ = .none as Optional<Int>
Optional(.none) // expected-error{{type of expression is ambiguous without more context}} Optional(.none) // expected-error{{generic parameter 'T' could not be inferred}}
// Interpolation // Interpolation
"\(hello), \(world) #\(i)!" "\(hello), \(world) #\(i)!"

View File

@@ -242,7 +242,7 @@ func r18800223(i : Int) {
var buttonTextColor: String? var buttonTextColor: String?
_ = (buttonTextColor != nil) ? 42 : {$0}; // expected-error {{unable to infer closure return type in current context}} _ = (buttonTextColor != nil) ? 42 : {$0}; // expected-error {{result values in '? :' expression have mismatching types 'Int' and '(_) -> _'}}
} }
// <rdar://problem/21883806> Bogus "'_' can only appear in a pattern or on the left side of an assignment" is back // <rdar://problem/21883806> Bogus "'_' can only appear in a pattern or on the left side of an assignment" is back
@@ -609,7 +609,8 @@ extension Array {
} }
func h() -> String { func h() -> String {
return "foo".unavail([0]) // expected-error {{value of type 'String' has no member 'Element'}} return "foo".unavail([0]) // expected-error {{cannot invoke 'unavail' with an argument list of type '([Int])'}}
// expected-note @-1 {{expected an argument list of type '(T)'}}
} }
} }

View File

@@ -7,7 +7,7 @@ protocol P {
func f<U: P>(rhs: U) -> X<U.A> { // expected-error {{use of undeclared type 'X'}} func f<U: P>(rhs: U) -> X<U.A> { // expected-error {{use of undeclared type 'X'}}
// FIXME: This diagnostic isn't great, it happens because the generic constraint // FIXME: This diagnostic isn't great, it happens because the generic constraint
// 'U' from the invalid type signature never gets resolved. // 'U' from the invalid type signature never gets resolved.
let g = rhs.generate() // expected-error {{type of expression is ambiguous without more context}} let g = rhs.generate() // expected-error {{cannot invoke 'generate' with no arguments}}
} }
struct Zzz<T> { struct Zzz<T> {

View File

@@ -85,7 +85,8 @@ enum Complex {
case B case B
} }
if Complex.A(1) == .B { } // expected-error{{type of expression is ambiguous without more context}} if Complex.A(1) == .B { } // expected-error{{binary operator '==' cannot be applied to operands of type 'Complex' and '_'}}
// expected-note @-1 {{overloads for '==' exist with these partially matching parameter lists: }}
// rdar://19773050 // rdar://19773050

View File

@@ -21,4 +21,4 @@ var e2a: E2<Int> = .First
e2a = .Second(5) e2a = .Second(5)
var e2b: E2 = .Second(5) var e2b: E2 = .Second(5)
e2b = .First e2b = .First
var e2c: E2 = .First // expected-error{{type of expression is ambiguous without more context}} var e2c: E2 = .First // expected-error{{generic parameter 'T' could not be inferred}}