Skip to content

[5.8] Introduce if/switch expressions #63378

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
1 change: 1 addition & 0 deletions include/swift/AST/ASTScope.h
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,7 @@ class PatternEntryInitializerScope final : public AbstractPatternEntryScope {

protected:
bool lookupLocalsOrMembers(DeclConsumer) const override;
bool isLabeledStmtLookupTerminator() const override;
};

/// The scope introduced by a conditional clause initializer in an
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/ASTTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ SWIFT_TYPEID(Fingerprint)
SWIFT_TYPEID(GenericSignature)
SWIFT_TYPEID(ImplicitImportList)
SWIFT_TYPEID(ImplicitMemberAction)
SWIFT_TYPEID(IsSingleValueStmtResult)
SWIFT_TYPEID(ParamSpecifier)
SWIFT_TYPEID(PropertyWrapperAuxiliaryVariables)
SWIFT_TYPEID(PropertyWrapperInitializerInfo)
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/ASTTypeIDs.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class GenericParamList;
class GenericSignature;
class GenericTypeParamType;
class InfixOperatorDecl;
class IsSingleValueStmtResult;
class IterableDeclContext;
class ModuleDecl;
struct ImplicitImportList;
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/CASTBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ void *SwiftVarDecl_create(void *ctx, BridgedIdentifier _Nullable name,
void *initExpr, void *loc, _Bool isStatic,
_Bool isLet, void *dc);

void *SingleValueStmtExpr_createWithWrappedBranches(void *ctx, void *S,
void *DC, _Bool mustBeExpr);

void *IfStmt_create(void *ctx, void *ifLoc, void *cond, void *_Nullable then,
void *_Nullable elseLoc, void *_Nullable elseStmt);

Expand Down
23 changes: 23 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,29 @@ ERROR(ternary_expr_cases_mismatch,none,
"result values in '? :' expression have mismatching types %0 and %1",
(Type, Type))

// Statements as expressions
ERROR(single_value_stmt_branches_mismatch,none,
"branches have mismatching types %0 and %1",
(Type, Type))
ERROR(single_value_stmt_out_of_place,none,
"'%0' may only be used as expression in return, throw, or as the source "
"of an assignment",
(StmtKind))
ERROR(single_value_stmt_must_be_unlabeled,none,
"'%0' cannot have a jump label when used as expression",
(StmtKind))
ERROR(if_expr_must_be_syntactically_exhaustive,none,
"'if' must have an unconditional 'else' to be used as expression",
())
ERROR(single_value_stmt_branch_must_end_in_throw,none,
"non-expression branch of '%0' expression may only end with a 'throw'",
(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(did_not_call_function_value,none,
"function value was used as a property; add () to call it",
())
Expand Down
98 changes: 95 additions & 3 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -4350,6 +4350,10 @@ class OpaqueValueExpr : public Expr {
Bits.OpaqueValueExpr.IsPlaceholder = isPlaceholder;
}

static OpaqueValueExpr *
createImplicit(ASTContext &ctx, Type Ty, bool isPlaceholder = false,
AllocationArena arena = AllocationArena::Permanent);

/// Whether this opaque value expression represents a placeholder that
/// is injected before type checking to act as a placeholder for some
/// value to be specified later.
Expand Down Expand Up @@ -5965,6 +5969,63 @@ class KeyPathDotExpr : public Expr {
}
};

/// An expression that may wrap a statement which produces a single value.
class SingleValueStmtExpr : public Expr {
public:
enum class Kind {
If, Switch
};

private:
Stmt *S;
DeclContext *DC;

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);

/// Creates a new SingleValueStmtExpr wrapping a statement, and recursively
/// attempts to wrap any branches of that statement that can become single
/// value statement expressions.
///
/// If \p mustBeExpr is true, branches will be eagerly wrapped even if they
/// may not be valid SingleValueStmtExprs (which Sema will later diagnose).
static SingleValueStmtExpr *createWithWrappedBranches(ASTContext &ctx,
Stmt *S,
DeclContext *DC,
bool mustBeExpr);

/// Attempt to look through valid parent expressions to a child
/// SingleValueStmtExpr.
static SingleValueStmtExpr *tryDigOutSingleValueStmtExpr(Expr *E);

/// Retrieve the wrapped statement.
Stmt *getStmt() const { return S; }
void setStmt(Stmt *newS) { S = newS; }

/// Retrieve the kind of statement being wrapped.
Kind getStmtKind() const;

/// 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.
ArrayRef<Expr *>
getSingleExprBranches(SmallVectorImpl<Expr *> &scratch) const;

DeclContext *getDeclContext() const { return DC; }

SourceRange getSourceRange() const;

static bool classof(const Expr *E) {
return E->getKind() == ExprKind::SingleValueStmt;
}
};

/// Expression node that effects a "one-way" constraint in
/// the constraint system, allowing type information to flow from the
/// subexpression outward but not the other way.
Expand Down Expand Up @@ -5998,6 +6059,10 @@ class TypeJoinExpr final : public Expr,

DeclRefExpr *Var;

/// If this is joining the expression branches for a SingleValueStmtExpr,
/// this holds the expr node. Otherwise, it is \c nullptr.
SingleValueStmtExpr *SVE;

size_t numTrailingObjects() const {
return getNumElements();
}
Expand All @@ -6006,11 +6071,34 @@ class TypeJoinExpr final : public Expr,
return { getTrailingObjects<Expr *>(), getNumElements() };
}

TypeJoinExpr(DeclRefExpr *var, ArrayRef<Expr *> elements);
TypeJoinExpr(llvm::PointerUnion<DeclRefExpr *, TypeBase *> result,
ArrayRef<Expr *> elements, SingleValueStmtExpr *SVE);

static TypeJoinExpr *
createImpl(ASTContext &ctx,
llvm::PointerUnion<DeclRefExpr *, TypeBase *> varOrType,
ArrayRef<Expr *> elements,
AllocationArena arena = AllocationArena::Permanent,
SingleValueStmtExpr *SVE = nullptr);

public:
static TypeJoinExpr *create(ASTContext &ctx, DeclRefExpr *var,
ArrayRef<Expr *> exprs);
static TypeJoinExpr *
create(ASTContext &ctx, DeclRefExpr *var, ArrayRef<Expr *> exprs,
AllocationArena arena = AllocationArena::Permanent) {
return createImpl(ctx, var, exprs, arena);
}

static TypeJoinExpr *
create(ASTContext &ctx, Type joinType, ArrayRef<Expr *> exprs,
AllocationArena arena = AllocationArena::Permanent) {
return createImpl(ctx, joinType.getPointer(), exprs, arena);
}

/// Create a join for the branch types of a SingleValueStmtExpr.
static TypeJoinExpr *
forBranchesOfSingleValueStmtExpr(ASTContext &ctx, Type joinType,
SingleValueStmtExpr *SVE,
AllocationArena arena);

SourceLoc getLoc() const { return SourceLoc(); }
SourceRange getSourceRange() const { return SourceRange(); }
Expand All @@ -6034,6 +6122,10 @@ class TypeJoinExpr final : public Expr,
getMutableElements()[i] = E;
}

/// If this is joining the expression branches for a SingleValueStmtExpr,
/// this returns the expr node. Otherwise, returns \c nullptr.
SingleValueStmtExpr *getSingleValueStmtExpr() const { return SVE; }

unsigned getNumElements() const { return Bits.TypeJoinExpr.NumElements; }

static bool classof(const Expr *E) {
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/ExprNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ EXPR(LazyInitializer, Expr)
EXPR(EditorPlaceholder, Expr)
EXPR(ObjCSelector, Expr)
EXPR(KeyPath, Expr)
EXPR(SingleValueStmt, Expr)
UNCHECKED_EXPR(KeyPathDot, Expr)
UNCHECKED_EXPR(OneWay, Expr)
EXPR(Tap, Expr)
Expand Down
27 changes: 24 additions & 3 deletions include/swift/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@ class ASTContext;
class ASTWalker;
class Decl;
class DeclContext;
class Evaluator;
class Expr;
class FuncDecl;
class Pattern;
class PatternBindingDecl;
class VarDecl;
class CaseStmt;
class DoCatchStmt;
class IsSingleValueStmtResult;
class SwitchStmt;

enum class StmtKind {
Expand Down Expand Up @@ -133,7 +135,12 @@ class alignas(8) Stmt : public ASTAllocated<Stmt> {

SourceRange getSourceRange() const;
SourceLoc TrailingSemiLoc;


/// Whether the statement can produce a single value, and as such may be
/// treated as an expression.
IsSingleValueStmtResult mayProduceSingleValue(Evaluator &eval) const;
IsSingleValueStmtResult mayProduceSingleValue(ASTContext &ctx) const;

/// isImplicit - Determines whether this statement was implicitly-generated,
/// rather than explicitly written in the AST.
bool isImplicit() const { return Bits.Stmt.Implicit; }
Expand Down Expand Up @@ -204,6 +211,10 @@ class BraceStmt final : public Stmt,

ASTNode findAsyncNode();

/// If this brace is wrapping a single expression, returns it. Otherwise
/// returns \c nullptr.
Expr *getSingleExpressionElement() const;

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

Expand Down Expand Up @@ -711,7 +722,14 @@ class IfStmt : public LabeledConditionalStmt {

Stmt *getElseStmt() const { return Else; }
void setElseStmt(Stmt *s) { Else = s; }


/// Retrieve the complete set of branches for this if statement, including
/// else if statements.
ArrayRef<Stmt *> getBranches(SmallVectorImpl<Stmt *> &scratch) const;

/// Whether the if statement has an unconditional \c else.
bool isSyntacticallyExhaustive() const;

// Implement isa/cast/dyncast/etc.
static bool classof(const Stmt *S) { return S->getKind() == StmtKind::If; }
};
Expand Down Expand Up @@ -1283,7 +1301,10 @@ class SwitchStmt final : public LabeledStmt,
AsCaseStmtRange getCases() const {
return AsCaseStmtRange(getRawCases(), AsCaseStmtWithSkippingNonCaseStmts());
}


/// Retrieve the complete set of branches for this switch statement.
ArrayRef<Stmt *> getBranches(SmallVectorImpl<Stmt *> &scratch) const;

static bool classof(const Stmt *S) {
return S->getKind() == StmtKind::Switch;
}
Expand Down
Loading