Move another chunk of lvalue diagnostics over to being handled via ConstraintFix.

This commit is contained in:
gregomni
2018-08-19 13:14:55 -07:00
parent aeb96274d2
commit 19fce5d36f
5 changed files with 80 additions and 72 deletions

View File

@@ -850,28 +850,30 @@ void swift::diagnoseSubElementFailure(Expr *destExpr,
return; return;
} }
if (auto *ICE = dyn_cast<ImplicitConversionExpr>(immInfo.first)) if (auto contextualType = CS.getContextualType(immInfo.first)) {
if (auto *LE = dyn_cast<LoadExpr>(ICE->getSubExpr())) { Type neededType = contextualType->getInOutObjectType();
Type actualType = CS.getType(LE); Type actualType = CS.getType(immInfo.first)->getInOutObjectType();
Type neededType = CS.getType(ICE); if (!neededType->isEqual(actualType)) {
if (diagID.ID == diag::cannot_pass_rvalue_inout_subelement.ID) { if (diagID.ID == diag::cannot_pass_rvalue_inout_subelement.ID) {
// We have a special diagnostic with tailored wording for this // We have a special diagnostic with tailored wording for this
// common case. // common case.
TC.diagnose(loc, diag::cannot_pass_rvalue_inout_converted, TC.diagnose(loc, diag::cannot_pass_rvalue_inout_converted, actualType,
actualType, neededType) neededType)
.highlight(ICE->getSourceRange()); .highlight(immInfo.first->getSourceRange());
fixItChangeInoutArgType(LE->getSubExpr(), actualType, neededType, CS); if (auto inoutExpr = dyn_cast<InOutExpr>(immInfo.first))
} fixItChangeInoutArgType(inoutExpr->getSubExpr(), actualType,
else neededType, CS);
} else {
TC.diagnose(loc, diagID, TC.diagnose(loc, diagID,
"implicit conversion from '" + "implicit conversion from '" + actualType->getString() +
actualType->getString() + "' to '" + "' to '" + neededType->getString() +
neededType->getString() + "' requires a temporary") "' requires a temporary")
.highlight(ICE->getSourceRange()); .highlight(immInfo.first->getSourceRange());
}
return; return;
} }
}
if (auto IE = dyn_cast<IfExpr>(immInfo.first)) { if (auto IE = dyn_cast<IfExpr>(immInfo.first)) {
if (isLoadedLValue(IE)) { if (isLoadedLValue(IE)) {
@@ -6527,9 +6529,7 @@ bool FailureDiagnosis::visitInOutExpr(InOutExpr *IOE) {
if (auto unwrapped = contextualType->getOptionalObjectType()) if (auto unwrapped = contextualType->getOptionalObjectType())
unwrappedType = unwrapped; unwrappedType = unwrapped;
PointerTypeKind pointerKind; if (auto pointerEltType = unwrappedType->getAnyPointerElementType()) {
if (auto pointerEltType =
unwrappedType->getAnyPointerElementType(pointerKind)) {
// If the element type is Void, then we allow any input type, since // If the element type is Void, then we allow any input type, since
// everything is convertible to UnsafeRawPointer // everything is convertible to UnsafeRawPointer
@@ -6541,20 +6541,7 @@ bool FailureDiagnosis::visitInOutExpr(InOutExpr *IOE) {
// Furthermore, if the subexpr type is already known to be an array type, // Furthermore, if the subexpr type is already known to be an array type,
// then we must have an attempt at an array to pointer conversion. // then we must have an attempt at an array to pointer conversion.
if (isKnownToBeArrayType(CS.getType(IOE->getSubExpr()))) { if (isKnownToBeArrayType(CS.getType(IOE->getSubExpr()))) {
// If we're converting to an UnsafeMutablePointer, then the pointer to contextualType = ArraySliceType::get(contextualType);
// the first element is being passed in. The array is ok, so long as
// it is mutable.
if (pointerKind == PTK_UnsafeMutablePointer) {
contextualType = ArraySliceType::get(contextualType);
} else if (pointerKind == PTK_UnsafePointer || pointerKind == PTK_UnsafeRawPointer) {
// If we're converting to an UnsafePointer, then the programmer
// specified an & unnecessarily. Produce a fixit hint to remove it.
diagnose(IOE->getLoc(), diag::extra_address_of_unsafepointer,
unwrappedType)
.highlight(IOE->getSourceRange())
.fixItRemove(IOE->getStartLoc());
return true;
}
} }
} else if (contextualType->is<InOutType>()) { } else if (contextualType->is<InOutType>()) {
contextualType = contextualType->getInOutObjectType(); contextualType = contextualType->getInOutObjectType();
@@ -6568,21 +6555,11 @@ bool FailureDiagnosis::visitInOutExpr(InOutExpr *IOE) {
} }
} }
auto subExpr = typeCheckChildIndependently(IOE->getSubExpr(), contextualType, if (!typeCheckChildIndependently(IOE->getSubExpr(), contextualType,
CS.getContextualTypePurpose(), CS.getContextualTypePurpose(),
TCC_AllowLValue); TCC_AllowLValue)) {
if (!subExpr) return true;
auto subExprType = CS.getType(subExpr);
// The common cause is that the operand is not an lvalue.
if (!subExprType->hasLValueType()) {
diagnoseSubElementFailure(subExpr, IOE->getLoc(), CS,
diag::cannot_pass_rvalue_inout_subelement,
diag::cannot_pass_rvalue_inout);
return true; return true;
} }
return false; return false;
} }

View File

@@ -535,34 +535,58 @@ bool RValueTreatedAsLValueFailure::diagnose() {
Expr *diagExpr = getLocator()->getAnchor(); Expr *diagExpr = getLocator()->getAnchor();
SourceLoc loc; SourceLoc loc;
auto callExpr = dyn_cast<ApplyExpr>(diagExpr); if (auto callExpr = dyn_cast<ApplyExpr>(diagExpr)) {
if (!callExpr) Expr *argExpr = callExpr->getArg();
return false; // currently only creating these for args, so should be loc = callExpr->getFn()->getLoc();
// unreachable
Expr *argExpr = callExpr->getArg(); if (isa<PrefixUnaryExpr>(callExpr) || isa<PostfixUnaryExpr>(callExpr)) {
loc = callExpr->getFn()->getLoc(); subElementDiagID = diag::cannot_apply_lvalue_unop_to_subelement;
rvalueDiagID = diag::cannot_apply_lvalue_unop_to_rvalue;
diagExpr = argExpr;
} else if (isa<BinaryExpr>(callExpr)) {
subElementDiagID = diag::cannot_apply_lvalue_binop_to_subelement;
rvalueDiagID = diag::cannot_apply_lvalue_binop_to_rvalue;
auto argTuple = dyn_cast<TupleExpr>(argExpr);
diagExpr = argTuple->getElement(0);
} else {
auto lastPathElement = getLocator()->getPath().back();
assert(lastPathElement.getKind() ==
ConstraintLocator::PathElementKind::ApplyArgToParam);
if (isa<PrefixUnaryExpr>(callExpr) || isa<PostfixUnaryExpr>(callExpr)) { subElementDiagID = diag::cannot_pass_rvalue_inout_subelement;
subElementDiagID = diag::cannot_apply_lvalue_unop_to_subelement; rvalueDiagID = diag::cannot_pass_rvalue_inout;
rvalueDiagID = diag::cannot_apply_lvalue_unop_to_rvalue; if (auto argTuple = dyn_cast<TupleExpr>(argExpr))
diagExpr = argExpr; diagExpr = argTuple->getElement(lastPathElement.getValue());
} else if (isa<BinaryExpr>(callExpr)) { else if (auto parens = dyn_cast<ParenExpr>(argExpr))
subElementDiagID = diag::cannot_apply_lvalue_binop_to_subelement; diagExpr = parens->getSubExpr();
rvalueDiagID = diag::cannot_apply_lvalue_binop_to_rvalue; }
auto argTuple = dyn_cast<TupleExpr>(argExpr); } else if (auto inoutExpr = dyn_cast<InOutExpr>(diagExpr)) {
diagExpr = argTuple->getElement(0); Type type = getConstraintSystem().getType(inoutExpr);
} else { for (auto &restriction : getSolution().ConstraintRestrictions) {
auto lastPathElement = getLocator()->getPath().back(); if (restriction.second == ConversionRestrictionKind::ArrayToPointer &&
assert(lastPathElement.getKind() == restriction.first.first->isEqual(type)) {
ConstraintLocator::PathElementKind::ApplyArgToParam); PointerTypeKind pointerKind;
if (restriction.first.second->getAnyPointerElementType(pointerKind) &&
(pointerKind == PTK_UnsafePointer ||
pointerKind == PTK_UnsafeRawPointer)) {
// If we're converting to an UnsafePointer, then the programmer
// specified an & unnecessarily. Produce a fixit hint to remove it.
emitDiagnostic(inoutExpr->getLoc(),
diag::extra_address_of_unsafepointer,
restriction.first.second)
.highlight(inoutExpr->getSourceRange())
.fixItRemove(inoutExpr->getStartLoc());
return true;
}
}
}
subElementDiagID = diag::cannot_pass_rvalue_inout_subelement; subElementDiagID = diag::cannot_pass_rvalue_inout_subelement;
rvalueDiagID = diag::cannot_pass_rvalue_inout; rvalueDiagID = diag::cannot_pass_rvalue_inout;
if (auto argTuple = dyn_cast<TupleExpr>(argExpr)) loc = diagExpr->getLoc();
diagExpr = argTuple->getElement(lastPathElement.getValue()); diagExpr = inoutExpr->getSubExpr();
else if (auto parens = dyn_cast<ParenExpr>(argExpr)) } else {
diagExpr = parens->getSubExpr(); return false;
} }
diagnoseSubElementFailure(diagExpr, loc, getConstraintSystem(), diagnoseSubElementFailure(diagExpr, loc, getConstraintSystem(),

View File

@@ -81,6 +81,8 @@ protected:
DeclContext *getDC() const { return getConstraintSystem().DC; } DeclContext *getDC() const { return getConstraintSystem().DC; }
const Solution &getSolution() const { return solution; }
Optional<SelectedOverload> Optional<SelectedOverload>
getOverloadChoiceIfAvailable(ConstraintLocator *locator) const { getOverloadChoiceIfAvailable(ConstraintLocator *locator) const {
return solution.getOverloadChoiceIfAvailable(locator); return solution.getOverloadChoiceIfAvailable(locator);

View File

@@ -2409,7 +2409,7 @@ namespace {
auto result = InOutType::get(lvalue); auto result = InOutType::get(lvalue);
CS.addConstraint(ConstraintKind::Conversion, CS.addConstraint(ConstraintKind::Conversion,
CS.getType(expr->getSubExpr()), bound, CS.getType(expr->getSubExpr()), bound,
CS.getConstraintLocator(expr->getSubExpr())); CS.getConstraintLocator(expr));
return result; return result;
} }

View File

@@ -2450,6 +2450,11 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
TreatRValueAsLValue::create(*this, getConstraintLocator(locator))); TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
} }
} }
if (type2->is<LValueType>() && !isTypeVarOrMember1) {
conversionsOrFixes.push_back(
TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
}
} }
if (conversionsOrFixes.empty()) { if (conversionsOrFixes.empty()) {
@@ -4659,11 +4664,11 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
addContextualScore(); addContextualScore();
// Unwrap an inout type. // Unwrap an inout type.
auto obj1 = type1->getInOutObjectType(); auto obj1 = type1->getInOutObjectType();
obj1 = getFixedTypeRecursive(obj1, false, false); obj1 = getFixedTypeRecursive(obj1, false, false);
auto t2 = type2->getDesugaredType(); auto t2 = type2->getDesugaredType();
auto baseType1 = getFixedTypeRecursive(*isArrayType(obj1), false, false); auto baseType1 = getFixedTypeRecursive(*isArrayType(obj1), false, false);
auto baseType2 = getBaseTypeForPointer(*this, t2); auto baseType2 = getBaseTypeForPointer(*this, t2);