Skip to content

Commit 42486cf

Browse files
authored
Merge pull request #74169 from kavon/6.0-ncgenerics-implicit-wrapping-127450418
[6.0🍒] ConstraintSystem: clarify consuming conversions
2 parents 169b75f + 1fa166b commit 42486cf

File tree

7 files changed

+368
-115
lines changed

7 files changed

+368
-115
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7774,6 +7774,10 @@ ERROR(noncopyable_objc_enum, none,
77747774
"noncopyable enums cannot be marked '@objc'", ())
77757775
ERROR(moveOnly_not_allowed_here,none,
77767776
"'@_moveOnly' attribute is only valid on structs or enums", ())
7777+
ERROR(consume_expression_needed_for_cast,none,
7778+
"implicit conversion to %0 is consuming", (Type))
7779+
NOTE(add_consume_to_silence,none,
7780+
"add 'consume' to make consumption explicit", ())
77777781
ERROR(consume_expression_not_passed_lvalue,none,
77787782
"'consume' can only be applied to a local binding ('let', 'var', or parameter)", ())
77797783
ERROR(consume_expression_partial_copyable,none,

include/swift/Sema/ConstraintSystem.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6574,6 +6574,13 @@ TypeVariableType *TypeVariableType::getNew(const ASTContext &C, unsigned ID,
65746574
/// underlying forced downcast expression.
65756575
ForcedCheckedCastExpr *findForcedDowncast(ASTContext &ctx, Expr *expr);
65766576

6577+
/// Assuming the expression appears in a consuming context,
6578+
/// if it does not already have an explicit `consume`,
6579+
/// can I add `consume` around this expression?
6580+
///
6581+
/// \param module represents the module in which the expr appears
6582+
bool canAddExplicitConsume(ModuleDecl *module, Expr *expr);
6583+
65776584
// Count the number of overload sets present
65786585
// in the expression and all of the children.
65796586
class OverloadSetCounter : public ASTWalker {

lib/Sema/CSApply.cpp

Lines changed: 106 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,45 @@ static bool buildObjCKeyPathString(KeyPathExpr *E,
355355
return true;
356356
}
357357

358+
/// Since a cast to an optional will consume a noncopyable type, check to see
359+
/// if injecting the value into an optional here will potentially be confusing.
360+
static bool willHaveConfusingConsumption(Type type,
361+
ConstraintLocatorBuilder locator,
362+
ConstraintSystem &cs) {
363+
assert(type);
364+
if (!type->isNoncopyable())
365+
return false; /// If it's a copyable type, there's no confusion.
366+
367+
auto loc = cs.getConstraintLocator(locator);
368+
if (!loc)
369+
return true;
370+
371+
auto path = loc->getPath();
372+
if (path.empty())
373+
return true;
374+
375+
switch (loc->getPath().back().getKind()) {
376+
case ConstraintLocator::FunctionResult:
377+
case ConstraintLocator::ClosureResult:
378+
case ConstraintLocator::ClosureBody:
379+
case ConstraintLocator::ContextualType:
380+
case ConstraintLocator::CoercionOperand:
381+
return false; // These last-uses won't be confused for borrowing.
382+
383+
case ConstraintLocator::ApplyArgToParam: {
384+
auto argLoc = loc->castLastElementTo<LocatorPathElt::ApplyArgToParam>();
385+
auto paramFlags = argLoc.getParameterFlags();
386+
if (paramFlags.getOwnershipSpecifier() == ParamSpecifier::Consuming)
387+
return false; // Parameter already declares 'consuming'.
388+
389+
return true;
390+
}
391+
392+
default:
393+
return true;
394+
}
395+
}
396+
358397
namespace {
359398

360399
/// Rewrites an expression by applying the solution of a constraint
@@ -3200,10 +3239,12 @@ namespace {
32003239
}
32013240

32023241
private:
3203-
/// A list of "suspicious" optional injections that come from
3204-
/// forced downcasts.
3242+
/// A list of "suspicious" optional injections.
32053243
SmallVector<InjectIntoOptionalExpr *, 4> SuspiciousOptionalInjections;
32063244

3245+
/// A list of implicit coercions of noncopyable types.
3246+
SmallVector<Expr *, 4> ConsumingCoercions;
3247+
32073248
/// Create a member reference to the given constructor.
32083249
Expr *applyCtorRefExpr(Expr *expr, Expr *base, SourceLoc dotLoc,
32093250
DeclNameLoc nameLoc, bool implicit,
@@ -4425,9 +4466,9 @@ namespace {
44254466
if (choice == 0) {
44264467
// Convert the subexpression.
44274468
Expr *sub = expr->getSubExpr();
4428-
4429-
sub = solution.coerceToType(sub, expr->getCastType(),
4430-
cs.getConstraintLocator(sub));
4469+
auto subLoc =
4470+
cs.getConstraintLocator(sub, ConstraintLocator::CoercionOperand);
4471+
sub = solution.coerceToType(sub, expr->getCastType(), subLoc);
44314472
if (!sub)
44324473
return nullptr;
44334474

@@ -5475,41 +5516,69 @@ namespace {
54755516
assert(OpenedExistentials.empty());
54765517

54775518
auto &ctx = cs.getASTContext();
5519+
auto *module = dc->getParentModule();
54785520

54795521
// Look at all of the suspicious optional injections
54805522
for (auto injection : SuspiciousOptionalInjections) {
5481-
auto *cast = findForcedDowncast(ctx, injection->getSubExpr());
5482-
if (!cast)
5483-
continue;
5484-
5485-
if (isa<ParenExpr>(injection->getSubExpr()))
5486-
continue;
5523+
if (auto *cast = findForcedDowncast(ctx, injection->getSubExpr())) {
5524+
if (!isa<ParenExpr>(injection->getSubExpr())) {
5525+
ctx.Diags.diagnose(
5526+
injection->getLoc(), diag::inject_forced_downcast,
5527+
cs.getType(injection->getSubExpr())->getRValueType());
5528+
auto exclaimLoc = cast->getExclaimLoc();
5529+
ctx.Diags
5530+
.diagnose(exclaimLoc, diag::forced_to_conditional_downcast,
5531+
cs.getType(injection)->getOptionalObjectType())
5532+
.fixItReplace(exclaimLoc, "?");
5533+
ctx.Diags
5534+
.diagnose(cast->getStartLoc(),
5535+
diag::silence_inject_forced_downcast)
5536+
.fixItInsert(cast->getStartLoc(), "(")
5537+
.fixItInsertAfter(cast->getEndLoc(), ")");
5538+
}
5539+
}
5540+
}
54875541

5488-
ctx.Diags.diagnose(
5489-
injection->getLoc(), diag::inject_forced_downcast,
5490-
cs.getType(injection->getSubExpr())->getRValueType());
5491-
auto exclaimLoc = cast->getExclaimLoc();
5542+
// Diagnose the implicit coercions of noncopyable values that happen in
5543+
// a context where it isn't "obviously" consuming already.
5544+
for (auto *coercion : ConsumingCoercions) {
5545+
assert(coercion->isImplicit());
54925546
ctx.Diags
5493-
.diagnose(exclaimLoc, diag::forced_to_conditional_downcast,
5494-
cs.getType(injection)->getOptionalObjectType())
5495-
.fixItReplace(exclaimLoc, "?");
5547+
.diagnose(coercion->getLoc(),
5548+
diag::consume_expression_needed_for_cast,
5549+
cs.getType(coercion));
54965550
ctx.Diags
5497-
.diagnose(cast->getStartLoc(), diag::silence_inject_forced_downcast)
5498-
.fixItInsert(cast->getStartLoc(), "(")
5499-
.fixItInsertAfter(cast->getEndLoc(), ")");
5551+
.diagnose(coercion->getLoc(),
5552+
diag::add_consume_to_silence)
5553+
.fixItInsert(coercion->getStartLoc(), "consume ");
55005554
}
55015555
}
55025556

55035557
/// Diagnose an optional injection that is probably not what the
5504-
/// user wanted, because it comes from a forced downcast.
5505-
void diagnoseOptionalInjection(InjectIntoOptionalExpr *injection) {
5558+
/// user wanted, because it comes from a forced downcast, or from an
5559+
/// implicitly consumed noncopyable type.
5560+
void diagnoseOptionalInjection(InjectIntoOptionalExpr *injection,
5561+
ConstraintLocatorBuilder locator) {
55065562
// Check whether we have a forced downcast.
5507-
auto *cast =
5508-
findForcedDowncast(cs.getASTContext(), injection->getSubExpr());
5509-
if (!cast)
5510-
return;
5511-
5512-
SuspiciousOptionalInjections.push_back(injection);
5563+
if (findForcedDowncast(cs.getASTContext(), injection->getSubExpr()))
5564+
SuspiciousOptionalInjections.push_back(injection);
5565+
5566+
/// Check if it needs an explicit consume, due to this being a cast.
5567+
auto *module = dc->getParentModule();
5568+
auto origType = cs.getType(injection->getSubExpr());
5569+
if (willHaveConfusingConsumption(origType, locator, cs) &&
5570+
canAddExplicitConsume(module, injection->getSubExpr()))
5571+
ConsumingCoercions.push_back(injection);
5572+
}
5573+
5574+
void diagnoseExistentialErasureOf(Expr *fromExpr, Expr *toExpr,
5575+
ConstraintLocatorBuilder locator) {
5576+
auto *module = dc->getParentModule();
5577+
auto fromType = cs.getType(fromExpr);
5578+
if (willHaveConfusingConsumption(fromType, locator, cs) &&
5579+
canAddExplicitConsume(module, fromExpr)) {
5580+
ConsumingCoercions.push_back(toExpr);
5581+
}
55135582
}
55145583
};
55155584
} // end anonymous namespace
@@ -5780,7 +5849,7 @@ Expr *ExprRewriter::coerceOptionalToOptional(Expr *expr, Type toType,
57805849
while (diff--) {
57815850
Type type = toOptionals[diff];
57825851
expr = cs.cacheType(new (ctx) InjectIntoOptionalExpr(expr, type));
5783-
diagnoseOptionalInjection(cast<InjectIntoOptionalExpr>(expr));
5852+
diagnoseOptionalInjection(cast<InjectIntoOptionalExpr>(expr), locator);
57845853
}
57855854

57865855
return expr;
@@ -6909,8 +6978,11 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
69096978
return coerceSuperclass(expr, toType);
69106979

69116980
case ConversionRestrictionKind::Existential:
6912-
case ConversionRestrictionKind::MetatypeToExistentialMetatype:
6913-
return coerceExistential(expr, toType, locator);
6981+
case ConversionRestrictionKind::MetatypeToExistentialMetatype: {
6982+
auto coerced = coerceExistential(expr, toType, locator);
6983+
diagnoseExistentialErasureOf(expr, coerced, locator);
6984+
return coerced;
6985+
}
69146986

69156987
case ConversionRestrictionKind::ClassMetatypeToAnyObject: {
69166988
assert(ctx.LangOpts.EnableObjCInterop &&
@@ -6939,7 +7011,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
69397011

69407012
auto *result =
69417013
cs.cacheType(new (ctx) InjectIntoOptionalExpr(expr, toType));
6942-
diagnoseOptionalInjection(result);
7014+
diagnoseOptionalInjection(result, locator);
69437015
return result;
69447016
}
69457017

@@ -7633,7 +7705,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
76337705
if (!expr) return nullptr;
76347706

76357707
auto *result = cs.cacheType(new (ctx) InjectIntoOptionalExpr(expr, toType));
7636-
diagnoseOptionalInjection(result);
7708+
diagnoseOptionalInjection(result, locator);
76377709
return result;
76387710
}
76397711

0 commit comments

Comments
 (0)