mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Requestify FallthroughStmt source and destination lookup
Follow a similar pattern to BreakTargetRequest and ContinueTargetRequest.
This commit is contained in:
@@ -1491,10 +1491,10 @@ BridgedDoCatchStmt BridgedDoCatchStmt_createParsed(
|
|||||||
BridgedNullableTypeRepr cThrownType, BridgedStmt cBody,
|
BridgedNullableTypeRepr cThrownType, BridgedStmt cBody,
|
||||||
BridgedArrayRef cCatches);
|
BridgedArrayRef cCatches);
|
||||||
|
|
||||||
SWIFT_NAME("BridgedFallthroughStmt.createParsed(_:loc:)")
|
SWIFT_NAME("BridgedFallthroughStmt.createParsed(loc:declContext:)")
|
||||||
BridgedFallthroughStmt
|
BridgedFallthroughStmt
|
||||||
BridgedFallthroughStmt_createParsed(BridgedASTContext cContext,
|
BridgedFallthroughStmt_createParsed(BridgedSourceLoc cLoc,
|
||||||
BridgedSourceLoc cLoc);
|
BridgedDeclContext cDC);
|
||||||
|
|
||||||
SWIFT_NAME("BridgedForEachStmt.createParsed(_:labelInfo:forLoc:tryLoc:awaitLoc:"
|
SWIFT_NAME("BridgedForEachStmt.createParsed(_:labelInfo:forLoc:tryLoc:awaitLoc:"
|
||||||
"pattern:inLoc:sequence:whereLoc:whereExpr:body:)")
|
"pattern:inLoc:sequence:whereLoc:whereExpr:body:)")
|
||||||
|
|||||||
@@ -1159,36 +1159,29 @@ public:
|
|||||||
/// FallthroughStmt - The keyword "fallthrough".
|
/// FallthroughStmt - The keyword "fallthrough".
|
||||||
class FallthroughStmt : public Stmt {
|
class FallthroughStmt : public Stmt {
|
||||||
SourceLoc Loc;
|
SourceLoc Loc;
|
||||||
CaseStmt *FallthroughSource;
|
DeclContext *DC;
|
||||||
CaseStmt *FallthroughDest;
|
|
||||||
|
|
||||||
public:
|
FallthroughStmt(SourceLoc Loc, DeclContext *DC,
|
||||||
FallthroughStmt(SourceLoc Loc, std::optional<bool> implicit = std::nullopt)
|
std::optional<bool> implicit = std::nullopt)
|
||||||
: Stmt(StmtKind::Fallthrough, getDefaultImplicitFlag(implicit, Loc)),
|
: Stmt(StmtKind::Fallthrough, getDefaultImplicitFlag(implicit, Loc)),
|
||||||
Loc(Loc), FallthroughSource(nullptr), FallthroughDest(nullptr) {}
|
Loc(Loc), DC(DC) {}
|
||||||
|
public:
|
||||||
|
static FallthroughStmt *createParsed(SourceLoc Loc, DeclContext *DC);
|
||||||
|
|
||||||
SourceLoc getLoc() const { return Loc; }
|
SourceLoc getLoc() const { return Loc; }
|
||||||
|
|
||||||
SourceRange getSourceRange() const { return Loc; }
|
SourceRange getSourceRange() const { return Loc; }
|
||||||
|
|
||||||
|
DeclContext *getDeclContext() const { return DC; }
|
||||||
|
void setDeclContext(DeclContext *newDC) { DC = newDC; }
|
||||||
|
|
||||||
/// Get the CaseStmt block from which the fallthrough transfers control.
|
/// Get the CaseStmt block from which the fallthrough transfers control.
|
||||||
/// Set during Sema. (May stay null if fallthrough is invalid.)
|
/// Returns \c nullptr if the fallthrough is invalid.
|
||||||
CaseStmt *getFallthroughSource() const { return FallthroughSource; }
|
CaseStmt *getFallthroughSource() const;
|
||||||
void setFallthroughSource(CaseStmt *C) {
|
|
||||||
assert(!FallthroughSource && "fallthrough source already set?!");
|
|
||||||
FallthroughSource = C;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the CaseStmt block to which the fallthrough transfers control.
|
/// Get the CaseStmt block to which the fallthrough transfers control.
|
||||||
/// Set during Sema.
|
/// Returns \c nullptr if the fallthrough is invalid.
|
||||||
CaseStmt *getFallthroughDest() const {
|
CaseStmt *getFallthroughDest() const;
|
||||||
assert(FallthroughDest && "fallthrough dest is not set until Sema");
|
|
||||||
return FallthroughDest;
|
|
||||||
}
|
|
||||||
void setFallthroughDest(CaseStmt *C) {
|
|
||||||
assert(!FallthroughDest && "fallthrough dest already set?!");
|
|
||||||
FallthroughDest = C;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool classof(const Stmt *S) {
|
static bool classof(const Stmt *S) {
|
||||||
return S->getKind() == StmtKind::Fallthrough;
|
return S->getKind() == StmtKind::Fallthrough;
|
||||||
|
|||||||
@@ -4162,6 +4162,29 @@ public:
|
|||||||
bool isCached() const { return true; }
|
bool isCached() const { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct FallthroughSourceAndDest {
|
||||||
|
CaseStmt *Source;
|
||||||
|
CaseStmt *Dest;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Lookup the source and destination of a 'fallthrough'.
|
||||||
|
class FallthroughSourceAndDestRequest
|
||||||
|
: public SimpleRequest<FallthroughSourceAndDestRequest,
|
||||||
|
FallthroughSourceAndDest(const FallthroughStmt *),
|
||||||
|
RequestFlags::Cached> {
|
||||||
|
public:
|
||||||
|
using SimpleRequest::SimpleRequest;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend SimpleRequest;
|
||||||
|
|
||||||
|
FallthroughSourceAndDest evaluate(Evaluator &evaluator,
|
||||||
|
const FallthroughStmt *FS) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool isCached() const { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
/// Precheck a ReturnStmt, which involves some initial validation, as well as
|
/// Precheck a ReturnStmt, which involves some initial validation, as well as
|
||||||
/// applying a conversion to a FailStmt if needed.
|
/// applying a conversion to a FailStmt if needed.
|
||||||
class PreCheckReturnStmtRequest
|
class PreCheckReturnStmtRequest
|
||||||
|
|||||||
@@ -482,6 +482,9 @@ SWIFT_REQUEST(TypeChecker, BreakTargetRequest,
|
|||||||
SWIFT_REQUEST(TypeChecker, ContinueTargetRequest,
|
SWIFT_REQUEST(TypeChecker, ContinueTargetRequest,
|
||||||
LabeledStmt *(const ContinueStmt *),
|
LabeledStmt *(const ContinueStmt *),
|
||||||
Cached, NoLocationInfo)
|
Cached, NoLocationInfo)
|
||||||
|
SWIFT_REQUEST(TypeChecker, FallthroughSourceAndDestRequest,
|
||||||
|
FallthroughSourceAndDest(const FallthroughStmt *),
|
||||||
|
Cached, NoLocationInfo)
|
||||||
SWIFT_REQUEST(TypeChecker, PreCheckReturnStmtRequest,
|
SWIFT_REQUEST(TypeChecker, PreCheckReturnStmtRequest,
|
||||||
Stmt *(ReturnStmt *, DeclContext *),
|
Stmt *(ReturnStmt *, DeclContext *),
|
||||||
Cached, NoLocationInfo)
|
Cached, NoLocationInfo)
|
||||||
|
|||||||
@@ -2005,9 +2005,9 @@ BridgedDoCatchStmt BridgedDoCatchStmt_createParsed(
|
|||||||
}
|
}
|
||||||
|
|
||||||
BridgedFallthroughStmt
|
BridgedFallthroughStmt
|
||||||
BridgedFallthroughStmt_createParsed(BridgedASTContext cContext,
|
BridgedFallthroughStmt_createParsed(BridgedSourceLoc cLoc,
|
||||||
BridgedSourceLoc cLoc) {
|
BridgedDeclContext cDC) {
|
||||||
return new (cContext.unbridged()) FallthroughStmt(cLoc.unbridged());
|
return FallthroughStmt::createParsed(cLoc.unbridged(), cDC.unbridged());
|
||||||
}
|
}
|
||||||
|
|
||||||
BridgedForEachStmt BridgedForEachStmt_createParsed(
|
BridgedForEachStmt BridgedForEachStmt_createParsed(
|
||||||
|
|||||||
@@ -989,6 +989,23 @@ LabeledStmt *ContinueStmt::getTarget() const {
|
|||||||
return evaluateOrDefault(eval, ContinueTargetRequest{this}, nullptr);
|
return evaluateOrDefault(eval, ContinueTargetRequest{this}, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FallthroughStmt *FallthroughStmt::createParsed(SourceLoc Loc, DeclContext *DC) {
|
||||||
|
auto &ctx = DC->getASTContext();
|
||||||
|
return new (ctx) FallthroughStmt(Loc, DC);
|
||||||
|
}
|
||||||
|
|
||||||
|
CaseStmt *FallthroughStmt::getFallthroughSource() const {
|
||||||
|
auto &eval = getDeclContext()->getASTContext().evaluator;
|
||||||
|
return evaluateOrDefault(eval, FallthroughSourceAndDestRequest{this}, {})
|
||||||
|
.Source;
|
||||||
|
}
|
||||||
|
|
||||||
|
CaseStmt *FallthroughStmt::getFallthroughDest() const {
|
||||||
|
auto &eval = getDeclContext()->getASTContext().evaluator;
|
||||||
|
return evaluateOrDefault(eval, FallthroughSourceAndDestRequest{this}, {})
|
||||||
|
.Dest;
|
||||||
|
}
|
||||||
|
|
||||||
SourceLoc swift::extractNearestSourceLoc(const Stmt *S) {
|
SourceLoc swift::extractNearestSourceLoc(const Stmt *S) {
|
||||||
return S->getStartLoc();
|
return S->getStartLoc();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -339,8 +339,8 @@ extension ASTGenVisitor {
|
|||||||
|
|
||||||
func generate(fallThroughStmt node: FallThroughStmtSyntax) -> BridgedFallthroughStmt {
|
func generate(fallThroughStmt node: FallThroughStmtSyntax) -> BridgedFallthroughStmt {
|
||||||
return .createParsed(
|
return .createParsed(
|
||||||
self.ctx,
|
loc: self.generateSourceLoc(node.fallthroughKeyword),
|
||||||
loc: self.generateSourceLoc(node.fallthroughKeyword)
|
declContext: self.declContext
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -648,8 +648,8 @@ ParserResult<Stmt> Parser::parseStmt(bool fromASTGen) {
|
|||||||
if (LabelInfo) diagnose(LabelInfo.Loc, diag::invalid_label_on_stmt);
|
if (LabelInfo) diagnose(LabelInfo.Loc, diag::invalid_label_on_stmt);
|
||||||
if (tryLoc.isValid()) diagnose(tryLoc, diag::try_on_stmt, Tok.getText());
|
if (tryLoc.isValid()) diagnose(tryLoc, diag::try_on_stmt, Tok.getText());
|
||||||
|
|
||||||
return makeParserResult(
|
auto loc = consumeToken(tok::kw_fallthrough);
|
||||||
new (Context) FallthroughStmt(consumeToken(tok::kw_fallthrough)));
|
return makeParserResult(FallthroughStmt::createParsed(loc, CurDeclContext));
|
||||||
}
|
}
|
||||||
case tok::pound_assert:
|
case tok::pound_assert:
|
||||||
if (LabelInfo) diagnose(LabelInfo.Loc, diag::invalid_label_on_stmt);
|
if (LabelInfo) diagnose(LabelInfo.Loc, diag::invalid_label_on_stmt);
|
||||||
|
|||||||
@@ -1794,7 +1794,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
ASTNode visitFallthroughStmt(FallthroughStmt *fallthroughStmt) {
|
ASTNode visitFallthroughStmt(FallthroughStmt *fallthroughStmt) {
|
||||||
if (checkFallthroughStmt(context.getAsDeclContext(), fallthroughStmt))
|
if (checkFallthroughStmt(fallthroughStmt))
|
||||||
hadError = true;
|
hadError = true;
|
||||||
return fallthroughStmt;
|
return fallthroughStmt;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -742,6 +742,25 @@ ContinueTargetRequest::evaluate(Evaluator &evaluator,
|
|||||||
CS->getTargetName(), CS->getTargetLoc(), /*isContinue*/ true, DC);
|
CS->getTargetName(), CS->getTargetLoc(), /*isContinue*/ true, DC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FallthroughSourceAndDest
|
||||||
|
FallthroughSourceAndDestRequest::evaluate(Evaluator &evaluator,
|
||||||
|
const FallthroughStmt *FS) const {
|
||||||
|
auto *SF = FS->getDeclContext()->getParentSourceFile();
|
||||||
|
auto &ctx = SF->getASTContext();
|
||||||
|
auto loc = FS->getLoc();
|
||||||
|
|
||||||
|
auto [src, dest] = ASTScope::lookupFallthroughSourceAndDest(SF, loc);
|
||||||
|
if (!src) {
|
||||||
|
ctx.Diags.diagnose(loc, diag::fallthrough_outside_switch);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (!dest) {
|
||||||
|
ctx.Diags.diagnose(loc, diag::fallthrough_from_last_case);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {src, dest};
|
||||||
|
}
|
||||||
|
|
||||||
static Expr *getDeclRefProvidingExpressionForHasSymbol(Expr *E) {
|
static Expr *getDeclRefProvidingExpressionForHasSymbol(Expr *E) {
|
||||||
// Strip coercions, which are necessary in source to disambiguate overloaded
|
// Strip coercions, which are necessary in source to disambiguate overloaded
|
||||||
// functions or generic functions, e.g.
|
// functions or generic functions, e.g.
|
||||||
@@ -925,12 +944,18 @@ static bool typeCheckConditionForStatement(LabeledConditionalStmt *stmt,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify that the pattern bindings for the cases that we're falling through
|
/// Check the correctness of a 'fallthrough' statement.
|
||||||
/// from and to are equivalent.
|
///
|
||||||
static void checkFallthroughPatternBindingsAndTypes(
|
/// \returns true if an error occurred.
|
||||||
ASTContext &ctx,
|
bool swift::checkFallthroughStmt(FallthroughStmt *FS) {
|
||||||
CaseStmt *caseBlock, CaseStmt *previousBlock,
|
auto &ctx = FS->getDeclContext()->getASTContext();
|
||||||
FallthroughStmt *fallthrough) {
|
auto *caseBlock = FS->getFallthroughDest();
|
||||||
|
auto *previousBlock = FS->getFallthroughSource();
|
||||||
|
if (!previousBlock || !caseBlock)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Verify that the pattern bindings for the cases that we're falling through
|
||||||
|
// from and to are equivalent.
|
||||||
auto firstPattern = caseBlock->getCaseLabelItems()[0].getPattern();
|
auto firstPattern = caseBlock->getCaseLabelItems()[0].getPattern();
|
||||||
SmallVector<VarDecl *, 4> vars;
|
SmallVector<VarDecl *, 4> vars;
|
||||||
firstPattern->collectVariables(vars);
|
firstPattern->collectVariables(vars);
|
||||||
@@ -969,36 +994,10 @@ static void checkFallthroughPatternBindingsAndTypes(
|
|||||||
|
|
||||||
if (!matched) {
|
if (!matched) {
|
||||||
ctx.Diags.diagnose(
|
ctx.Diags.diagnose(
|
||||||
fallthrough->getLoc(), diag::fallthrough_into_case_with_var_binding,
|
FS->getLoc(), diag::fallthrough_into_case_with_var_binding,
|
||||||
expected->getName());
|
expected->getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Check the correctness of a 'fallthrough' statement.
|
|
||||||
///
|
|
||||||
/// \returns true if an error occurred.
|
|
||||||
bool swift::checkFallthroughStmt(DeclContext *dc, FallthroughStmt *stmt) {
|
|
||||||
CaseStmt *fallthroughSource;
|
|
||||||
CaseStmt *fallthroughDest;
|
|
||||||
ASTContext &ctx = dc->getASTContext();
|
|
||||||
auto sourceFile = dc->getParentSourceFile();
|
|
||||||
std::tie(fallthroughSource, fallthroughDest) =
|
|
||||||
ASTScope::lookupFallthroughSourceAndDest(sourceFile, stmt->getLoc());
|
|
||||||
|
|
||||||
if (!fallthroughSource) {
|
|
||||||
ctx.Diags.diagnose(stmt->getLoc(), diag::fallthrough_outside_switch);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!fallthroughDest) {
|
|
||||||
ctx.Diags.diagnose(stmt->getLoc(), diag::fallthrough_from_last_case);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
stmt->setFallthroughSource(fallthroughSource);
|
|
||||||
stmt->setFallthroughDest(fallthroughDest);
|
|
||||||
|
|
||||||
checkFallthroughPatternBindingsAndTypes(
|
|
||||||
ctx, fallthroughDest, fallthroughSource, stmt);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1457,7 +1456,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Stmt *visitFallthroughStmt(FallthroughStmt *S) {
|
Stmt *visitFallthroughStmt(FallthroughStmt *S) {
|
||||||
if (checkFallthroughStmt(DC, S))
|
if (checkFallthroughStmt(S))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return S;
|
return S;
|
||||||
|
|||||||
@@ -1593,6 +1593,8 @@ namespace {
|
|||||||
BS->setDeclContext(NewDC);
|
BS->setDeclContext(NewDC);
|
||||||
if (auto *CS = dyn_cast<ContinueStmt>(S))
|
if (auto *CS = dyn_cast<ContinueStmt>(S))
|
||||||
CS->setDeclContext(NewDC);
|
CS->setDeclContext(NewDC);
|
||||||
|
if (auto *FS = dyn_cast<FallthroughStmt>(S))
|
||||||
|
FS->setDeclContext(NewDC);
|
||||||
|
|
||||||
return Action::Continue(S);
|
return Action::Continue(S);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1433,7 +1433,7 @@ LabeledStmt *findBreakOrContinueStmtTarget(ASTContext &ctx,
|
|||||||
/// Check the correctness of a 'fallthrough' statement.
|
/// Check the correctness of a 'fallthrough' statement.
|
||||||
///
|
///
|
||||||
/// \returns true if an error occurred.
|
/// \returns true if an error occurred.
|
||||||
bool checkFallthroughStmt(DeclContext *dc, FallthroughStmt *stmt);
|
bool checkFallthroughStmt(FallthroughStmt *stmt);
|
||||||
|
|
||||||
/// Check for restrictions on the use of the @unknown attribute on a
|
/// Check for restrictions on the use of the @unknown attribute on a
|
||||||
/// case statement.
|
/// case statement.
|
||||||
|
|||||||
Reference in New Issue
Block a user