Diagnostics: Ported tuple mismatch diagnostic to new diagnostic framework

This commit is contained in:
Sam Lazarus
2019-07-23 15:28:53 -04:00
parent b28b44b9c7
commit a3b56c2790
9 changed files with 115 additions and 14 deletions

View File

@@ -1147,9 +1147,9 @@ bool FailureDiagnosis::diagnoseGeneralConversionFailure(Constraint *constraint){
if (auto fromTT = fromType->getAs<TupleType>()) if (auto fromTT = fromType->getAs<TupleType>())
if (auto toTT = toType->getAs<TupleType>()) { if (auto toTT = toType->getAs<TupleType>()) {
if (fromTT->getNumElements() != toTT->getNumElements()) { if (fromTT->getNumElements() != toTT->getNumElements()) {
diagnose(anchor->getLoc(), diag::tuple_types_not_convertible_nelts, auto failure = TupleContextualFailure(anchor, CS, fromTT, toTT,
fromTT, toTT) CS.getConstraintLocator(expr));
.highlight(anchor->getSourceRange()); failure.diagnoseAsError();
return true; return true;
} }
@@ -1166,9 +1166,9 @@ bool FailureDiagnosis::diagnoseGeneralConversionFailure(Constraint *constraint){
// then we have a type error. // then we have a type error.
if (computeTupleShuffle(TEType->castTo<TupleType>()->getElements(), if (computeTupleShuffle(TEType->castTo<TupleType>()->getElements(),
toTT->getElements(), sources)) { toTT->getElements(), sources)) {
diagnose(anchor->getLoc(), diag::tuple_types_not_convertible, auto failure = TupleContextualFailure(anchor, CS, fromTT, toTT,
fromTT, toTT) CS.getConstraintLocator(expr));
.highlight(anchor->getSourceRange()); failure.diagnoseAsError();
return true; return true;
} }
} }

View File

@@ -1887,6 +1887,14 @@ void ContextualFailure::tryComputedPropertyFixIts(Expr *expr) const {
} }
} }
bool TupleContextualFailure::diagnoseAsError() {
auto diagnostic = isNumElementsMismatch()
? diag::tuple_types_not_convertible_nelts
: diag::tuple_types_not_convertible;
emitDiagnostic(getAnchor()->getLoc(), diagnostic, getFromType(), getToType());
return true;
}
bool AutoClosureForwardingFailure::diagnoseAsError() { bool AutoClosureForwardingFailure::diagnoseAsError() {
auto path = getLocator()->getPath(); auto path = getLocator()->getPath();
assert(!path.empty()); assert(!path.empty());

View File

@@ -759,6 +759,23 @@ private:
void tryComputedPropertyFixIts(Expr *expr) const; void tryComputedPropertyFixIts(Expr *expr) const;
}; };
/// Diagnose mismatches relating to tuple destructuring.
class TupleContextualFailure final : public ContextualFailure {
public:
TupleContextualFailure(Expr *root, ConstraintSystem &cs, Type lhs, Type rhs,
ConstraintLocator *locator)
: ContextualFailure(root, cs, lhs, rhs, locator) {}
bool diagnoseAsError() override;
bool isNumElementsMismatch() const {
auto lhsTy = dyn_cast<TupleType>(getFromType().getPointer());
auto rhsTy = dyn_cast<TupleType>(getToType().getPointer());
assert(lhsTy && rhsTy);
return lhsTy->getElements().size() != rhsTy->getElements().size();
}
};
/// Diagnose situations when @autoclosure argument is passed to @autoclosure /// Diagnose situations when @autoclosure argument is passed to @autoclosure
/// parameter directly without calling it first. /// parameter directly without calling it first.
class AutoClosureForwardingFailure final : public FailureDiagnostic { class AutoClosureForwardingFailure final : public FailureDiagnostic {

View File

@@ -229,6 +229,18 @@ ContextualMismatch *ContextualMismatch::create(ConstraintSystem &cs, Type lhs,
return new (cs.getAllocator()) ContextualMismatch(cs, lhs, rhs, locator); return new (cs.getAllocator()) ContextualMismatch(cs, lhs, rhs, locator);
} }
bool AllowTupleTypeMismatch::diagnose(Expr *root, bool asNote) const {
auto failure = TupleContextualFailure(root, getConstraintSystem(), LHS, RHS,
getLocator());
return failure.diagnose(asNote);
}
AllowTupleTypeMismatch *
AllowTupleTypeMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs,
ConstraintLocator *locator) {
return new (cs.getAllocator()) AllowTupleTypeMismatch(cs, lhs, rhs, locator);
}
bool GenericArgumentsMismatch::diagnose(Expr *root, bool asNote) const { bool GenericArgumentsMismatch::diagnose(Expr *root, bool asNote) const {
auto failure = GenericArgumentsMismatchFailure(root, getConstraintSystem(), auto failure = GenericArgumentsMismatchFailure(root, getConstraintSystem(),
getActual(), getRequired(), getActual(), getRequired(),

View File

@@ -136,6 +136,10 @@ enum class FixKind : uint8_t {
/// referenced constructor must be required. /// referenced constructor must be required.
AllowInvalidInitRef, AllowInvalidInitRef,
/// Allow a tuple to be destructured with a mismatched number of
/// elements, or mismatched types.
AllowTupleTypeMismatch,
/// Allow an invalid member access on a value of protocol type as if /// Allow an invalid member access on a value of protocol type as if
/// that protocol type were a generic constraint requiring conformance /// that protocol type were a generic constraint requiring conformance
/// to that protocol. /// to that protocol.
@@ -862,6 +866,25 @@ private:
ConstraintLocator *locator); ConstraintLocator *locator);
}; };
class AllowTupleTypeMismatch final : public ConstraintFix {
Type LHS, RHS;
AllowTupleTypeMismatch(ConstraintSystem &cs, Type lhs, Type rhs,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AllowTupleTypeMismatch, locator), LHS(lhs),
RHS(rhs) {}
public:
static AllowTupleTypeMismatch *create(ConstraintSystem &cs, Type lhs,
Type rhs, ConstraintLocator *locator);
std::string getName() const override {
return "allow invalid tuple destructuring";
}
bool diagnose(Expr *root, bool asNote = false) const override;
};
class AllowMutatingMemberOnRValueBase final : public AllowInvalidMemberRef { class AllowMutatingMemberOnRValueBase final : public AllowInvalidMemberRef {
AllowMutatingMemberOnRValueBase(ConstraintSystem &cs, Type baseType, AllowMutatingMemberOnRValueBase(ConstraintSystem &cs, Type baseType,
ValueDecl *member, DeclName name, ValueDecl *member, DeclName name,

View File

@@ -2382,6 +2382,13 @@ bool ConstraintSystem::repairFailures(
getConstraintLocator(locator)); getConstraintLocator(locator));
conversionsOrFixes.push_back(fix); conversionsOrFixes.push_back(fix);
} }
if (purpose == CTP_Initialization && lhs->is<TupleType>() &&
rhs->is<TupleType>()) {
auto *fix = AllowTupleTypeMismatch::create(*this, lhs, rhs,
getConstraintLocator(locator));
conversionsOrFixes.push_back(fix);
}
break; break;
} }
@@ -2413,6 +2420,11 @@ bool ConstraintSystem::repairFailures(
conversionsOrFixes.push_back(CollectionElementContextualMismatch::create( conversionsOrFixes.push_back(CollectionElementContextualMismatch::create(
*this, lhs, rhs, getConstraintLocator(locator))); *this, lhs, rhs, getConstraintLocator(locator)));
} }
if (lhs->is<TupleType>() && rhs->is<TupleType>()) {
auto *fix = AllowTupleTypeMismatch::create(*this, lhs, rhs,
getConstraintLocator(locator));
conversionsOrFixes.push_back(fix);
}
break; break;
} }
@@ -6951,6 +6963,32 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
return matchTypes(type1, type2, matchKind, subflags, locator); return matchTypes(type1, type2, matchKind, subflags, locator);
} }
case FixKind::AllowTupleTypeMismatch: {
auto lhs = dyn_cast<TupleType>(type1.getPointer());
auto rhs = dyn_cast<TupleType>(type2.getPointer());
auto lhsLarger = lhs->getElements().size() >= rhs->getElements().size();
auto larger = lhsLarger ? lhs : rhs;
auto smaller = lhsLarger ? rhs : lhs;
if (lhs && rhs && getContextualTypePurpose() == CTP_Initialization) {
// Match up the tuple type elements, and match any
for (unsigned i = 0; i < larger->getElements().size(); ++i) {
auto largerTy = larger->getElement(i).getType();
if (i < smaller->getElements().size()) {
auto smallerTy = smaller->getElement(i).getType();
if (smallerTy->isTypeVariableOrMember() ||
largerTy->isTypeVariableOrMember())
addConstraint(ConstraintKind::Bind, largerTy, smallerTy,
getConstraintLocator(locator));
} else {
addConstraint(ConstraintKind::Defaultable, largerTy,
getASTContext().TheAnyType,
getConstraintLocator(locator));
}
}
}
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
}
case FixKind::InsertCall: case FixKind::InsertCall:
case FixKind::RemoveReturn: case FixKind::RemoveReturn:
case FixKind::AddConformance: case FixKind::AddConformance:

View File

@@ -19,5 +19,5 @@ struct S: P {
typealias R = T3 typealias R = T3
static let foo: (T1, (R) -> T2) = bind() static let foo: (T1, (R) -> T2) = bind()
// expected-error@-1 {{cannot convert value of type '(T1, (S.R) -> T3)' (aka '(Int, (Bool) -> Bool)') to specified type '(T1, (S.R) -> T2)' (aka '(Int, (Bool) -> Float)')}} // expected-error@-1 {{tuple type '(T1, (S.R) -> T3)' (aka '(Int, (Bool) -> Bool)') is not convertible to tuple '(T1, (S.R) -> T2)' (aka '(Int, (Bool) -> Float)')}}
} }

View File

@@ -55,8 +55,8 @@ func test_varname_binding() {
var (d, e) = (c.1, c.0) var (d, e) = (c.1, c.0)
var ((), (g1, g2), h) = ((), (e, d), e) var ((), (g1, g2), h) = ((), (e, d), e)
var (j, k, l) = callee1() var (j, k, l) = callee1()
var (m, n) = callee1() // expected-error{{'(Int, Int, Int)' is not convertible to '(_, _)', tuples have a different number of elements}} var (m, n) = callee1() // expected-error{{'(Int, Int, Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}}
var (o, p, q, r) = callee1() // expected-error{{'(Int, Int, Int)' is not convertible to '(_, _, _, _)', tuples have a different number of elements}} var (o, p, q, r) = callee1() // expected-error{{'(Int, Int, Int)' is not convertible to '(Int, Int, Int, Any)', tuples have a different number of elements}}
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

@@ -139,6 +139,7 @@ func funcdecl7(_ a: Int, b: (c: Int, d: Int), third: (c: Int, d: Int)) -> Int {
// Error recovery. // Error recovery.
func testfunc2 (_: ((), Int) -> Int) -> Int {} func testfunc2 (_: ((), Int) -> Int) -> Int {}
func makeTuple() -> (String, Int) { return ("foo", 42) }
func errorRecovery() { func errorRecovery() {
testfunc2({ $0 + 1 }) // expected-error {{contextual closure type '((), Int) -> Int' expects 2 arguments, but 1 was used in closure body}} testfunc2({ $0 + 1 }) // expected-error {{contextual closure type '((), Int) -> Int' expects 2 arguments, but 1 was used in closure body}}
@@ -149,12 +150,14 @@ func errorRecovery() {
var a: Int = .hello // expected-error {{type 'Int' has no member 'hello'}} var a: Int = .hello // expected-error {{type 'Int' has no member 'hello'}}
var b: union1 = .bar // ok var b: union1 = .bar // ok
var c: union1 = .xyz // expected-error {{type 'union1' has no member 'xyz'}} var c: union1 = .xyz // expected-error {{type 'union1' has no member 'xyz'}}
var d: (Int,Int,Int) = (1,2) // expected-error {{cannot convert value of type '(Int, Int)' to specified type '(Int, Int, Int)'}} var d: (Int,Int,Int) = (1,2) // expected-error {{'(Int, Int)' is not convertible to '(Int, Int, Int)', tuples have a different number of elements}}
var e: (Int,Int) = (1, 2, 3) // expected-error {{cannot convert value of type '(Int, Int, Int)' to specified type '(Int, Int)'}} var e: (Int,Int) = (1, 2, 3) // expected-error {{'(Int, Int, Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}}
var f: (Int,Int) = (1, 2, f : 3) // expected-error {{cannot convert value of type '(Int, Int, f: Int)' to specified type '(Int, Int)'}} var f: (Int,Int) = (1, 2, f : 3) // expected-error {{'(Int, Int, f: Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}}
// <rdar://problem/22426860> CrashTracer: [USER] swift at mous_namespace::ConstraintGenerator::getTypeForPattern + 698 // <rdar://problem/22426860> CrashTracer: [USER] swift at mous_namespace::ConstraintGenerator::getTypeForPattern + 698
var (g1, g2, g3) = (1, 2) // expected-error {{'(Int, Int)' is not convertible to '(_, _, _)', tuples have a different number of elements}} var (g1, g2, g3) = (1, 2) // expected-error {{'(Int, Int)' is not convertible to '(Int, Int, Any)', tuples have a different number of elements}}
var (h1, h2) = (1, 2, 3) // expected-error {{'(Int, Int, Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}}
var i: (Bool, Bool) = makeTuple() // expected-error {{tuple type '(String, Int)' is not convertible to tuple '(Bool, Bool)'}}
} }
func acceptsInt(_ x: Int) {} func acceptsInt(_ x: Int) {}
@@ -185,7 +188,7 @@ func test4() -> ((_ arg1: Int, _ arg2: Int) -> Int) {
func test5() { func test5() {
let a: (Int, Int) = (1,2) let a: (Int, Int) = (1,2)
var var
_: ((Int) -> Int, Int) = a // expected-error {{cannot convert value of type '(Int, Int)' to specified type '((Int) -> Int, Int)'}} _: ((Int) -> Int, Int) = a // expected-error {{tuple type '(Int, Int)' is not convertible to tuple '((Int) -> Int, Int)'}}
let c: (a: Int, b: Int) = (1,2) let c: (a: Int, b: Int) = (1,2)