Skip to content

Introduce then statements #67454

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -1244,8 +1244,9 @@ ERROR(case_stmt_without_body,none,
// 'try' on statements
ERROR(try_on_stmt,none,
"'try' cannot be used with '%0'", (StringRef))
ERROR(try_on_return_throw_yield,none,
"'try' must be placed on the %select{returned|thrown|yielded}0 expression",
ERROR(try_must_come_after_stmt,none,
"'try' must be placed on the %select{returned|thrown|yielded|produced}0 "
"expression",
(unsigned))
ERROR(try_on_var_let,none,
"'try' must be placed on the initial value expression", ())
Expand Down Expand Up @@ -1399,6 +1400,12 @@ ERROR(expected_expr_after_each, none,
"expected expression after 'each'", ())
ERROR(expected_expr_after_repeat, none,
"expected expression after 'repeat'", ())
ERROR(expected_expr_after_then,none,
"expected expression after 'then'", ())

WARNING(unindented_code_after_then,none,
"expression following 'then' is treated as an argument of "
"the 'then'", ())

WARNING(move_consume_final_spelling, none,
"'_move' has been renamed to 'consume', and the '_move' spelling will be removed shortly", ())
Expand Down
8 changes: 6 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1231,14 +1231,18 @@ ERROR(if_expr_must_be_syntactically_exhaustive,none,
ERROR(single_value_stmt_branch_empty,none,
"expected expression in branch of '%0' expression",
(StmtKind))
ERROR(single_value_stmt_branch_must_end_in_throw,none,
"non-expression branch of '%0' expression may only end with a 'throw'",
ERROR(single_value_stmt_branch_must_end_in_result,none,
"non-expression branch of '%0' expression may only end with a 'throw' "
"or 'then'",
(StmtKind))
ERROR(cannot_jump_in_single_value_stmt,none,
"cannot '%0' in '%1' when used as expression",
(StmtKind, StmtKind))
ERROR(effect_marker_on_single_value_stmt,none,
"'%0' may not be used on '%1' expression", (StringRef, StmtKind))
ERROR(out_of_place_then_stmt,none,
"'then' may only appear as the last statement in an 'if' or 'switch' "
"expression", ())

ERROR(did_not_call_function_value,none,
"function value was used as a property; add () to call it",
Expand Down
24 changes: 20 additions & 4 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ namespace swift {
class CallExpr;
class KeyPathExpr;
class CaptureListExpr;
class ThenStmt;

enum class ExprKind : uint8_t {
#define EXPR(Id, Parent) Id,
Expand Down Expand Up @@ -6125,10 +6126,10 @@ class SingleValueStmtExpr : public Expr {
SingleValueStmtExpr(Stmt *S, DeclContext *DC)
: Expr(ExprKind::SingleValueStmt, /*isImplicit*/ true), S(S), DC(DC) {}

public:
/// Creates a new SingleValueStmtExpr wrapping a statement.
static SingleValueStmtExpr *create(ASTContext &ctx, Stmt *S, DeclContext *DC);

public:
/// Creates a new SingleValueStmtExpr wrapping a statement, and recursively
/// attempts to wrap any branches of that statement that can become single
/// value statement expressions.
Expand All @@ -6144,6 +6145,16 @@ class SingleValueStmtExpr : public Expr {
/// SingleValueStmtExpr.
static SingleValueStmtExpr *tryDigOutSingleValueStmtExpr(Expr *E);

/// Retrieves a resulting ThenStmt from the given BraceStmt, or \c nullptr if
/// the brace does not have a resulting ThenStmt.
static ThenStmt *getThenStmtFrom(BraceStmt *BS);

/// Whether the given BraceStmt has a result to be produced from a parent
/// SingleValueStmtExpr.
static bool hasResult(BraceStmt *BS) {
return getThenStmtFrom(BS);
}

/// Retrieve the wrapped statement.
Stmt *getStmt() const { return S; }
void setStmt(Stmt *newS) { S = newS; }
Expand All @@ -6154,10 +6165,15 @@ class SingleValueStmtExpr : public Expr {
/// Retrieve the complete set of branches for the underlying statement.
ArrayRef<Stmt *> getBranches(SmallVectorImpl<Stmt *> &scratch) const;

/// Retrieve the single expression branches of the statement, excluding
/// branches that either have multiple expressions, or have statements.
/// Retrieve the resulting ThenStmts from each branch of the
/// SingleValueStmtExpr.
ArrayRef<ThenStmt *>
getThenStmts(SmallVectorImpl<ThenStmt *> &scratch) const;

/// Retrieve the result expressions from each branch of the
/// SingleValueStmtExpr.
ArrayRef<Expr *>
getSingleExprBranches(SmallVectorImpl<Expr *> &scratch) const;
getResultExprs(SmallVectorImpl<Expr *> &scratch) const;

DeclContext *getDeclContext() const { return DC; }

Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/NameLookup.h
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,7 @@ class FindLocalVal : public StmtVisitor<FindLocalVal> {
void visitFailStmt(FailStmt *) {}
void visitReturnStmt(ReturnStmt *) {}
void visitYieldStmt(YieldStmt *) {}
void visitThenStmt(ThenStmt *) {}
void visitThrowStmt(ThrowStmt *) {}
void visitDiscardStmt(DiscardStmt *) {}
void visitPoundAssertStmt(PoundAssertStmt *) {}
Expand Down
32 changes: 32 additions & 0 deletions include/swift/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,38 @@ class YieldStmt final
static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Yield; }
};

/// The statement `then <expr>`. This is used within if/switch expressions to
/// indicate the value being produced by a given branch.
class ThenStmt : public Stmt {
SourceLoc ThenLoc;
Expr *Result;

ThenStmt(SourceLoc thenLoc, Expr *result, bool isImplicit)
: Stmt(StmtKind::Then, isImplicit), ThenLoc(thenLoc), Result(result) {
assert(Result && "Must have non-null result");
}

public:
/// Create a new parsed ThenStmt.
static ThenStmt *createParsed(ASTContext &ctx, SourceLoc thenLoc,
Expr *result);

/// Create an implicit ThenStmt.
///
/// Note that such statements will be elided during the result builder
/// transform.
static ThenStmt *createImplicit(ASTContext &ctx, Expr *result);

SourceLoc getThenLoc() const { return ThenLoc; }

SourceRange getSourceRange() const;

Expr *getResult() const { return Result; }
void setResult(Expr *e) { Result = e; }

static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Then; }
};

/// DeferStmt - A 'defer' statement. This runs the substatement it contains
/// when the enclosing scope is exited.
///
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/StmtNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@

STMT(Brace, Stmt)
STMT(Return, Stmt)
STMT(Then, Stmt)
STMT(Yield, Stmt)
STMT(Defer, Stmt)
ABSTRACT_STMT(Labeled, Stmt)
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ MISC(string_segment)
MISC(string_interpolation_anchor)
MISC(kw_yield)
MISC(kw_discard)
MISC(kw_then)

// The following tokens are irrelevant for swiftSyntax and thus only declared
// in this .def file
Expand Down
8 changes: 4 additions & 4 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -3901,8 +3901,8 @@ class IsSingleValueStmtResult {
/// The statement is an 'if' statement without an unconditional 'else'.
NonExhaustiveIf,

/// There are no single-expression branches.
NoExpressionBranches,
/// There is no branch that produces a resulting value.
NoResult,

/// There is an unhandled statement branch. This should only be the case
/// for invalid AST.
Expand Down Expand Up @@ -3957,8 +3957,8 @@ class IsSingleValueStmtResult {
static IsSingleValueStmtResult nonExhaustiveIf() {
return IsSingleValueStmtResult(Kind::NonExhaustiveIf);
}
static IsSingleValueStmtResult noExpressionBranches() {
return IsSingleValueStmtResult(Kind::NoExpressionBranches);
static IsSingleValueStmtResult noResult() {
return IsSingleValueStmtResult(Kind::NoResult);
}
static IsSingleValueStmtResult unhandledStmt() {
return IsSingleValueStmtResult(Kind::UnhandledStmt);
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ EXPERIMENTAL_FEATURE(GlobalConcurrency, false)
/// "playground transform" is enabled.
EXPERIMENTAL_FEATURE(PlaygroundExtendedCallbacks, true)

/// Enable 'then' statements.
EXPERIMENTAL_FEATURE(ThenStatements, false)

/// Enable the `@_rawLayout` attribute.
EXPERIMENTAL_FEATURE(RawLayout, true)

Expand Down
1 change: 1 addition & 0 deletions include/swift/IDE/CodeCompletionResult.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ enum class CompletionKind : uint8_t {
CallArg,
LabeledTrailingClosure,
ReturnStmtExpr,
ThenStmtExpr,
YieldStmtExpr,
ForEachSequence,

Expand Down
2 changes: 2 additions & 0 deletions include/swift/Parse/IDEInspectionCallbacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ class CodeCompletionCallbacks {

virtual void completeReturnStmt(CodeCompletionExpr *E) {};

virtual void completeThenStmt(CodeCompletionExpr *E) {};

/// Complete a yield statement. A missing yield index means that the
/// completion immediately follows the 'yield' keyword; it may be either
/// an expression or a parenthesized expression list. A present yield
Expand Down
15 changes: 13 additions & 2 deletions include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,10 @@ class Parser {
cast<AccessorDecl>(CurDeclContext)->isCoroutine());
}

/// Whether the current token is the contextual keyword for a \c then
/// statement.
bool isContextualThenKeyword(bool preferExpr);

/// `discard self` is the only valid phrase, but we peek ahead for just any
/// identifier after `discard` to determine if it's the statement. This helps
/// us avoid interpreting `discard(self)` as the statement and not a call.
Expand Down Expand Up @@ -641,7 +645,7 @@ class Parser {
while (Tok.isNot(K..., tok::eof, tok::r_brace, tok::pound_endif,
tok::pound_else, tok::pound_elseif,
tok::code_complete) &&
!isStartOfStmt() &&
!isStartOfStmt(/*preferExpr*/ false) &&
!isStartOfSwiftDecl(/*allowPoundIfAttributes=*/true)) {
skipSingle();
}
Expand Down Expand Up @@ -1919,7 +1923,13 @@ class Parser {
//===--------------------------------------------------------------------===//
// Statement Parsing

bool isStartOfStmt();
/// Whether we are at the start of a statement.
///
/// \param preferExpr If either an expression or statement could be parsed and
/// this parameter is \c true, the function returns \c false such that an
/// expression can be parsed.
bool isStartOfStmt(bool preferExpr);

bool isTerminatorForBraceItemListKind(BraceItemListKind Kind,
ArrayRef<ASTNode> ParsedDecls);
ParserResult<Stmt> parseStmt();
Expand All @@ -1928,6 +1938,7 @@ class Parser {
ParserResult<Stmt> parseStmtContinue();
ParserResult<Stmt> parseStmtReturn(SourceLoc tryLoc);
ParserResult<Stmt> parseStmtYield(SourceLoc tryLoc);
ParserResult<Stmt> parseStmtThen(SourceLoc tryLoc);
ParserResult<Stmt> parseStmtThrow(SourceLoc tryLoc);
ParserResult<Stmt> parseStmtDiscard();
ParserResult<Stmt> parseStmtDefer();
Expand Down
33 changes: 32 additions & 1 deletion include/swift/Parse/Token.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,38 @@ class Token {
bool isBinaryOperator() const {
return Kind == tok::oper_binary_spaced || Kind == tok::oper_binary_unspaced;
}


/// Checks whether the token is either a binary operator, or is a token that
/// acts like a binary operator (e.g infix '=', '?', '->').
bool isBinaryOperatorLike() const {
if (isBinaryOperator())
return true;

switch (Kind) {
case tok::equal:
case tok::arrow:
case tok::question_infix:
return true;
default:
return false;
}
llvm_unreachable("Unhandled case in switch!");
}

/// Checks whether the token is either a postfix operator, or is a token that
/// acts like a postfix operator (e.g postfix '!' and '?').
bool isPostfixOperatorLike() const {
switch (Kind) {
case tok::oper_postfix:
case tok::exclaim_postfix:
case tok::question_postfix:
return true;
default:
return false;
}
llvm_unreachable("Unhandled case in switch!");
}

bool isAnyOperator() const {
return isBinaryOperator() || Kind == tok::oper_postfix ||
Kind == tok::oper_prefix;
Expand Down
26 changes: 26 additions & 0 deletions include/swift/Sema/CSFix.h
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,9 @@ enum class FixKind : uint8_t {
/// Ignore an attempt to specialize non-generic type.
AllowConcreteTypeSpecialization,

/// Ignore an out-of-place \c then statement.
IgnoreOutOfPlaceThenStmt,

/// Ignore situations when provided number of generic arguments didn't match
/// expected number of parameters.
IgnoreGenericSpecializationArityMismatch,
Expand Down Expand Up @@ -3684,6 +3687,29 @@ class AllowConcreteTypeSpecialization final : public ConstraintFix {
}
};

class IgnoreOutOfPlaceThenStmt final : public ConstraintFix {
IgnoreOutOfPlaceThenStmt(ConstraintSystem &cs, ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::IgnoreOutOfPlaceThenStmt, locator) {}

public:
std::string getName() const override {
return "ignore out-of-place ThenStmt";
}

bool diagnose(const Solution &solution, bool asNote = false) const override;

bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
return diagnose(*commonFixes.front().first);
}

static IgnoreOutOfPlaceThenStmt *create(ConstraintSystem &cs,
ConstraintLocator *locator);

static bool classof(const ConstraintFix *fix) {
return fix->getKind() == FixKind::IgnoreOutOfPlaceThenStmt;
}
};

class IgnoreGenericSpecializationArityMismatch final : public ConstraintFix {
ValueDecl *D;
unsigned NumParams;
Expand Down
24 changes: 15 additions & 9 deletions include/swift/Sema/ConstraintLocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,14 @@ enum class ConversionRestrictionKind;

/// The kind of SingleValueStmtExpr branch the locator identifies.
enum class SingleValueStmtBranchKind {
Regular,
InSingleExprClosure
/// Explicitly written 'then <expr>'.
Explicit,

/// Implicitly written '<expr>'.
Implicit,

/// Implicitly written '<expr>' in a single expr closure body.
ImplicitInSingleExprClosure
};

/// Locates a given constraint within the expression being
Expand Down Expand Up @@ -938,19 +944,19 @@ class LocatorPathElt::TernaryBranch final : public StoredIntegerElement<1> {
}
};

/// The branch of a SingleValueStmtExpr. Note the stored index corresponds to
/// the expression branches, i.e it skips statement branch indices.
class LocatorPathElt::SingleValueStmtBranch final
/// A particular result of a ThenStmt at a given index in a SingleValueStmtExpr.
/// The stored index corresponds to the \c getResultExprs array.
class LocatorPathElt::SingleValueStmtResult final
: public StoredIntegerElement<1> {
public:
SingleValueStmtBranch(unsigned exprIdx)
: StoredIntegerElement(ConstraintLocator::SingleValueStmtBranch,
SingleValueStmtResult(unsigned exprIdx)
: StoredIntegerElement(ConstraintLocator::SingleValueStmtResult,
exprIdx) {}

unsigned getExprBranchIndex() const { return getValue(); }
unsigned getIndex() const { return getValue(); }

static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == ConstraintLocator::SingleValueStmtBranch;
return elt->getKind() == ConstraintLocator::SingleValueStmtResult;
}
};

Expand Down
4 changes: 2 additions & 2 deletions include/swift/Sema/ConstraintLocatorPathElts.def
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,8 @@ CUSTOM_LOCATOR_PATH_ELT(SyntacticElement)
/// The element of the pattern binding declaration.
CUSTOM_LOCATOR_PATH_ELT(PatternBindingElement)

/// A branch of a SingleValueStmtExpr.
CUSTOM_LOCATOR_PATH_ELT(SingleValueStmtBranch)
/// A particular result of a ThenStmt at a given index in a SingleValueStmtExpr.
CUSTOM_LOCATOR_PATH_ELT(SingleValueStmtResult)

/// A declaration introduced by a pattern: name (i.e. `x`) or `_`
ABSTRACT_LOCATOR_PATH_ELT(PatternDecl)
Expand Down
Loading