Introduce if/switch expressions

Introduce SingleValueStmtExpr, which allows the
embedding of a statement in an expression context.
This then allows us to parse and type-check `if`
and `switch` statements as expressions, gated
behind the `IfSwitchExpression` experimental
feature for now. In the future,
SingleValueStmtExpr could also be used for e.g
`do` expressions.

For now, only single expression branches are
supported for producing a value from an
`if`/`switch` expression, and each branch is
type-checked independently. A multi-statement
branch may only appear if it ends with a `throw`,
and it may not `break`, `continue`, or `return`.

The placement of `if`/`switch` expressions is also
currently limited by a syntactic use diagnostic.
Currently they're only allowed in bindings,
assignments, throws, and returns. But this could
be lifted in the future if desired.
This commit is contained in:
Hamish Knight
2023-02-01 15:30:18 +00:00
parent df2b3b2880
commit a40f1abaff
70 changed files with 5520 additions and 188 deletions

View File

@@ -1961,12 +1961,19 @@ private:
/// type-checked.
DeclContext *dc;
// TODO: Fold the 3 below fields into ContextualTypeInfo
/// The purpose of the contextual type.
ContextualTypePurpose contextualPurpose;
/// The type to which the expression should be converted.
TypeLoc convertType;
/// The locator for the contextual type conversion constraint, or
/// \c nullptr to use the default locator which is anchored directly on
/// the expression.
ConstraintLocator *convertTypeLocator;
/// When initializing a pattern from the expression, this is the
/// pattern.
Pattern *pattern;
@@ -2054,14 +2061,25 @@ private:
public:
SolutionApplicationTarget(Expr *expr, DeclContext *dc,
ContextualTypePurpose contextualPurpose,
Type convertType, bool isDiscarded)
Type convertType,
ConstraintLocator *convertTypeLocator,
bool isDiscarded)
: SolutionApplicationTarget(expr, dc, contextualPurpose,
TypeLoc::withoutLoc(convertType),
isDiscarded) { }
convertTypeLocator, isDiscarded) {}
SolutionApplicationTarget(Expr *expr, DeclContext *dc,
ContextualTypePurpose contextualPurpose,
TypeLoc convertType, bool isDiscarded);
Type convertType, bool isDiscarded)
: SolutionApplicationTarget(expr, dc, contextualPurpose, convertType,
/*convertTypeLocator*/ nullptr, isDiscarded) {
}
SolutionApplicationTarget(Expr *expr, DeclContext *dc,
ContextualTypePurpose contextualPurpose,
TypeLoc convertType,
ConstraintLocator *convertTypeLocator,
bool isDiscarded);
SolutionApplicationTarget(Expr *expr, DeclContext *dc, ExprPattern *pattern,
Type patternType)
@@ -2288,6 +2306,13 @@ public:
return getExprContextualType();
}
/// Retrieve the conversion type locator for the expression, or \c nullptr
/// if it has not been set.
ConstraintLocator *getExprConvertTypeLocator() const {
assert(kind == Kind::expression);
return expression.convertTypeLocator;
}
/// Returns the autoclosure parameter type, or \c nullptr if the
/// expression has a different kind of context.
FunctionType *getAsAutoclosureParamType() const {
@@ -4224,7 +4249,8 @@ public:
/// Add the appropriate constraint for a contextual conversion.
void addContextualConversionConstraint(Expr *expr, Type conversionType,
ContextualTypePurpose purpose);
ContextualTypePurpose purpose,
ConstraintLocator *locator);
/// Convenience function to pass an \c ArrayRef to \c addJoinConstraint
Type addJoinConstraint(ConstraintLocator *locator,
@@ -5080,6 +5106,11 @@ public:
LLVM_NODISCARD
bool generateConstraints(AnyFunctionRef fn, BraceStmt *body);
/// Generate constraints for a given SingleValueStmtExpr.
///
/// \returns \c true if constraint generation failed, \c false otherwise
bool generateConstraints(SingleValueStmtExpr *E);
/// Generate constraints for the given (unchecked) expression.
///
/// \returns a possibly-sanitized expression, or null if an error occurred.
@@ -5978,6 +6009,22 @@ public:
SolutionApplicationTarget)>
rewriteTarget);
/// Apply the given solution to the given SingleValueStmtExpr.
///
/// \param solution The solution to apply.
/// \param SVE The SingleValueStmtExpr to rewrite.
/// \param DC The declaration context in which transformations will be
/// applied.
/// \param rewriteTarget Function that performs a rewrite of any
/// solution application target within the context.
///
/// \returns true if solution cannot be applied.
bool applySolutionToSingleValueStmt(
Solution &solution, SingleValueStmtExpr *SVE, DeclContext *DC,
std::function<
Optional<SolutionApplicationTarget>(SolutionApplicationTarget)>
rewriteTarget);
/// Reorder the disjunctive clauses for a given expression to
/// increase the likelihood that a favored constraint will be successfully
/// resolved before any others.