Skip to content

[Diagnostics] Handle CoerceExpr conversion failure in contextual mismatch #29011

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1888,16 +1888,6 @@ bool FailureDiagnosis::visitCoerceExpr(CoerceExpr *CE) {
if (!expr)
return true;

auto ref = expr->getReferencedDecl();
if (auto *decl = ref.getDecl()) {
// Without explicit coercion we might end up
// type-checking sub-expression as unavaible
// declaration, let's try to diagnose that here.
if (AvailableAttr::isUnavailable(decl))
return diagnoseExplicitUnavailability(
decl, expr->getSourceRange(), CS.DC, dyn_cast<ApplyExpr>(expr));
}

return false;
}

Expand Down
29 changes: 29 additions & 0 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1853,6 +1853,9 @@ bool ContextualFailure::diagnoseAsError() {
getFromType(), getToType());
return true;
}

if (diagnoseCoercionToUnrelatedType())
return true;

return false;
}
Expand Down Expand Up @@ -2220,6 +2223,28 @@ bool ContextualFailure::diagnoseMissingFunctionCall() const {
return true;
}

bool ContextualFailure::diagnoseCoercionToUnrelatedType() const {
auto *anchor = getAnchor();

if (auto *coerceExpr = dyn_cast<CoerceExpr>(anchor)) {
auto fromType = getFromType();
auto toType = getType(coerceExpr->getCastTypeLoc());
auto diagnostic =
getDiagnosticFor(CTP_CoerceOperand,
/*forProtocol=*/toType->isAnyExistentialType());

auto diag =
emitDiagnostic(anchor->getLoc(), *diagnostic, fromType, toType);
diag.highlight(anchor->getSourceRange());

(void)tryFixIts(diag);

return true;
}

return false;
}

bool ContextualFailure::diagnoseConversionToBool() const {
auto toType = getToType();
if (!toType->isBool())
Expand Down Expand Up @@ -2565,6 +2590,10 @@ bool ContextualFailure::trySequenceSubsequenceFixIts(
if (getFromType()->isEqual(Substring)) {
if (getToType()->isEqual(String)) {
auto *anchor = getAnchor()->getSemanticsProvidingExpr();
if (auto *CE = dyn_cast<CoerceExpr>(anchor)) {
anchor = CE->getSubExpr();
}

auto range = anchor->getSourceRange();
diagnostic.fixItInsert(range.Start, "String(");
diagnostic.fixItInsertAfter(range.End, ")");
Expand Down
3 changes: 3 additions & 0 deletions lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,9 @@ class ContextualFailure : public FailureDiagnostic {

/// If we're trying to convert something to `nil`.
bool diagnoseConversionToNil() const;

/// Diagnose failed conversion in a `CoerceExpr`.
bool diagnoseCoercionToUnrelatedType() const;

// If we're trying to convert something of type "() -> T" to T,
// then we probably meant to call the value.
Expand Down
9 changes: 9 additions & 0 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2823,6 +2823,15 @@ bool ConstraintSystem::repairFailures(
conversionsOrFixes.push_back(coerceToCheckCastFix);
return true;
}

// If it has a deep equality restriction, defer the diagnostic to
// GenericMismatch.
if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality))
return false;

auto *fix = ContextualMismatch::create(*this, lhs, rhs,
getConstraintLocator(locator));
conversionsOrFixes.push_back(fix);
}

// This could be:
Expand Down
8 changes: 5 additions & 3 deletions test/Parse/recovery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -619,9 +619,11 @@ class WrongInheritanceClause6(Int {}
class WrongInheritanceClause7<T>(Int where T:AnyObject {}

// <rdar://problem/18502220> [swift-crashes 078] parser crash on invalid cast in sequence expr
Base=1 as Base=1 // expected-error {{cannot convert value of type 'Int' to type 'Base' in coercion}}


Base=1 as Base=1 // expected-error{{cannot convert value of type 'Int' to type 'Base' in coercion}}
// expected-error@-1 {{cannot assign to immutable expression of type 'Base.Type'}}
// expected-error@-2 {{cannot assign to immutable expression of type 'Base'}}
// expected-error@-3 {{cannot assign value of type '()' to type 'Base.Type'}}
// expected-error@-4 {{cannot assign value of type 'Int' to type 'Base'}}

// <rdar://problem/18634543> Parser hangs at swift::Parser::parseType
public enum TestA {
Expand Down
3 changes: 1 addition & 2 deletions test/type/subclass_composition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,7 @@ func basicSubtyping(
let _: Derived = baseAndP2 // expected-error {{cannot convert value of type 'Base<Int> & P2' to specified type 'Derived'}}
let _: Derived & P2 = baseAndP2 // expected-error {{value of type 'Base<Int> & P2' does not conform to specified type 'Derived & P2'}}

// TODO(diagnostics): Diagnostic regression, better message is `value of type 'Unrelated' does not conform to 'Derived & P2' in coercion`
let _ = Unrelated() as Derived & P2 // expected-error {{cannot convert value of type 'Unrelated' to type 'Derived' in coercion}}
let _ = Unrelated() as Derived & P2 // expected-error {{value of type 'Unrelated' does not conform to 'Derived & P2' in coercion}}
let _ = Unrelated() as? Derived & P2 // expected-warning {{always fails}}
let _ = baseAndP2 as Unrelated // expected-error {{cannot convert value of type 'Base<Int> & P2' to type 'Unrelated' in coercion}}
let _ = baseAndP2 as? Unrelated // expected-warning {{always fails}}
Expand Down