Skip to content

Commit 5002612

Browse files
committed
NFC: refactor checkConsumeExpr
I want to expose the syntactic checking to other parts of Sema, so that we can determine whether an expression is a candidate for having a `consume` added to it. (cherry picked from commit 1f83017)
1 parent 23bce84 commit 5002612

File tree

2 files changed

+110
-81
lines changed

2 files changed

+110
-81
lines changed

lib/Sema/MiscDiagnostics.cpp

Lines changed: 87 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -422,87 +422,11 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
422422
}
423423

424424
void checkConsumeExpr(ConsumeExpr *consumeExpr) {
425-
auto *subExpr = consumeExpr->getSubExpr();
426-
bool noncopyable =
427-
subExpr->getType()->getCanonicalType()->isNoncopyable();
428-
429-
bool partial = false;
430-
Expr *current = subExpr;
431-
while (current) {
432-
if (auto *dre = dyn_cast<DeclRefExpr>(current)) {
433-
if (partial & !noncopyable) {
434-
Ctx.Diags.diagnose(consumeExpr->getLoc(),
435-
diag::consume_expression_partial_copyable);
436-
return;
437-
}
438-
// The chain of member_ref_exprs and load_exprs terminates at a
439-
// declref_expr. This is legal.
440-
return;
441-
}
442-
// Look through loads.
443-
if (auto *le = dyn_cast<LoadExpr>(current)) {
444-
current = le->getSubExpr();
445-
continue;
446-
}
447-
auto *mre = dyn_cast<MemberRefExpr>(current);
448-
if (mre) {
449-
auto *vd = dyn_cast<VarDecl>(mre->getMember().getDecl());
450-
if (!vd) {
451-
Ctx.Diags.diagnose(consumeExpr->getLoc(),
452-
diag::consume_expression_non_storage);
453-
return;
454-
}
455-
partial = true;
456-
AccessStrategy strategy = vd->getAccessStrategy(
457-
mre->getAccessSemantics(), AccessKind::Read,
458-
DC->getParentModule(), ResilienceExpansion::Minimal);
459-
if (strategy.getKind() != AccessStrategy::Storage) {
460-
if (noncopyable) {
461-
Ctx.Diags.diagnose(consumeExpr->getLoc(),
462-
diag::consume_expression_non_storage);
463-
Ctx.Diags.diagnose(
464-
mre->getLoc(),
465-
diag::note_consume_expression_non_storage_property);
466-
} else {
467-
Ctx.Diags.diagnose(consumeExpr->getLoc(),
468-
diag::consume_expression_partial_copyable);
469-
}
470-
return;
471-
}
472-
current = mre->getBase();
473-
continue;
474-
}
475-
auto *ce = dyn_cast<CallExpr>(current);
476-
if (ce) {
477-
if (noncopyable) {
478-
Ctx.Diags.diagnose(consumeExpr->getLoc(),
479-
diag::consume_expression_non_storage);
480-
Ctx.Diags.diagnose(ce->getLoc(),
481-
diag::note_consume_expression_non_storage_call);
482-
} else {
483-
Ctx.Diags.diagnose(consumeExpr->getLoc(),
484-
diag::consume_expression_partial_copyable);
485-
}
486-
return;
487-
}
488-
auto *se = dyn_cast<SubscriptExpr>(current);
489-
if (se) {
490-
if (noncopyable) {
491-
Ctx.Diags.diagnose(consumeExpr->getLoc(),
492-
diag::consume_expression_non_storage);
493-
Ctx.Diags.diagnose(
494-
se->getLoc(),
495-
diag::note_consume_expression_non_storage_subscript);
496-
} else {
497-
Ctx.Diags.diagnose(consumeExpr->getLoc(),
498-
diag::consume_expression_partial_copyable);
499-
}
500-
return;
501-
}
502-
Ctx.Diags.diagnose(consumeExpr->getLoc(),
503-
diag::consume_expression_not_passed_lvalue);
504-
return;
505-
}
425+
auto diags = findSyntacticErrorForConsume(DC->getParentModule(),
426+
consumeExpr->getLoc(),
427+
consumeExpr->getSubExpr());
428+
for (auto &diag : diags)
429+
diag.emit(Ctx);
506430
}
507431

508432
void checkCopyExpr(CopyExpr *copyExpr) {
@@ -1521,6 +1445,82 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
15211445
}
15221446
}
15231447

1448+
DeferredDiags swift::findSyntacticErrorForConsume(
1449+
ModuleDecl *module, SourceLoc loc, Expr *subExpr) {
1450+
assert(!isa<ConsumeExpr>(subExpr) && "operates on the sub-expr of a consume");
1451+
1452+
DeferredDiags result;
1453+
const bool noncopyable =
1454+
subExpr->getType()->getCanonicalType()->isNoncopyable();
1455+
1456+
bool partial = false;
1457+
Expr *current = subExpr;
1458+
while (current) {
1459+
if (auto *dre = dyn_cast<DeclRefExpr>(current)) {
1460+
if (partial & !noncopyable)
1461+
result.emplace_back(loc, diag::consume_expression_partial_copyable);
1462+
1463+
// The chain of member_ref_exprs and load_exprs terminates at a
1464+
// declref_expr. This is legal.
1465+
break;
1466+
}
1467+
// Look through loads.
1468+
if (auto *le = dyn_cast<LoadExpr>(current)) {
1469+
current = le->getSubExpr();
1470+
continue;
1471+
}
1472+
auto *mre = dyn_cast<MemberRefExpr>(current);
1473+
if (mre) {
1474+
auto *vd = dyn_cast<VarDecl>(mre->getMember().getDecl());
1475+
if (!vd) {
1476+
result.emplace_back(loc, diag::consume_expression_non_storage);
1477+
break;
1478+
}
1479+
partial = true;
1480+
AccessStrategy strategy = vd->getAccessStrategy(
1481+
mre->getAccessSemantics(), AccessKind::Read,
1482+
module, ResilienceExpansion::Minimal);
1483+
if (strategy.getKind() != AccessStrategy::Storage) {
1484+
if (noncopyable) {
1485+
result.emplace_back(loc, diag::consume_expression_non_storage);
1486+
result.emplace_back(mre->getLoc(),
1487+
diag::note_consume_expression_non_storage_property);
1488+
break;
1489+
}
1490+
result.emplace_back(loc, diag::consume_expression_partial_copyable);
1491+
break;
1492+
}
1493+
current = mre->getBase();
1494+
continue;
1495+
}
1496+
auto *ce = dyn_cast<CallExpr>(current);
1497+
if (ce) {
1498+
if (noncopyable) {
1499+
result.emplace_back(loc, diag::consume_expression_non_storage);
1500+
result.emplace_back(ce->getLoc(),
1501+
diag::note_consume_expression_non_storage_call);
1502+
break;
1503+
}
1504+
result.emplace_back(loc, diag::consume_expression_partial_copyable);
1505+
break;
1506+
}
1507+
auto *se = dyn_cast<SubscriptExpr>(current);
1508+
if (se) {
1509+
if (noncopyable) {
1510+
result.emplace_back(loc, diag::consume_expression_non_storage);
1511+
result.emplace_back(se->getLoc(),
1512+
diag::note_consume_expression_non_storage_subscript);
1513+
break;
1514+
}
1515+
result.emplace_back(loc, diag::consume_expression_partial_copyable);
1516+
break;
1517+
}
1518+
result.emplace_back(loc, diag::consume_expression_not_passed_lvalue);
1519+
break;
1520+
}
1521+
return result;
1522+
}
1523+
15241524

15251525
/// Diagnose recursive use of properties within their own accessors
15261526
static void diagRecursivePropertyAccess(const Expr *E, const DeclContext *DC) {
@@ -6722,3 +6722,9 @@ bool swift::diagnoseUnhandledThrowsInAsyncContext(DeclContext *dc,
67226722

67236723
return false;
67246724
}
6725+
6726+
void DeferredDiag::emit(swift::ASTContext &ctx) {
6727+
assert(loc && "no loc... already emitted?");
6728+
ctx.Diags.diagnose(loc, diag);
6729+
loc = SourceLoc();
6730+
}

lib/Sema/MiscDiagnostics.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,29 @@ namespace swift {
161161
static bool shouldWalkIntoDeclInClosureContext(Decl *D);
162162
};
163163

164+
// A simple, deferred diagnostic container.
165+
struct DeferredDiag {
166+
SourceLoc loc;
167+
ZeroArgDiagnostic diag;
168+
DeferredDiag(SourceLoc loc, ZeroArgDiagnostic diag)
169+
: loc(loc), diag(diag) {}
170+
171+
// Emits this diagnostic.
172+
void emit(ASTContext &ctx);
173+
};
174+
175+
using DeferredDiags = SmallVector<DeferredDiag, 2>;
176+
177+
/// Search for syntactic errors in the given sub-expression of a ConsumeExpr,
178+
/// collecting them without actually emitting them.
179+
///
180+
/// \param loc corresponds to the location of the 'consume' for which
181+
/// diagnostics should be collected, if any.
182+
///
183+
/// \returns an empty collection if there are no errors.
184+
DeferredDiags findSyntacticErrorForConsume(ModuleDecl *module,
185+
SourceLoc loc,
186+
Expr *subExpr);
164187
} // namespace swift
165188

166189
#endif // SWIFT_SEMA_MISC_DIAGNOSTICS_H

0 commit comments

Comments
 (0)