Skip to content

Commit b9cf4fa

Browse files
committed
[Diagnostics] Add a diagnostic for invalid conversion of inout argument/parameter
Since there is no subtyping allowed in `inout` positions, let's produce a tailored error message and a note about that.
1 parent 844feda commit b9cf4fa

File tree

5 files changed

+104
-82
lines changed

5 files changed

+104
-82
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 77 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,17 +1567,7 @@ bool AssignmentFailure::diagnoseAsError() {
15671567
Type neededType = contextualType->getInOutObjectType();
15681568
Type actualType = getType(immInfo.first)->getInOutObjectType();
15691569
if (!neededType->isEqual(actualType)) {
1570-
if (DeclDiagnostic.ID == diag::cannot_pass_rvalue_inout_subelement.ID) {
1571-
// We have a special diagnostic with tailored wording for this
1572-
// common case.
1573-
emitDiagnostic(Loc, diag::cannot_pass_rvalue_inout_converted,
1574-
actualType, neededType)
1575-
.highlight(immInfo.first->getSourceRange());
1576-
1577-
if (auto inoutExpr = dyn_cast<InOutExpr>(immInfo.first))
1578-
fixItChangeInoutArgType(inoutExpr->getSubExpr(), actualType,
1579-
neededType);
1580-
} else {
1570+
if (DeclDiagnostic.ID != diag::cannot_pass_rvalue_inout_subelement.ID) {
15811571
emitDiagnostic(Loc, DeclDiagnostic,
15821572
"implicit conversion from '" + actualType->getString() +
15831573
"' to '" + neededType->getString() +
@@ -1601,68 +1591,6 @@ bool AssignmentFailure::diagnoseAsError() {
16011591
return true;
16021592
}
16031593

1604-
void AssignmentFailure::fixItChangeInoutArgType(const Expr *arg,
1605-
Type actualType,
1606-
Type neededType) const {
1607-
auto *DC = getDC();
1608-
auto *DRE = dyn_cast<DeclRefExpr>(arg);
1609-
if (!DRE)
1610-
return;
1611-
1612-
auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl());
1613-
if (!VD)
1614-
return;
1615-
1616-
// Don't emit for non-local variables.
1617-
// (But in script-mode files, we consider module-scoped
1618-
// variables in the same file to be local variables.)
1619-
auto VDC = VD->getDeclContext();
1620-
bool isLocalVar = VDC->isLocalContext();
1621-
if (!isLocalVar && VDC->isModuleScopeContext()) {
1622-
auto argFile = DC->getParentSourceFile();
1623-
auto varFile = VDC->getParentSourceFile();
1624-
isLocalVar = (argFile == varFile && argFile->isScriptMode());
1625-
}
1626-
if (!isLocalVar)
1627-
return;
1628-
1629-
SmallString<32> scratch;
1630-
SourceLoc endLoc; // Filled in if we decide to diagnose this
1631-
SourceLoc startLoc; // Left invalid if we're inserting
1632-
1633-
auto isSimpleTypelessPattern = [](Pattern *P) -> bool {
1634-
if (auto VP = dyn_cast_or_null<VarPattern>(P))
1635-
P = VP->getSubPattern();
1636-
return P && isa<NamedPattern>(P);
1637-
};
1638-
1639-
auto typeRange = VD->getTypeSourceRangeForDiagnostics();
1640-
if (typeRange.isValid()) {
1641-
startLoc = typeRange.Start;
1642-
endLoc = typeRange.End;
1643-
} else if (isSimpleTypelessPattern(VD->getParentPattern())) {
1644-
endLoc = VD->getNameLoc();
1645-
scratch += ": ";
1646-
}
1647-
1648-
if (endLoc.isInvalid())
1649-
return;
1650-
1651-
scratch += neededType.getString();
1652-
1653-
// Adjust into the location where we actually want to insert
1654-
endLoc = Lexer::getLocForEndOfToken(getASTContext().SourceMgr, endLoc);
1655-
1656-
// Since we already adjusted endLoc, this will turn an insertion
1657-
// into a zero-character replacement.
1658-
if (!startLoc.isValid())
1659-
startLoc = endLoc;
1660-
1661-
emitDiagnostic(VD->getLoc(), diag::inout_change_var_type_if_possible,
1662-
actualType, neededType)
1663-
.fixItReplaceChars(startLoc, endLoc, scratch);
1664-
}
1665-
16661594
std::pair<Expr *, Optional<OverloadChoice>>
16671595
AssignmentFailure::resolveImmutableBase(Expr *expr) const {
16681596
auto &cs = getConstraintSystem();
@@ -4348,3 +4276,79 @@ bool ThrowingFunctionConversionFailure::diagnoseAsError() {
43484276
getFromType(), getToType());
43494277
return true;
43504278
}
4279+
4280+
bool InOutConversionFailure::diagnoseAsError() {
4281+
auto *anchor = getAnchor();
4282+
emitDiagnostic(anchor->getLoc(), diag::cannot_pass_rvalue_inout_converted,
4283+
getFromType(), getToType());
4284+
fixItChangeArgumentType();
4285+
return true;
4286+
}
4287+
4288+
void InOutConversionFailure::fixItChangeArgumentType() const {
4289+
auto *argExpr = getAnchor();
4290+
auto *DC = getDC();
4291+
4292+
if (auto *IOE = dyn_cast<InOutExpr>(argExpr))
4293+
argExpr = IOE->getSubExpr();
4294+
4295+
auto *DRE = dyn_cast<DeclRefExpr>(argExpr);
4296+
if (!DRE)
4297+
return;
4298+
4299+
auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl());
4300+
if (!VD)
4301+
return;
4302+
4303+
// Don't emit for non-local variables.
4304+
// (But in script-mode files, we consider module-scoped
4305+
// variables in the same file to be local variables.)
4306+
auto VDC = VD->getDeclContext();
4307+
bool isLocalVar = VDC->isLocalContext();
4308+
if (!isLocalVar && VDC->isModuleScopeContext()) {
4309+
auto argFile = DC->getParentSourceFile();
4310+
auto varFile = VDC->getParentSourceFile();
4311+
isLocalVar = (argFile == varFile && argFile->isScriptMode());
4312+
}
4313+
if (!isLocalVar)
4314+
return;
4315+
4316+
auto actualType = getFromType();
4317+
auto neededType = getToType();
4318+
4319+
SmallString<32> scratch;
4320+
SourceLoc endLoc; // Filled in if we decide to diagnose this
4321+
SourceLoc startLoc; // Left invalid if we're inserting
4322+
4323+
auto isSimpleTypelessPattern = [](Pattern *P) -> bool {
4324+
if (auto VP = dyn_cast_or_null<VarPattern>(P))
4325+
P = VP->getSubPattern();
4326+
return P && isa<NamedPattern>(P);
4327+
};
4328+
4329+
auto typeRange = VD->getTypeSourceRangeForDiagnostics();
4330+
if (typeRange.isValid()) {
4331+
startLoc = typeRange.Start;
4332+
endLoc = typeRange.End;
4333+
} else if (isSimpleTypelessPattern(VD->getParentPattern())) {
4334+
endLoc = VD->getNameLoc();
4335+
scratch += ": ";
4336+
}
4337+
4338+
if (endLoc.isInvalid())
4339+
return;
4340+
4341+
scratch += neededType.getString();
4342+
4343+
// Adjust into the location where we actually want to insert
4344+
endLoc = Lexer::getLocForEndOfToken(getASTContext().SourceMgr, endLoc);
4345+
4346+
// Since we already adjusted endLoc, this will turn an insertion
4347+
// into a zero-character replacement.
4348+
if (!startLoc.isValid())
4349+
startLoc = endLoc;
4350+
4351+
emitDiagnostic(VD->getLoc(), diag::inout_change_var_type_if_possible,
4352+
actualType, neededType)
4353+
.fixItReplaceChars(startLoc, endLoc, scratch);
4354+
}

lib/Sema/CSDiagnostics.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -574,9 +574,6 @@ class AssignmentFailure final : public FailureDiagnostic {
574574
bool diagnoseAsError() override;
575575

576576
private:
577-
void fixItChangeInoutArgType(const Expr *arg, Type actualType,
578-
Type neededType) const;
579-
580577
/// Given an expression that has a non-lvalue type, dig into it until
581578
/// we find the part of the expression that prevents the entire subexpression
582579
/// from being mutable. For example, in a sequence like "x.v.v = 42" we want
@@ -1509,6 +1506,24 @@ class MissingContextualConformanceFailure final : public ContextualFailure {
15091506
bool diagnoseAsError() override;
15101507
};
15111508

1509+
/// Diagnose a conversion mismatch between object types of `inout`
1510+
/// argument/parameter e.g. `'inout S' argument conv 'inout P'`.
1511+
///
1512+
/// Even if `S` conforms to `P` there is no subtyping rule for
1513+
/// argument type of `inout` parameter, they have to be equal.
1514+
class InOutConversionFailure final : public ContextualFailure {
1515+
public:
1516+
InOutConversionFailure(Expr *root, ConstraintSystem &cs, Type argType,
1517+
Type paramType, ConstraintLocator *locator)
1518+
: ContextualFailure(root, cs, argType, paramType, locator) {}
1519+
1520+
bool diagnoseAsError() override;
1521+
1522+
protected:
1523+
/// Suggest to change a type of the argument if possible.
1524+
void fixItChangeArgumentType() const;
1525+
};
1526+
15121527
/// Diagnose generic argument omission e.g.
15131528
///
15141529
/// ```swift

lib/Sema/CSFix.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -775,7 +775,10 @@ IgnoreContextualType *IgnoreContextualType::create(ConstraintSystem &cs,
775775
}
776776

777777
bool AllowInOutConversion::diagnose(Expr *root, bool asNote) const {
778-
return false;
778+
auto &cs = getConstraintSystem();
779+
InOutConversionFailure failure(root, cs, getFromType(), getToType(),
780+
getLocator());
781+
return failure.diagnose(asNote);
779782
}
780783

781784
AllowInOutConversion *AllowInOutConversion::create(ConstraintSystem &cs,

test/Constraints/lvalues.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ z[0] = 0.0 // expected-error{{cannot assign through subscript: subscript is get-
9090
f2(&z[0]) // expected-error{{cannot pass immutable value as inout argument: subscript is get-only}}
9191
f1(&z[0]) // expected-error{{cannot pass immutable value as inout argument: subscript is get-only}}
9292
z[0] += 0.0 // expected-error{{left side of mutating operator isn't mutable: subscript is get-only}}
93-
+++z[0] // expected-error{{cannot pass immutable value as inout argument: subscript is get-only}}
93+
+++z[0] // expected-error{{cannot convert value of type 'Double' to expected argument type 'X'}}
94+
+++z[(i: 0, j: 0)] // expected-error{{cannot pass immutable value to mutating operator: subscript is get-only}}
9495

9596
// settable property of an rvalue value type is non-settable:
9697
fz().settable_x = x // expected-error{{cannot assign to property: 'fz' returns immutable value}}
@@ -111,7 +112,7 @@ z.non_settable_x.property = 1.0 // expected-error{{cannot assign to property: 'n
111112
f2(&z.non_settable_x.property) // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
112113
f1(&z.non_settable_x.property) // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
113114
z.non_settable_x.property += 1.0 // expected-error{{left side of mutating operator isn't mutable: 'non_settable_x' is a get-only property}}
114-
+++z.non_settable_x.property // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
115+
+++z.non_settable_x.property // expected-error{{cannot convert value of type 'Double' to expected argument type 'X'}}
115116

116117
// settable property of a non-settable reference type IS SETTABLE:
117118
z.non_settable_reftype.property = 1.0

test/Constraints/operator.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,8 @@ func rdar37290898(_ arr: inout [P_37290898], _ element: S_37290898?) {
205205
// SR-8221
206206
infix operator ??=
207207
func ??= <T>(lhs: inout T?, rhs: T?) {}
208-
var c: Int = 0
209-
c ??= 5 // expected-error{{binary operator '??=' cannot be applied to two 'Int' operands}}
210-
// expected-note@-1{{expected an argument list of type '(inout T?, T?)'}}
208+
var c: Int = 0 // expected-note {{change variable type to 'Int?' if it doesn't need to be declared as 'Int'}}
209+
c ??= 5 // expected-error{{inout argument could be set to a value with a type other than 'Int'; use a value declared as type 'Int?' instead}}
211210

212211
func rdar46459603() {
213212
enum E {

0 commit comments

Comments
 (0)