Skip to content

[Diagnostics] Port diagnostic for CTP_YieldByReference #26772

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 1 commit into from
Aug 22, 2019
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
18 changes: 0 additions & 18 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1683,24 +1683,6 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
if (isUnresolvedOrTypeVarType(exprType) || exprType->isEqual(contextualType))
return false;

if (CTP == CTP_YieldByReference) {
if (auto contextualLV = contextualType->getAs<LValueType>())
contextualType = contextualLV->getObjectType();
if (auto exprLV = exprType->getAs<LValueType>()) {
diagnose(expr->getLoc(), diag::cannot_yield_wrong_type_by_reference,
exprLV->getObjectType(), contextualType);
} else if (exprType->isEqual(contextualType)) {
diagnose(expr->getLoc(), diag::cannot_yield_rvalue_by_reference_same_type,
exprType);
} else {
diagnose(expr->getLoc(), diag::cannot_yield_rvalue_by_reference, exprType,
contextualType);
}
return true;
}

exprType = exprType->getRValueType();

// Don't attempt fixits if we have an unsolved type variable, since
// the recovery path's recursion into the type checker via typeCheckCast()
// will confuse matters.
Expand Down
24 changes: 24 additions & 0 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1885,6 +1885,9 @@ bool ContextualFailure::diagnoseAsError() {
if (diagnoseThrowsTypeMismatch())
return true;

if (diagnoseYieldByReferenceMismatch())
return true;

auto contextualType = getToType();
if (auto msg = getDiagnosticFor(CTP, contextualType->isExistentialType())) {
diagnostic = *msg;
Expand Down Expand Up @@ -2238,6 +2241,27 @@ bool ContextualFailure::diagnoseThrowsTypeMismatch() const {
return true;
}

bool ContextualFailure::diagnoseYieldByReferenceMismatch() const {
if (CTP != CTP_YieldByReference)
return false;

auto *anchor = getAnchor();
auto exprType = getType(anchor);
auto contextualType = getToType();

if (auto exprLV = exprType->getAs<LValueType>()) {
emitDiagnostic(anchor->getLoc(), diag::cannot_yield_wrong_type_by_reference,
exprLV->getObjectType(), contextualType);
} else if (exprType->isEqual(contextualType)) {
emitDiagnostic(anchor->getLoc(),
diag::cannot_yield_rvalue_by_reference_same_type, exprType);
} else {
emitDiagnostic(anchor->getLoc(), diag::cannot_yield_rvalue_by_reference,
exprType, contextualType);
}
return true;
}

bool ContextualFailure::tryRawRepresentableFixIts(
InFlightDiagnostic &diagnostic,
KnownProtocolKind rawRepresentableProtocol) const {
Expand Down
4 changes: 4 additions & 0 deletions lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,10 @@ class ContextualFailure : public FailureDiagnostic {
/// something with doesn't conform to `Error`.
bool diagnoseThrowsTypeMismatch() const;

/// Produce a specialized diagnostic if this is an attempt to `yield`
/// something of incorrect type.
bool diagnoseYieldByReferenceMismatch() const;

/// Attempt to attach any relevant fix-its to already produced diagnostic.
void tryFixIts(InFlightDiagnostic &diagnostic) const;

Expand Down
49 changes: 46 additions & 3 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2028,15 +2028,36 @@ ConstraintSystem::matchTypesBindTypeVar(
type = type->getRValueType();
}

// Attempt to fix situations where type variable can't be bound
// to a particular type e.g. `l-value` or `inout`.
auto fixReferenceMismatch = [&](TypeVariableType *typeVar,
Type type) -> bool {
if (auto last = locator.last()) {
if (last->is<LocatorPathElt::ContextualType>()) {
auto *fix = IgnoreContextualType::create(*this, typeVar, type,
getConstraintLocator(locator));
return !recordFix(fix);
}
}

return false;
};

// If the left-hand type variable cannot bind to an lvalue,
// but we still have an lvalue, fail.
if (!typeVar->getImpl().canBindToLValue() && type->hasLValueType()) {
return getTypeMatchFailure(locator);
if (shouldAttemptFixes() && fixReferenceMismatch(typeVar, type))
return getTypeMatchSuccess();

return getTypeMatchFailure(locator);
}

// If the left-hand type variable cannot bind to an inout,
// but we still have an inout, fail.
if (!typeVar->getImpl().canBindToInOut() && type->is<InOutType>()) {
if (shouldAttemptFixes() && fixReferenceMismatch(typeVar, type))
return getTypeMatchSuccess();

return getTypeMatchFailure(locator);
}

Expand Down Expand Up @@ -2399,9 +2420,31 @@ bool ConstraintSystem::repairFailures(
});
};

auto &elt = path.back();
auto elt = path.back();
switch (elt.getKind()) {
case ConstraintLocator::LValueConversion:
case ConstraintLocator::LValueConversion: {
auto CTP = getContextualTypePurpose();
// Special case for `CTP_CallArgument` set by CSDiag
// while type-checking each argument because we yet
// to cover argument-to-parameter conversions in the
// new framework.
if (CTP != CTP_CallArgument) {
// Ignore l-value conversion element since it has already
// played its role.
path.pop_back();
// If this is a contextual mismatch between l-value types e.g.
// `@lvalue String vs. @lvalue Int`, let's pretend that it's okay.
if (!path.empty() && path.back().is<LocatorPathElt::ContextualType>()) {
auto *locator = getConstraintLocator(anchor, path.back());
conversionsOrFixes.push_back(
IgnoreContextualType::create(*this, lhs, rhs, locator));
break;
}
}

LLVM_FALLTHROUGH;
}

case ConstraintLocator::ApplyArgToParam: {
auto loc = getConstraintLocator(locator);
if (repairByInsertingExplicitCall(lhs, rhs))
Expand Down
2 changes: 1 addition & 1 deletion test/stmt/yield.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct YieldVariables {
}
_modify {
var x = 0
yield &x // expected-error {{cannot yield immutable value of type 'Int' as an inout yield of type 'String'}}
yield &x // expected-error {{cannot yield reference to storage of type 'Int' as an inout yield of type 'String'}}
}
}

Expand Down