Skip to content

Commit d5a9d6a

Browse files
[ConstraintSystem] Introduce new locator path elemet to identify coercion
1 parent 63315cc commit d5a9d6a

File tree

5 files changed

+84
-64
lines changed

5 files changed

+84
-64
lines changed

include/swift/Sema/ConstraintLocatorPathElts.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,9 @@ ABSTRACT_LOCATOR_PATH_ELT(PatternDecl)
265265
/// A function type global actor.
266266
SIMPLE_LOCATOR_PATH_ELT(GlobalActorType)
267267

268+
/// A type coercion operand.
269+
SIMPLE_LOCATOR_PATH_ELT(CoercionOperand)
270+
268271
#undef LOCATOR_PATH_ELT
269272
#undef CUSTOM_LOCATOR_PATH_ELT
270273
#undef SIMPLE_LOCATOR_PATH_ELT

lib/Sema/CSApply.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4275,8 +4275,15 @@ namespace {
42754275
bool hasForcedOptionalResult(ExplicitCastExpr *expr) {
42764276
const auto *const TR = expr->getCastTypeRepr();
42774277
if (TR && TR->getKind() == TypeReprKind::ImplicitlyUnwrappedOptional) {
4278-
auto *locator = cs.getConstraintLocator(
4279-
expr, ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice);
4278+
ConstraintLocator *locator;
4279+
if (isExpr<CoerceExpr>(expr)) {
4280+
locator = cs.getConstraintLocator(
4281+
expr, {LocatorPathElt::CoercionOperand(),
4282+
ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice});
4283+
} else {
4284+
locator = cs.getConstraintLocator(
4285+
expr, ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice);
4286+
}
42804287
return solution.getDisjunctionChoice(locator);
42814288
}
42824289
return false;
@@ -4349,7 +4356,8 @@ namespace {
43494356
// If we weren't explicitly told by the caller which disjunction choice,
43504357
// get it from the solution to determine whether we've picked a coercion
43514358
// or a bridging conversion.
4352-
auto *locator = cs.getConstraintLocator(expr);
4359+
auto *locator =
4360+
cs.getConstraintLocator(expr, LocatorPathElt::CoercionOperand());
43534361
auto choice = solution.getDisjunctionChoice(locator);
43544362

43554363
// Handle the coercion/bridging of the underlying subexpression, where

lib/Sema/CSGen.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3306,7 +3306,8 @@ namespace {
33063306
if (repr) CS.setType(repr, toType);
33073307

33083308
auto fromType = CS.getType(expr->getSubExpr());
3309-
auto locator = CS.getConstraintLocator(expr);
3309+
auto locator =
3310+
CS.getConstraintLocator(expr, LocatorPathElt::CoercionOperand());
33103311

33113312
// Literal initialization (e.g. `UInt32(0)`) doesn't require
33123313
// a conversion because the literal is supposed to assume the

lib/Sema/CSSimplify.cpp

Lines changed: 61 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -5065,63 +5065,6 @@ bool ConstraintSystem::repairFailures(
50655065
if (!anchor)
50665066
return false;
50675067

5068-
if (auto *coercion = getAsExpr<CoerceExpr>(anchor)) {
5069-
// Coercion from T.Type to T.Protocol.
5070-
if (hasConversionOrRestriction(
5071-
ConversionRestrictionKind::MetatypeToExistentialMetatype))
5072-
return false;
5073-
5074-
if (hasConversionOrRestriction(ConversionRestrictionKind::Superclass))
5075-
return false;
5076-
5077-
// Let's check whether the sub-expression is an optional type which
5078-
// is possible to unwrap (either by force or `??`) to satisfy the cast,
5079-
// otherwise we'd have to fallback to force downcast.
5080-
if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind,
5081-
conversionsOrFixes,
5082-
getConstraintLocator(coercion->getSubExpr())))
5083-
return true;
5084-
5085-
// If the result type of the coercion has an value to optional conversion
5086-
// we can instead suggest the conditional downcast as it is safer in
5087-
// situations like conditional binding.
5088-
auto useConditionalCast =
5089-
llvm::any_of(ConstraintRestrictions, [&](const auto &restriction) {
5090-
Type type1, type2;
5091-
std::tie(type1, type2) = restriction.first;
5092-
auto restrictionKind = restriction.second;
5093-
5094-
if (restrictionKind != ConversionRestrictionKind::ValueToOptional)
5095-
return false;
5096-
5097-
return rhs->isEqual(type1);
5098-
});
5099-
5100-
// Repair a coercion ('as') with a runtime checked cast ('as!' or 'as?').
5101-
if (auto *coerceToCheckCastFix =
5102-
CoerceToCheckedCast::attempt(*this, lhs, rhs, useConditionalCast,
5103-
getConstraintLocator(locator))) {
5104-
conversionsOrFixes.push_back(coerceToCheckCastFix);
5105-
return true;
5106-
}
5107-
5108-
// If it has a deep equality restriction, defer the diagnostic to
5109-
// GenericMismatch.
5110-
if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) &&
5111-
!hasConversionOrRestriction(
5112-
ConversionRestrictionKind::OptionalToOptional)) {
5113-
return false;
5114-
}
5115-
5116-
if (hasConversionOrRestriction(ConversionRestrictionKind::Existential))
5117-
return false;
5118-
5119-
auto *fix = ContextualMismatch::create(*this, lhs, rhs,
5120-
getConstraintLocator(locator));
5121-
conversionsOrFixes.push_back(fix);
5122-
return true;
5123-
}
5124-
51255068
// This could be:
51265069
// - `InOutExpr` used with r-value e.g. `foo(&x)` where `x` is a `let`.
51275070
// - `ForceValueExpr` e.g. `foo.bar! = 42` where `bar` or `foo` are
@@ -6456,6 +6399,64 @@ bool ConstraintSystem::repairFailures(
64566399
break;
64576400
}
64586401

6402+
case ConstraintLocator::CoercionOperand: {
6403+
auto *coercion = castToExpr<CoerceExpr>(anchor);
6404+
6405+
// Coercion from T.Type to T.Protocol.
6406+
if (hasConversionOrRestriction(
6407+
ConversionRestrictionKind::MetatypeToExistentialMetatype))
6408+
return false;
6409+
6410+
if (hasConversionOrRestriction(ConversionRestrictionKind::Superclass))
6411+
return false;
6412+
6413+
// Let's check whether the sub-expression is an optional type which
6414+
// is possible to unwrap (either by force or `??`) to satisfy the cast,
6415+
// otherwise we'd have to fallback to force downcast.
6416+
if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind,
6417+
conversionsOrFixes,
6418+
getConstraintLocator(coercion->getSubExpr())))
6419+
return true;
6420+
6421+
// If the result type of the coercion has an value to optional conversion
6422+
// we can instead suggest the conditional downcast as it is safer in
6423+
// situations like conditional binding.
6424+
auto useConditionalCast =
6425+
llvm::any_of(ConstraintRestrictions, [&](const auto &restriction) {
6426+
Type type1, type2;
6427+
std::tie(type1, type2) = restriction.first;
6428+
auto restrictionKind = restriction.second;
6429+
6430+
if (restrictionKind != ConversionRestrictionKind::ValueToOptional)
6431+
return false;
6432+
6433+
return rhs->isEqual(type1);
6434+
});
6435+
6436+
// Repair a coercion ('as') with a runtime checked cast ('as!' or 'as?').
6437+
if (auto *coerceToCheckCastFix =
6438+
CoerceToCheckedCast::attempt(*this, lhs, rhs, useConditionalCast,
6439+
getConstraintLocator(locator))) {
6440+
conversionsOrFixes.push_back(coerceToCheckCastFix);
6441+
return true;
6442+
}
6443+
6444+
// If it has a deep equality restriction, defer the diagnostic to
6445+
// GenericMismatch.
6446+
if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) &&
6447+
!hasConversionOrRestriction(
6448+
ConversionRestrictionKind::OptionalToOptional)) {
6449+
return false;
6450+
}
6451+
6452+
if (hasConversionOrRestriction(ConversionRestrictionKind::Existential))
6453+
return false;
6454+
6455+
auto *fix = ContextualMismatch::create(*this, lhs, rhs,
6456+
getConstraintLocator(locator));
6457+
conversionsOrFixes.push_back(fix);
6458+
return true;
6459+
}
64596460
default:
64606461
break;
64616462
}
@@ -6974,7 +6975,8 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
69746975
ArrayRef<LocatorPathElt> path) {
69756976
// E.g. contextual conversion from coercion/cast
69766977
// to some other type.
6977-
if (!path.empty())
6978+
if (!(path.empty() ||
6979+
path.back().is<LocatorPathElt::CoercionOperand>()))
69786980
return false;
69796981

69806982
return isExpr<CoerceExpr>(anchor) || isExpr<IsExpr>(anchor) ||
@@ -11339,7 +11341,7 @@ ConstraintSystem::simplifyBridgingConstraint(Type type1,
1133911341

1134011342
SmallVector<LocatorPathElt, 4> elts;
1134111343
auto anchor = locator.getLocatorParts(elts);
11342-
if (!elts.empty())
11344+
if (elts.empty() || !elts.back().is<LocatorPathElt::CoercionOperand>())
1134311345
return false;
1134411346

1134511347
auto *coercion = getAsExpr<CoerceExpr>(anchor);

lib/Sema/ConstraintLocator.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const {
105105
case ConstraintLocator::SingleValueStmtBranch:
106106
case ConstraintLocator::AnyPatternDecl:
107107
case ConstraintLocator::GlobalActorType:
108+
case ConstraintLocator::CoercionOperand:
108109
return 0;
109110

110111
case ConstraintLocator::FunctionArgument:
@@ -496,6 +497,11 @@ void LocatorPathElt::dump(raw_ostream &out) const {
496497
out << "global actor type";
497498
break;
498499
}
500+
501+
case ConstraintLocator::CoercionOperand: {
502+
out << "coercion operand";
503+
break;
504+
}
499505
}
500506
}
501507

@@ -601,7 +607,7 @@ bool ConstraintLocator::isForAssignment() const {
601607
}
602608

603609
bool ConstraintLocator::isForCoercion() const {
604-
return directlyAt<CoerceExpr>();
610+
return isLastElement<LocatorPathElt::CoercionOperand>();
605611
}
606612

607613
bool ConstraintLocator::isForOptionalTry() const {

0 commit comments

Comments
 (0)