Skip to content

Commit cfaecfc

Browse files
authored
Merge pull request #26755 from xedin/port-throws-contextual-mismatch
[Diagnostics] Port tailored contextual diagnostic when thrown type do…
2 parents 5092d18 + 8296b55 commit cfaecfc

File tree

4 files changed

+63
-40
lines changed

4 files changed

+63
-40
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,43 +1683,6 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
16831683
if (isUnresolvedOrTypeVarType(exprType) || exprType->isEqual(contextualType))
16841684
return false;
16851685

1686-
// If this is conversion failure due to a return statement with an argument
1687-
// that cannot be coerced to the result type of the function, emit a
1688-
// specific error.
1689-
if (CTP == CTP_ThrowStmt) {
1690-
// If we tried to throw the error code of an error type, suggest object
1691-
// construction.
1692-
auto &TC = CS.getTypeChecker();
1693-
if (auto errorCodeProtocol =
1694-
TC.Context.getProtocol(KnownProtocolKind::ErrorCodeProtocol)) {
1695-
if (auto conformance = TypeChecker::conformsToProtocol(
1696-
CS.getType(expr), errorCodeProtocol, CS.DC,
1697-
ConformanceCheckFlags::InExpression)) {
1698-
Type errorCodeType = CS.getType(expr);
1699-
Type errorType =
1700-
conformance
1701-
->getTypeWitnessByName(errorCodeType, TC.Context.Id_ErrorType)
1702-
->getCanonicalType();
1703-
if (errorType) {
1704-
auto diag = diagnose(expr->getLoc(), diag::cannot_throw_error_code,
1705-
errorCodeType, errorType);
1706-
if (auto unresolvedDot = dyn_cast<UnresolvedDotExpr>(expr)) {
1707-
diag.fixItInsert(unresolvedDot->getDotLoc(), "(");
1708-
diag.fixItInsertAfter(unresolvedDot->getEndLoc(), ")");
1709-
}
1710-
return true;
1711-
}
1712-
}
1713-
}
1714-
1715-
// The conversion destination of throw is always ErrorType (at the moment)
1716-
// if this ever expands, this should be a specific form like () is for
1717-
// return.
1718-
diagnose(expr->getLoc(), diag::cannot_convert_thrown_type, exprType)
1719-
.highlight(expr->getSourceRange());
1720-
return true;
1721-
}
1722-
17231686
if (CTP == CTP_YieldByReference) {
17241687
if (auto contextualLV = contextualType->getAs<LValueType>())
17251688
contextualType = contextualLV->getObjectType();

lib/Sema/CSDiagnostics.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1882,6 +1882,9 @@ bool ContextualFailure::diagnoseAsError() {
18821882
if (diagnoseConversionToBool())
18831883
return true;
18841884

1885+
if (diagnoseThrowsTypeMismatch())
1886+
return true;
1887+
18851888
auto contextualType = getToType();
18861889
if (auto msg = getDiagnosticFor(CTP, contextualType->isExistentialType())) {
18871890
diagnostic = *msg;
@@ -2191,6 +2194,50 @@ bool ContextualFailure::diagnoseConversionToDictionary() const {
21912194
return true;
21922195
}
21932196

2197+
bool ContextualFailure::diagnoseThrowsTypeMismatch() const {
2198+
// If this is conversion failure due to a return statement with an argument
2199+
// that cannot be coerced to the result type of the function, emit a
2200+
// specific error.
2201+
if (CTP != CTP_ThrowStmt)
2202+
return false;
2203+
2204+
auto *anchor = getAnchor();
2205+
2206+
// If we tried to throw the error code of an error type, suggest object
2207+
// construction.
2208+
auto &TC = getTypeChecker();
2209+
if (auto errorCodeProtocol =
2210+
TC.Context.getProtocol(KnownProtocolKind::ErrorCodeProtocol)) {
2211+
Type errorCodeType = getFromType();
2212+
if (auto conformance = TypeChecker::conformsToProtocol(
2213+
errorCodeType, errorCodeProtocol, getDC(),
2214+
ConformanceCheckFlags::InExpression)) {
2215+
Type errorType = conformance
2216+
->getTypeWitnessByName(errorCodeType,
2217+
getASTContext().Id_ErrorType)
2218+
->getCanonicalType();
2219+
if (errorType) {
2220+
auto diagnostic =
2221+
emitDiagnostic(anchor->getLoc(), diag::cannot_throw_error_code,
2222+
errorCodeType, errorType);
2223+
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor)) {
2224+
diagnostic.fixItInsert(UDE->getDotLoc(), "(");
2225+
diagnostic.fixItInsertAfter(UDE->getEndLoc(), ")");
2226+
}
2227+
return true;
2228+
}
2229+
}
2230+
}
2231+
2232+
// The conversion destination of throw is always ErrorType (at the moment)
2233+
// if this ever expands, this should be a specific form like () is for
2234+
// return.
2235+
emitDiagnostic(anchor->getLoc(), diag::cannot_convert_thrown_type,
2236+
getFromType())
2237+
.highlight(anchor->getSourceRange());
2238+
return true;
2239+
}
2240+
21942241
bool ContextualFailure::tryRawRepresentableFixIts(
21952242
InFlightDiagnostic &diagnostic,
21962243
KnownProtocolKind rawRepresentableProtocol) const {

lib/Sema/CSDiagnostics.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,10 @@ class ContextualFailure : public FailureDiagnostic {
629629
/// or convert an array literal to a dictionary e.g. `let _: [String: Int] = ["A", 0]`
630630
bool diagnoseConversionToDictionary() const;
631631

632+
/// Produce a specialized diagnostic if this is an attempt to throw
633+
/// something with doesn't conform to `Error`.
634+
bool diagnoseThrowsTypeMismatch() const;
635+
632636
/// Attempt to attach any relevant fix-its to already produced diagnostic.
633637
void tryFixIts(InFlightDiagnostic &diagnostic) const;
634638

lib/Sema/CSSimplify.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3831,14 +3831,23 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
38313831
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
38323832
}
38333833

3834+
if (path.empty())
3835+
return SolutionKind::Error;
3836+
3837+
// If this is a conformance failure related to a contextual type
3838+
// let's record it as a "contextual mismatch" because diagnostic
3839+
// is going to be dependent on other contextual information.
3840+
if (path.back().is<LocatorPathElt::ContextualType>()) {
3841+
auto *fix = ContextualMismatch::create(*this, type, protocolTy,
3842+
getConstraintLocator(locator));
3843+
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
3844+
}
3845+
38343846
// Let's not try to fix missing conformance for Void
38353847
// or Never because that doesn't really make sense.
38363848
if (type->isVoid() || type->isUninhabited())
38373849
return SolutionKind::Error;
38383850

3839-
if (path.empty())
3840-
return SolutionKind::Error;
3841-
38423851
if (path.back().is<LocatorPathElt::AnyRequirement>()) {
38433852
if (auto *fix =
38443853
fixRequirementFailure(*this, type, protocolTy, anchor, path)) {

0 commit comments

Comments
 (0)