Skip to content

Commit 02e0770

Browse files
authored
Merge pull request #73916 from kavon/ncgenerics-implicit-wrapping-127450418
ConstraintSystem: clarify consuming conversions
2 parents 2175abd + 77e8fb0 commit 02e0770

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
@@ -7830,6 +7830,10 @@ ERROR(noncopyable_objc_enum, none,
78307830
"noncopyable enums cannot be marked '@objc'", ())
78317831
ERROR(moveOnly_not_allowed_here,none,
78327832
"'@_moveOnly' attribute is only valid on structs or enums", ())
7833+
ERROR(consume_expression_needed_for_cast,none,
7834+
"implicit conversion to %0 is consuming", (Type))
7835+
NOTE(add_consume_to_silence,none,
7836+
"add 'consume' to make consumption explicit", ())
78337837
ERROR(consume_expression_not_passed_lvalue,none,
78347838
"'consume' can only be applied to a local binding ('let', 'var', or parameter)", ())
78357839
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
@@ -6582,6 +6582,13 @@ TypeVariableType *TypeVariableType::getNew(const ASTContext &C, unsigned ID,
65826582
/// underlying forced downcast expression.
65836583
ForcedCheckedCastExpr *findForcedDowncast(ASTContext &ctx, Expr *expr);
65846584

6585+
/// Assuming the expression appears in a consuming context,
6586+
/// if it does not already have an explicit `consume`,
6587+
/// can I add `consume` around this expression?
6588+
///
6589+
/// \param module represents the module in which the expr appears
6590+
bool canAddExplicitConsume(ModuleDecl *module, Expr *expr);
6591+
65856592
// Count the number of overload sets present
65866593
// in the expression and all of the children.
65876594
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
@@ -3241,10 +3280,12 @@ namespace {
32413280
}
32423281

32433282
private:
3244-
/// A list of "suspicious" optional injections that come from
3245-
/// forced downcasts.
3283+
/// A list of "suspicious" optional injections.
32463284
SmallVector<InjectIntoOptionalExpr *, 4> SuspiciousOptionalInjections;
32473285

3286+
/// A list of implicit coercions of noncopyable types.
3287+
SmallVector<Expr *, 4> ConsumingCoercions;
3288+
32483289
/// Create a member reference to the given constructor.
32493290
Expr *applyCtorRefExpr(Expr *expr, Expr *base, SourceLoc dotLoc,
32503291
DeclNameLoc nameLoc, bool implicit,
@@ -4466,9 +4507,9 @@ namespace {
44664507
if (choice == 0) {
44674508
// Convert the subexpression.
44684509
Expr *sub = expr->getSubExpr();
4469-
4470-
sub = solution.coerceToType(sub, expr->getCastType(),
4471-
cs.getConstraintLocator(sub));
4510+
auto subLoc =
4511+
cs.getConstraintLocator(sub, ConstraintLocator::CoercionOperand);
4512+
sub = solution.coerceToType(sub, expr->getCastType(), subLoc);
44724513
if (!sub)
44734514
return nullptr;
44744515

@@ -5516,41 +5557,69 @@ namespace {
55165557
assert(OpenedExistentials.empty());
55175558

55185559
auto &ctx = cs.getASTContext();
5560+
auto *module = dc->getParentModule();
55195561

55205562
// Look at all of the suspicious optional injections
55215563
for (auto injection : SuspiciousOptionalInjections) {
5522-
auto *cast = findForcedDowncast(ctx, injection->getSubExpr());
5523-
if (!cast)
5524-
continue;
5525-
5526-
if (isa<ParenExpr>(injection->getSubExpr()))
5527-
continue;
5564+
if (auto *cast = findForcedDowncast(ctx, injection->getSubExpr())) {
5565+
if (!isa<ParenExpr>(injection->getSubExpr())) {
5566+
ctx.Diags.diagnose(
5567+
injection->getLoc(), diag::inject_forced_downcast,
5568+
cs.getType(injection->getSubExpr())->getRValueType());
5569+
auto exclaimLoc = cast->getExclaimLoc();
5570+
ctx.Diags
5571+
.diagnose(exclaimLoc, diag::forced_to_conditional_downcast,
5572+
cs.getType(injection)->getOptionalObjectType())
5573+
.fixItReplace(exclaimLoc, "?");
5574+
ctx.Diags
5575+
.diagnose(cast->getStartLoc(),
5576+
diag::silence_inject_forced_downcast)
5577+
.fixItInsert(cast->getStartLoc(), "(")
5578+
.fixItInsertAfter(cast->getEndLoc(), ")");
5579+
}
5580+
}
5581+
}
55285582

5529-
ctx.Diags.diagnose(
5530-
injection->getLoc(), diag::inject_forced_downcast,
5531-
cs.getType(injection->getSubExpr())->getRValueType());
5532-
auto exclaimLoc = cast->getExclaimLoc();
5583+
// Diagnose the implicit coercions of noncopyable values that happen in
5584+
// a context where it isn't "obviously" consuming already.
5585+
for (auto *coercion : ConsumingCoercions) {
5586+
assert(coercion->isImplicit());
55335587
ctx.Diags
5534-
.diagnose(exclaimLoc, diag::forced_to_conditional_downcast,
5535-
cs.getType(injection)->getOptionalObjectType())
5536-
.fixItReplace(exclaimLoc, "?");
5588+
.diagnose(coercion->getLoc(),
5589+
diag::consume_expression_needed_for_cast,
5590+
cs.getType(coercion));
55375591
ctx.Diags
5538-
.diagnose(cast->getStartLoc(), diag::silence_inject_forced_downcast)
5539-
.fixItInsert(cast->getStartLoc(), "(")
5540-
.fixItInsertAfter(cast->getEndLoc(), ")");
5592+
.diagnose(coercion->getLoc(),
5593+
diag::add_consume_to_silence)
5594+
.fixItInsert(coercion->getStartLoc(), "consume ");
55415595
}
55425596
}
55435597

55445598
/// Diagnose an optional injection that is probably not what the
5545-
/// user wanted, because it comes from a forced downcast.
5546-
void diagnoseOptionalInjection(InjectIntoOptionalExpr *injection) {
5599+
/// user wanted, because it comes from a forced downcast, or from an
5600+
/// implicitly consumed noncopyable type.
5601+
void diagnoseOptionalInjection(InjectIntoOptionalExpr *injection,
5602+
ConstraintLocatorBuilder locator) {
55475603
// Check whether we have a forced downcast.
5548-
auto *cast =
5549-
findForcedDowncast(cs.getASTContext(), injection->getSubExpr());
5550-
if (!cast)
5551-
return;
5552-
5553-
SuspiciousOptionalInjections.push_back(injection);
5604+
if (findForcedDowncast(cs.getASTContext(), injection->getSubExpr()))
5605+
SuspiciousOptionalInjections.push_back(injection);
5606+
5607+
/// Check if it needs an explicit consume, due to this being a cast.
5608+
auto *module = dc->getParentModule();
5609+
auto origType = cs.getType(injection->getSubExpr());
5610+
if (willHaveConfusingConsumption(origType, locator, cs) &&
5611+
canAddExplicitConsume(module, injection->getSubExpr()))
5612+
ConsumingCoercions.push_back(injection);
5613+
}
5614+
5615+
void diagnoseExistentialErasureOf(Expr *fromExpr, Expr *toExpr,
5616+
ConstraintLocatorBuilder locator) {
5617+
auto *module = dc->getParentModule();
5618+
auto fromType = cs.getType(fromExpr);
5619+
if (willHaveConfusingConsumption(fromType, locator, cs) &&
5620+
canAddExplicitConsume(module, fromExpr)) {
5621+
ConsumingCoercions.push_back(toExpr);
5622+
}
55545623
}
55555624
};
55565625
} // end anonymous namespace
@@ -5821,7 +5890,7 @@ Expr *ExprRewriter::coerceOptionalToOptional(Expr *expr, Type toType,
58215890
while (diff--) {
58225891
Type type = toOptionals[diff];
58235892
expr = cs.cacheType(new (ctx) InjectIntoOptionalExpr(expr, type));
5824-
diagnoseOptionalInjection(cast<InjectIntoOptionalExpr>(expr));
5893+
diagnoseOptionalInjection(cast<InjectIntoOptionalExpr>(expr), locator);
58255894
}
58265895

58275896
return expr;
@@ -6950,8 +7019,11 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
69507019
return coerceSuperclass(expr, toType);
69517020

69527021
case ConversionRestrictionKind::Existential:
6953-
case ConversionRestrictionKind::MetatypeToExistentialMetatype:
6954-
return coerceExistential(expr, toType, locator);
7022+
case ConversionRestrictionKind::MetatypeToExistentialMetatype: {
7023+
auto coerced = coerceExistential(expr, toType, locator);
7024+
diagnoseExistentialErasureOf(expr, coerced, locator);
7025+
return coerced;
7026+
}
69557027

69567028
case ConversionRestrictionKind::ClassMetatypeToAnyObject: {
69577029
assert(ctx.LangOpts.EnableObjCInterop &&
@@ -6980,7 +7052,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
69807052

69817053
auto *result =
69827054
cs.cacheType(new (ctx) InjectIntoOptionalExpr(expr, toType));
6983-
diagnoseOptionalInjection(result);
7055+
diagnoseOptionalInjection(result, locator);
69847056
return result;
69857057
}
69867058

@@ -7674,7 +7746,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
76747746
if (!expr) return nullptr;
76757747

76767748
auto *result = cs.cacheType(new (ctx) InjectIntoOptionalExpr(expr, toType));
7677-
diagnoseOptionalInjection(result);
7749+
diagnoseOptionalInjection(result, locator);
76787750
return result;
76797751
}
76807752

0 commit comments

Comments
 (0)