Skip to content

Commit 66fe1ec

Browse files
authored
Merge pull request #29053 from xedin/port-ternary-diags
[Diagnostics] Detect and diagnose ternary branch type mismatches
2 parents c9c51be + f85dfbb commit 66fe1ec

11 files changed

+113
-53
lines changed

docs/TypeChecker.rst

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -985,9 +985,6 @@ The things in the queue yet to be ported are:
985985
Most of the associated diagnostics have been ported and fixes are
986986
located in ``ConstraintSystem::simplifyMemberConstraint``.
987987

988-
- Diagnostics related to ``if`` statement - "conditional" type mismatch
989-
and, in case of ternary operator, type mismatches between branches.
990-
991988
- Problems related to calls and operator applications e.g.
992989

993990
- Missing explicit ``Self.`` and ``self.``

lib/Sema/CSDiag.cpp

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,6 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
248248
bool visitSubscriptExpr(SubscriptExpr *SE);
249249
bool visitApplyExpr(ApplyExpr *AE);
250250
bool visitCoerceExpr(CoerceExpr *CE);
251-
bool visitIfExpr(IfExpr *IE);
252251
bool visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E);
253252
};
254253
} // end anonymous namespace
@@ -1890,37 +1889,6 @@ bool FailureDiagnosis::visitCoerceExpr(CoerceExpr *CE) {
18901889
return false;
18911890
}
18921891

1893-
bool FailureDiagnosis::visitIfExpr(IfExpr *IE) {
1894-
auto typeCheckClauseExpr = [&](Expr *clause, Type contextType = Type(),
1895-
ContextualTypePurpose convertPurpose =
1896-
CTP_Unused) -> Expr * {
1897-
// Provide proper contextual type when type conversion is specified.
1898-
return typeCheckChildIndependently(clause, contextType, convertPurpose,
1899-
TCCOptions(), nullptr, false);
1900-
};
1901-
// Check all of the subexpressions independently.
1902-
auto condExpr = typeCheckClauseExpr(IE->getCondExpr());
1903-
if (!condExpr) return true;
1904-
auto trueExpr = typeCheckClauseExpr(IE->getThenExpr(), CS.getContextualType(),
1905-
CS.getContextualTypePurpose());
1906-
if (!trueExpr) return true;
1907-
auto falseExpr = typeCheckClauseExpr(
1908-
IE->getElseExpr(), CS.getContextualType(), CS.getContextualTypePurpose());
1909-
if (!falseExpr) return true;
1910-
1911-
// If the true/false values already match, it must be a contextual problem.
1912-
if (CS.getType(trueExpr)->isEqual(CS.getType(falseExpr)))
1913-
return false;
1914-
1915-
// Otherwise, the true/false result types must not be matching.
1916-
diagnose(IE->getColonLoc(), diag::if_expr_cases_mismatch,
1917-
CS.getType(trueExpr), CS.getType(falseExpr))
1918-
.highlight(trueExpr->getSourceRange())
1919-
.highlight(falseExpr->getSourceRange());
1920-
return true;
1921-
}
1922-
1923-
19241892
bool FailureDiagnosis::
19251893
visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E) {
19261894
// Don't walk the children for this node, it leads to multiple diagnostics

lib/Sema/CSDiagnostics.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1906,6 +1906,14 @@ bool ContextualFailure::diagnoseAsError() {
19061906
break;
19071907
}
19081908

1909+
case ConstraintLocator::TernaryBranch: {
1910+
auto *ifExpr = cast<IfExpr>(getRawAnchor());
1911+
fromType = getType(ifExpr->getThenExpr());
1912+
toType = getType(ifExpr->getElseExpr());
1913+
diagnostic = diag::if_expr_cases_mismatch;
1914+
break;
1915+
}
1916+
19091917
case ConstraintLocator::ContextualType: {
19101918
if (diagnoseConversionToBool())
19111919
return true;

lib/Sema/CSGen.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2616,18 +2616,18 @@ namespace {
26162616
CS.addConstraint(
26172617
ConstraintKind::Conversion, CS.getType(expr->getCondExpr()),
26182618
boolDecl->getDeclaredType(),
2619-
CS.getConstraintLocator(expr, LocatorPathElt::Condition()));
2619+
CS.getConstraintLocator(expr, ConstraintLocator::Condition));
26202620

26212621
// The branches must be convertible to a common type.
2622-
return CS.addJoinConstraint(CS.getConstraintLocator(expr),
2623-
{
2624-
{ CS.getType(expr->getThenExpr()),
2625-
CS.getConstraintLocator(expr->getThenExpr()) },
2626-
{ CS.getType(expr->getElseExpr()),
2627-
CS.getConstraintLocator(expr->getElseExpr()) }
2628-
});
2622+
return CS.addJoinConstraint(
2623+
CS.getConstraintLocator(expr),
2624+
{{CS.getType(expr->getThenExpr()),
2625+
CS.getConstraintLocator(expr, LocatorPathElt::TernaryBranch(true))},
2626+
{CS.getType(expr->getElseExpr()),
2627+
CS.getConstraintLocator(expr,
2628+
LocatorPathElt::TernaryBranch(false))}});
26292629
}
2630-
2630+
26312631
virtual Type visitImplicitConversionExpr(ImplicitConversionExpr *expr) {
26322632
llvm_unreachable("Already type-checked");
26332633
}

lib/Sema/CSSimplify.cpp

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2804,6 +2804,13 @@ bool ConstraintSystem::repairFailures(
28042804
});
28052805
};
28062806

2807+
auto markAnyTypeVarsAsPotentialHoles = [&](Type type) {
2808+
type.visit([&](Type subType) {
2809+
if (auto *typeVar = subType->getAs<TypeVariableType>())
2810+
recordPotentialHole(typeVar);
2811+
});
2812+
};
2813+
28072814
if (path.empty()) {
28082815
if (!anchor)
28092816
return false;
@@ -3663,6 +3670,33 @@ bool ConstraintSystem::repairFailures(
36633670
break;
36643671
}
36653672

3673+
case ConstraintLocator::TernaryBranch: {
3674+
markAnyTypeVarsAsPotentialHoles(lhs);
3675+
markAnyTypeVarsAsPotentialHoles(rhs);
3676+
3677+
// If `if` expression has a contextual type, let's consider it a source of
3678+
// truth and produce a contextual mismatch instead of per-branch failure,
3679+
// because it's a better pointer than potential then-to-else type mismatch.
3680+
if (auto contextualType = getContextualType(anchor)) {
3681+
if (contextualType->isEqual(rhs)) {
3682+
auto *loc =
3683+
getConstraintLocator(anchor, LocatorPathElt::ContextualType());
3684+
if (hasFixFor(loc, FixKind::ContextualMismatch))
3685+
return true;
3686+
3687+
conversionsOrFixes.push_back(
3688+
ContextualMismatch::create(*this, lhs, rhs, loc));
3689+
break;
3690+
}
3691+
}
3692+
3693+
// If there is no contextual type, this is most likely a contextual type
3694+
// mismatch between then/else branches of ternary operator.
3695+
conversionsOrFixes.push_back(ContextualMismatch::create(
3696+
*this, lhs, rhs, getConstraintLocator(locator)));
3697+
break;
3698+
}
3699+
36663700
default:
36673701
break;
36683702
}
@@ -8626,7 +8660,24 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
86268660
}
86278661

86288662
case FixKind::ContextualMismatch: {
8629-
if (recordFix(fix))
8663+
auto impact = 1;
8664+
8665+
auto locator = fix->getLocator();
8666+
if (auto branchElt =
8667+
locator->getLastElementAs<LocatorPathElt::TernaryBranch>()) {
8668+
// If this is `else` branch of a ternary operator, let's
8669+
// increase its impact to eliminate the chance of ambiguity.
8670+
//
8671+
// Branches are connected through two `subtype` constraints
8672+
// to a common type variable with represents their join, which
8673+
// means that result would attempt a type from each side if
8674+
// one is available and that would result in two fixes - one for
8675+
// each mismatched branch.
8676+
if (branchElt->forElse())
8677+
impact = 10;
8678+
}
8679+
8680+
if (recordFix(fix, impact))
86308681
return SolutionKind::Error;
86318682

86328683
if (auto *fnType1 = type1->getAs<FunctionType>()) {

lib/Sema/ConstraintLocator.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,17 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor,
6161
case ConditionalRequirement:
6262
case TypeParameterRequirement:
6363
case ContextualType:
64-
case SynthesizedArgument: {
64+
case SynthesizedArgument:
65+
case TernaryBranch: {
6566
auto numValues = numNumericValuesInPathElement(elt.getKind());
6667
for (unsigned i = 0; i < numValues; ++i)
6768
id.AddInteger(elt.getValue(i));
6869
break;
6970
}
7071
#define SIMPLE_LOCATOR_PATH_ELT(Name) case Name :
7172
#include "ConstraintLocatorPathElts.def"
72-
// Nothing to do for simple locator elements.
73-
break;
73+
// Nothing to do for simple locator elements.
74+
break;
7475
}
7576
}
7677
}
@@ -115,6 +116,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const {
115116
case ConstraintLocator::Condition:
116117
case ConstraintLocator::DynamicCallable:
117118
case ConstraintLocator::ImplicitCallAsFunction:
119+
case ConstraintLocator::TernaryBranch:
118120
return 0;
119121

120122
case ConstraintLocator::FunctionArgument:
@@ -463,6 +465,12 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const {
463465
case ImplicitCallAsFunction:
464466
out << "implicit reference to callAsFunction";
465467
break;
468+
469+
case TernaryBranch:
470+
auto branchElt = elt.castTo<LocatorPathElt::TernaryBranch>();
471+
out << (branchElt.forThen() ? "'then'" : "'else'")
472+
<< " branch of a ternary operator";
473+
break;
466474
}
467475
}
468476
out << ']';

lib/Sema/ConstraintLocator.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
7878
case KeyPathComponent:
7979
case SynthesizedArgument:
8080
case KeyPathDynamicMember:
81+
case TernaryBranch:
8182
return 1;
8283

8384
case TypeParameterRequirement:
@@ -782,6 +783,20 @@ class LocatorPathElt::KeyPathDynamicMember final : public LocatorPathElt {
782783
}
783784
};
784785

786+
class LocatorPathElt::TernaryBranch final : public LocatorPathElt {
787+
public:
788+
TernaryBranch(bool side)
789+
: LocatorPathElt(ConstraintLocator::TernaryBranch, side) {}
790+
791+
bool forThen() const { return bool(getValue(0)); }
792+
793+
bool forElse() const { return !bool(getValue(0)); }
794+
795+
static bool classof(const LocatorPathElt *elt) {
796+
return elt->getKind() == ConstraintLocator::TernaryBranch;
797+
}
798+
};
799+
785800
/// A simple stack-only builder object that constructs a
786801
/// constraint locator without allocating memory.
787802
///

lib/Sema/ConstraintLocatorPathElts.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ SIMPLE_LOCATOR_PATH_ELT(Condition)
172172

173173
SIMPLE_LOCATOR_PATH_ELT(DynamicCallable)
174174

175+
/// The 'true' or 'false' branch of a ternary operator.
176+
CUSTOM_LOCATOR_PATH_ELT(TernaryBranch)
177+
175178
#undef LOCATOR_PATH_ELT
176179
#undef CUSTOM_LOCATOR_PATH_ELT
177180
#undef SIMPLE_LOCATOR_PATH_ELT

lib/Sema/ConstraintSystem.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3332,6 +3332,15 @@ void constraints::simplifyLocator(Expr *&anchor,
33323332
continue;
33333333
}
33343334

3335+
case ConstraintLocator::TernaryBranch: {
3336+
auto branch = path[0].castTo<LocatorPathElt::TernaryBranch>();
3337+
auto *ifExpr = cast<IfExpr>(anchor);
3338+
3339+
anchor = branch.forThen() ? ifExpr->getThenExpr() : ifExpr->getElseExpr();
3340+
path = path.slice(1);
3341+
continue;
3342+
}
3343+
33353344
default:
33363345
// FIXME: Lots of other cases to handle.
33373346
break;

test/Constraints/diagnostics.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,8 @@ func r18800223(_ i : Int) {
289289

290290

291291
var buttonTextColor: String?
292-
_ = (buttonTextColor != nil) ? 42 : {$0}; // expected-error {{unable to infer closure type in the current context}}
292+
_ = (buttonTextColor != nil) ? 42 : {$0}; // expected-error {{result values in '? :' expression have mismatching types 'Int' and '(_) -> _'}}
293+
// expected-error@-1 {{unable to infer closure return type; add explicit type to disambiguate}}
293294
}
294295

295296
// <rdar://problem/21883806> Bogus "'_' can only appear in a pattern or on the left side of an assignment" is back

test/Constraints/one_way_constraints.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ func testTernaryOneWay(b: Bool) {
1010
let _: Float = b ? 3.14159 : 17
1111

1212
// Errors due to one-way inference.
13-
let _: Float = b ? Builtin.one_way(3.14159) // expected-error{{cannot convert value of type 'Double' to specified type 'Float'}}
13+
let _: Float = b ? Builtin.one_way(3.14159) // expected-error{{result values in '? :' expression have mismatching types 'Double' and 'Int'}}
1414
: 17
15-
let _: Float = b ? 3.14159
16-
: Builtin.one_way(17) // expected-error{{cannot convert value of type 'Int' to specified type 'Float'}}
17-
let _: Float = b ? Builtin.one_way(3.14159) // expected-error{{cannot convert value of type 'Double' to specified type 'Float'}}
15+
let _: Float = b ? 3.14159 // expected-error {{cannot convert value of type 'Int' to specified type 'Float'}}
16+
: Builtin.one_way(17)
17+
let _: Float = b ? Builtin.one_way(3.14159) // expected-error {{cannot convert value of type 'Int' to specified type 'Float'}}
1818
: Builtin.one_way(17)
1919

2020
// Okay: default still works.

0 commit comments

Comments
 (0)