diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 4f1bf66fa4a20..3719586a56cbb 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -955,7 +955,6 @@ 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 diff --git a/include/swift/AST/ASTTypeIDZone.def b/include/swift/AST/ASTTypeIDZone.def index 00ace188d39cd..428bf7c64182b 100644 --- a/include/swift/AST/ASTTypeIDZone.def +++ b/include/swift/AST/ASTTypeIDZone.def @@ -26,7 +26,6 @@ SWIFT_TYPEID(Fingerprint) SWIFT_TYPEID(GenericSignature) SWIFT_TYPEID(ImplicitImportList) SWIFT_TYPEID(ImplicitMemberAction) -SWIFT_TYPEID(IsSingleValueStmtResult) SWIFT_TYPEID(ParamSpecifier) SWIFT_TYPEID(PropertyWrapperAuxiliaryVariables) SWIFT_TYPEID(PropertyWrapperInitializerInfo) diff --git a/include/swift/AST/ASTTypeIDs.h b/include/swift/AST/ASTTypeIDs.h index 4629489ebc490..a93b0cea8547e 100644 --- a/include/swift/AST/ASTTypeIDs.h +++ b/include/swift/AST/ASTTypeIDs.h @@ -41,7 +41,6 @@ class GenericParamList; class GenericSignature; class GenericTypeParamType; class InfixOperatorDecl; -class IsSingleValueStmtResult; class IterableDeclContext; class ModuleDecl; struct ImplicitImportList; diff --git a/include/swift/AST/CASTBridging.h b/include/swift/AST/CASTBridging.h index a3e8af0f994d5..fd69e2e0361d0 100644 --- a/include/swift/AST/CASTBridging.h +++ b/include/swift/AST/CASTBridging.h @@ -169,9 +169,6 @@ 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); diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index b00ef0a4e0eda..fabfe8b30b364 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1080,29 +1080,6 @@ 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", ()) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 2d1e915900ff1..6ae8d31d3a948 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -4350,10 +4350,6 @@ 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. @@ -5969,63 +5965,6 @@ 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 getBranches(SmallVectorImpl &scratch) const; - - /// Retrieve the single expression branches of the statement, excluding - /// branches that either have multiple expressions, or have statements. - ArrayRef - getSingleExprBranches(SmallVectorImpl &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. @@ -6059,10 +5998,6 @@ 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(); } @@ -6071,34 +6006,11 @@ class TypeJoinExpr final : public Expr, return { getTrailingObjects(), getNumElements() }; } - TypeJoinExpr(llvm::PointerUnion result, - ArrayRef elements, SingleValueStmtExpr *SVE); - - static TypeJoinExpr * - createImpl(ASTContext &ctx, - llvm::PointerUnion varOrType, - ArrayRef elements, - AllocationArena arena = AllocationArena::Permanent, - SingleValueStmtExpr *SVE = nullptr); + TypeJoinExpr(DeclRefExpr *var, ArrayRef elements); public: - static TypeJoinExpr * - create(ASTContext &ctx, DeclRefExpr *var, ArrayRef exprs, - AllocationArena arena = AllocationArena::Permanent) { - return createImpl(ctx, var, exprs, arena); - } - - static TypeJoinExpr * - create(ASTContext &ctx, Type joinType, ArrayRef 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); + static TypeJoinExpr *create(ASTContext &ctx, DeclRefExpr *var, + ArrayRef exprs); SourceLoc getLoc() const { return SourceLoc(); } SourceRange getSourceRange() const { return SourceRange(); } @@ -6122,10 +6034,6 @@ 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) { diff --git a/include/swift/AST/ExprNodes.def b/include/swift/AST/ExprNodes.def index 1d5481959f783..58c0ecf7af4c3 100644 --- a/include/swift/AST/ExprNodes.def +++ b/include/swift/AST/ExprNodes.def @@ -204,7 +204,6 @@ 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) diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index f0be7068e8bf7..2bc3656093127 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -35,7 +35,6 @@ class ASTContext; class ASTWalker; class Decl; class DeclContext; -class Evaluator; class Expr; class FuncDecl; class Pattern; @@ -43,7 +42,6 @@ class PatternBindingDecl; class VarDecl; class CaseStmt; class DoCatchStmt; -class IsSingleValueStmtResult; class SwitchStmt; enum class StmtKind { @@ -135,12 +133,7 @@ class alignas(8) Stmt : public ASTAllocated { 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; } @@ -211,10 +204,6 @@ 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; } }; @@ -722,14 +711,7 @@ 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 getBranches(SmallVectorImpl &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; } }; @@ -1301,10 +1283,7 @@ class SwitchStmt final : public LabeledStmt, AsCaseStmtRange getCases() const { return AsCaseStmtRange(getRawCases(), AsCaseStmtWithSkippingNonCaseStmts()); } - - /// Retrieve the complete set of branches for this switch statement. - ArrayRef getBranches(SmallVectorImpl &scratch) const; - + static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Switch; } diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 4a8e115cc6d72..ef9a7cb828587 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -54,7 +54,6 @@ class PropertyWrapperInitializerInfo; struct PropertyWrapperLValueness; struct PropertyWrapperMutability; class RequirementRepr; -class ReturnStmt; class SpecializeAttr; class TrailingWhereClause; class TypeAliasDecl; @@ -3760,146 +3759,6 @@ class ContinueTargetRequest bool isCached() const { return true; } }; -/// Precheck a ReturnStmt, which involves some initial validation, as well as -/// applying a conversion to a FailStmt if needed. -class PreCheckReturnStmtRequest - : public SimpleRequest { -public: - using SimpleRequest::SimpleRequest; - -private: - friend SimpleRequest; - - Stmt *evaluate(Evaluator &evaluator, ReturnStmt *RS, DeclContext *DC) const; - -public: - bool isCached() const { return true; } -}; - -/// The result of the query for whether a statement can produce a single value. -class IsSingleValueStmtResult { -public: - enum class Kind { - /// The statement may become a SingleValueStmtExpr. - Valid, - - /// There are non-single-expression branches that do not end in a throw. - UnterminatedBranches, - - /// The statement is an 'if' statement without an unconditional 'else'. - NonExhaustiveIf, - - /// There are no single-expression branches. - NoExpressionBranches, - - /// There is an unhandled statement branch. This should only be the case - /// for invalid AST. - UnhandledStmt, - - /// There was a circular reference when evaluating the request. This can be - /// ignored, as we will have already diagnosed it. - CircularReference, - - /// There is a 'break' or 'continue' within the statement that prevents it - /// from being treated as an expression. - InvalidJumps, - - /// The statement has a jump label, which is invalid for an expression. - HasLabel - }; - -private: - Kind TheKind; - TinyPtrVector InvalidJumps; - TinyPtrVector UnterminatedBranches; - - IsSingleValueStmtResult(Kind kind) : TheKind(kind) { - assert(kind != Kind::UnterminatedBranches && kind != Kind::InvalidJumps); - } - - IsSingleValueStmtResult(Kind kind, TinyPtrVector stmts) - : TheKind(kind) { - switch (kind) { - case Kind::UnterminatedBranches: { - UnterminatedBranches = std::move(stmts); - break; - } - case Kind::InvalidJumps: { - InvalidJumps = std::move(stmts); - break; - } - default: - llvm_unreachable("Unhandled case in switch!"); - } - } - -public: - static IsSingleValueStmtResult valid() { - return IsSingleValueStmtResult(Kind::Valid); - } - static IsSingleValueStmtResult - unterminatedBranches(TinyPtrVector branches) { - return IsSingleValueStmtResult(Kind::UnterminatedBranches, - std::move(branches)); - } - static IsSingleValueStmtResult nonExhaustiveIf() { - return IsSingleValueStmtResult(Kind::NonExhaustiveIf); - } - static IsSingleValueStmtResult noExpressionBranches() { - return IsSingleValueStmtResult(Kind::NoExpressionBranches); - } - static IsSingleValueStmtResult unhandledStmt() { - return IsSingleValueStmtResult(Kind::UnhandledStmt); - } - static IsSingleValueStmtResult circularReference() { - return IsSingleValueStmtResult(Kind::CircularReference); - } - static IsSingleValueStmtResult invalidJumps(TinyPtrVector jumps) { - return IsSingleValueStmtResult(Kind::InvalidJumps, std::move(jumps)); - } - static IsSingleValueStmtResult hasLabel() { - return IsSingleValueStmtResult(Kind::HasLabel); - } - - Kind getKind() const { return TheKind; } - - /// For an unterminated branch kind, retrieves the branch. - const TinyPtrVector &getUnterminatedBranches() const { - assert(TheKind == Kind::UnterminatedBranches); - return UnterminatedBranches; - } - - /// For an invalid jump kind, retrieves the list of invalid jumps. - const TinyPtrVector &getInvalidJumps() const { - assert(TheKind == Kind::InvalidJumps); - return InvalidJumps; - } - - explicit operator bool() const { - return TheKind == Kind::Valid; - } -}; - -/// Computes whether a given statement can be treated as a SingleValueStmtExpr. -class IsSingleValueStmtRequest - : public SimpleRequest { -public: - using SimpleRequest::SimpleRequest; - -private: - friend SimpleRequest; - - IsSingleValueStmtResult - evaluate(Evaluator &evaluator, const Stmt *stmt) const; - -public: - bool isCached() const { return true; } -}; - class GetTypeWrapperInitializer : public SimpleRequest isForSingleValueStmtBranch() const; - /// Determine whether this locator points directly to a given expression. template bool directlyAt() const { if (auto *expr = getAnchor().dyn_cast()) @@ -915,22 +898,6 @@ 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 - : public StoredIntegerElement<1> { -public: - SingleValueStmtBranch(unsigned exprIdx) - : StoredIntegerElement(ConstraintLocator::SingleValueStmtBranch, - exprIdx) {} - - unsigned getExprBranchIndex() const { return getValue(); } - - static bool classof(const LocatorPathElt *elt) { - return elt->getKind() == ConstraintLocator::SingleValueStmtBranch; - } -}; - class LocatorPathElt::PatternMatch final : public StoredPointerElement { public: PatternMatch(Pattern *pattern) diff --git a/include/swift/Sema/ConstraintLocatorPathElts.def b/include/swift/Sema/ConstraintLocatorPathElts.def index 9d6186351a03e..a7ba0222ecdf6 100644 --- a/include/swift/Sema/ConstraintLocatorPathElts.def +++ b/include/swift/Sema/ConstraintLocatorPathElts.def @@ -251,9 +251,6 @@ 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 declaration introduced by a pattern: name (i.e. `x`) or `_` ABSTRACT_LOCATOR_PATH_ELT(PatternDecl) /// The variable declared by a named pattern. diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 920bb961912c6..1aa66ac894bea 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -1947,19 +1947,12 @@ class SolutionApplicationTarget { /// 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; @@ -2047,25 +2040,14 @@ class SolutionApplicationTarget { public: SolutionApplicationTarget(Expr *expr, DeclContext *dc, ContextualTypePurpose contextualPurpose, - Type convertType, - ConstraintLocator *convertTypeLocator, - bool isDiscarded) + Type convertType, bool isDiscarded) : SolutionApplicationTarget(expr, dc, contextualPurpose, TypeLoc::withoutLoc(convertType), - convertTypeLocator, isDiscarded) {} - - SolutionApplicationTarget(Expr *expr, DeclContext *dc, - ContextualTypePurpose contextualPurpose, - Type convertType, bool isDiscarded) - : SolutionApplicationTarget(expr, dc, contextualPurpose, convertType, - /*convertTypeLocator*/ nullptr, isDiscarded) { - } + isDiscarded) { } SolutionApplicationTarget(Expr *expr, DeclContext *dc, ContextualTypePurpose contextualPurpose, - TypeLoc convertType, - ConstraintLocator *convertTypeLocator, - bool isDiscarded); + TypeLoc convertType, bool isDiscarded); SolutionApplicationTarget(Expr *expr, DeclContext *dc, ExprPattern *pattern, Type patternType) @@ -2292,13 +2274,6 @@ class SolutionApplicationTarget { 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 { @@ -4223,8 +4198,7 @@ class ConstraintSystem { /// Add the appropriate constraint for a contextual conversion. void addContextualConversionConstraint(Expr *expr, Type conversionType, - ContextualTypePurpose purpose, - ConstraintLocator *locator); + ContextualTypePurpose purpose); /// Convenience function to pass an \c ArrayRef to \c addJoinConstraint Type addJoinConstraint(ConstraintLocator *locator, @@ -5080,11 +5054,6 @@ class ConstraintSystem { 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. @@ -5983,22 +5952,6 @@ class ConstraintSystem { 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)> - rewriteTarget); - /// Reorder the disjunctive clauses for a given expression to /// increase the likelihood that a favored constraint will be successfully /// resolved before any others. diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 214e7ceb74a51..4970d5a845cc6 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -2958,13 +2958,6 @@ class PrintExpr : public ExprVisitor { PrintWithColorRAII(OS, ParenthesisColor) << ')'; } - void visitSingleValueStmtExpr(SingleValueStmtExpr *E) { - printCommon(E, "single_value_stmt_expr"); - OS << '\n'; - printRec(E->getStmt(), E->getDeclContext()->getASTContext()); - PrintWithColorRAII(OS, ParenthesisColor) << ')'; - } - void visitOneWayExpr(OneWayExpr *E) { printCommon(E, "one_way_expr"); OS << '\n'; @@ -2988,18 +2981,8 @@ class PrintExpr : public ExprVisitor { void visitTypeJoinExpr(TypeJoinExpr *E) { printCommon(E, "type_join_expr"); - if (auto *var = E->getVar()) { - PrintWithColorRAII(OS, DeclColor) << " var="; - printRec(var); - OS << '\n'; - } - - if (auto *SVE = E->getSingleValueStmtExpr()) { - PrintWithColorRAII(OS, ExprColor) << "single_value_stmt_expr="; - printRec(SVE); - OS << '\n'; - } - + PrintWithColorRAII(OS, DeclColor) << " var="; + printRec(E->getVar()); OS << '\n'; for (auto *member : E->getElements()) { diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 4c4e3bc3dfd59..632ad171753b2 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -4759,10 +4759,6 @@ void PrintAST::visitErasureExpr(ErasureExpr *expr) { void PrintAST::visitKeyPathExpr(KeyPathExpr *expr) { } -void PrintAST::visitSingleValueStmtExpr(SingleValueStmtExpr *expr) { - visit(expr->getStmt()); -} - void PrintAST::visitForceTryExpr(ForceTryExpr *expr) { Printer << "try! "; visit(expr->getSubExpr()); diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index ea4f883c2ef2f..e5f99bba8b2f3 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -470,15 +470,9 @@ class NodeAdder ASTScopeImpl *visitExpr(Expr *expr, ASTScopeImpl *p, ScopeCreator &scopeCreator) { - if (!expr) - return p; - - // If we have a single value statement expression, we expand scopes based - // on the underlying statement. - if (auto *SVE = dyn_cast(expr)) - return visit(SVE->getStmt(), p, scopeCreator); + if (expr) + scopeCreator.addExprToScopeTree(expr, p); - scopeCreator.addExprToScopeTree(expr, p); return p; } }; diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index a2367cbc1ffa8..79470fcfe37ad 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -535,12 +535,6 @@ bool PatternEntryDeclScope::isLabeledStmtLookupTerminator() const { return false; } -bool PatternEntryInitializerScope::isLabeledStmtLookupTerminator() const { - // This is needed for SingleValueStmtExprs, which may be used in bindings, - // and have nested statements. - return false; -} - llvm::SmallVector ASTScopeImpl::lookupLabeledStmts(SourceFile *sourceFile, SourceLoc loc) { // Find the innermost scope from which to start our search. diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index 87934087f08f6..8069da8e260ba 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -33,7 +33,6 @@ #include "swift/AST/ProtocolConformance.h" #include "swift/AST/SourceFile.h" #include "swift/AST/Stmt.h" -#include "swift/AST/TypeCheckRequests.h" #include "swift/AST/TypeRepr.h" #include "swift/Basic/SourceManager.h" #include "swift/Subsystems.h" @@ -1185,31 +1184,6 @@ class Verifier : public ASTWalker { verifyCheckedBase(E); } - void verifyChecked(SingleValueStmtExpr *E) { - using Kind = IsSingleValueStmtResult::Kind; - switch (E->getStmt()->mayProduceSingleValue(Ctx).getKind()) { - case Kind::NoExpressionBranches: - // These are allowed as long as the type is Void. - checkSameType( - E->getType(), Ctx.getVoidType(), - "SingleValueStmtExpr with no expression branches must be Void"); - break; - case Kind::UnterminatedBranches: - case Kind::NonExhaustiveIf: - case Kind::UnhandledStmt: - case Kind::CircularReference: - case Kind::HasLabel: - case Kind::InvalidJumps: - // These should have been diagnosed. - Out << "invalid SingleValueStmtExpr should be been diagnosed:\n"; - E->dump(Out); - Out << "\n"; - abort(); - case Kind::Valid: - break; - } - } - void verifyParsed(AbstractClosureExpr *E) { Type Ty = E->getType(); if (!Ty) diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 5bc9b5f2043e6..317b2fb73c22f 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -1219,15 +1219,6 @@ class Traversal : public ASTVisitorgetStmt())) { - E->setStmt(S); - } else { - return nullptr; - } - return E; - } - Expr *visitOneWayExpr(OneWayExpr *E) { if (auto oldSubExpr = E->getSubExpr()) { if (auto subExpr = doIt(oldSubExpr)) { @@ -1269,12 +1260,10 @@ class Traversal : public ASTVisitorgetVar()) { - if (auto *newVar = dyn_cast(doIt(var))) { - E->setVar(newVar); - } else { - return nullptr; - } + if (auto *newVar = dyn_cast(doIt(E->getVar()))) { + E->setVar(newVar); + } else { + return nullptr; } for (unsigned i = 0, e = E->getNumElements(); i != e; ++i) { diff --git a/lib/AST/CASTBridging.cpp b/lib/AST/CASTBridging.cpp index 180c0606cc268..5503fd134d46e 100644 --- a/lib/AST/CASTBridging.cpp +++ b/lib/AST/CASTBridging.cpp @@ -261,13 +261,6 @@ void *SwiftVarDecl_create(void *ctx, BridgedIdentifier _Nullable nameId, reinterpret_cast(dc)); } -void *SingleValueStmtExpr_createWithWrappedBranches(void *_ctx, void *S, - void *DC, bool mustBeExpr) { - auto &ctx = *static_cast(_ctx); - return SingleValueStmtExpr::createWithWrappedBranches( - ctx, (Stmt *)S, (DeclContext *)DC, mustBeExpr); -} - void *IfStmt_create(void *ctx, void *ifLoc, void *cond, void *_Nullable then, void *_Nullable elseLoc, void *_Nullable elseStmt) { ASTContext &Context = *static_cast(ctx); diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 0cd01ad405bc5..4afe23dbbd6d7 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -335,7 +335,6 @@ ConcreteDeclRef Expr::getReferencedDecl(bool stopAtParenExpr) const { NO_REFERENCE(MagicIdentifierLiteral); NO_REFERENCE(DiscardAssignment); NO_REFERENCE(LazyInitializer); - NO_REFERENCE(SingleValueStmt); SIMPLE_REFERENCE(DeclRef, getDeclRef); SIMPLE_REFERENCE(SuperRef, getSelf); @@ -668,7 +667,6 @@ bool Expr::canAppendPostfixExpression(bool appendingPostfixOperator) const { case ExprKind::MagicIdentifierLiteral: case ExprKind::ObjCSelector: case ExprKind::KeyPath: - case ExprKind::SingleValueStmt: return true; case ExprKind::ObjectLiteral: @@ -994,7 +992,6 @@ bool Expr::isValidParentOfTypeExpr(Expr *typeExpr) const { case ExprKind::KeyPathDot: case ExprKind::OneWay: case ExprKind::Tap: - case ExprKind::SingleValueStmt: case ExprKind::TypeJoin: case ExprKind::MacroExpansion: return false; @@ -1528,15 +1525,6 @@ static ValueDecl *getCalledValue(Expr *E, bool skipFunctionConversions) { return nullptr; } -OpaqueValueExpr *OpaqueValueExpr::createImplicit(ASTContext &ctx, Type Ty, - bool isPlaceholder, - AllocationArena arena) { - auto *mem = - ctx.Allocate(sizeof(OpaqueValueExpr), alignof(OpaqueValueExpr), arena); - return new (mem) OpaqueValueExpr( - /*Range=*/SourceRange(), Ty, isPlaceholder); -} - PropertyWrapperValuePlaceholderExpr * PropertyWrapperValuePlaceholderExpr::create(ASTContext &ctx, SourceRange range, Type ty, Expr *wrappedValue, @@ -2413,119 +2401,6 @@ KeyPathExpr::Component::Component( ? nullptr : indexHashables.data(); } -SingleValueStmtExpr *SingleValueStmtExpr::create(ASTContext &ctx, Stmt *S, - DeclContext *DC) { - return new (ctx) SingleValueStmtExpr(S, DC); -} - -SingleValueStmtExpr *SingleValueStmtExpr::createWithWrappedBranches( - ASTContext &ctx, Stmt *S, DeclContext *DC, bool mustBeExpr) { - auto *SVE = create(ctx, S, DC); - - // Attempt to wrap any branches that can be wrapped. - SmallVector scratch; - for (auto *branch : SVE->getBranches(scratch)) { - auto *BS = dyn_cast(branch); - if (!BS) - continue; - - auto elts = BS->getElements(); - if (elts.size() != 1) - continue; - - auto *S = elts.front().dyn_cast(); - if (!S) - continue; - - if (mustBeExpr) { - // If this must be an expression, we can eagerly wrap any exhaustive if - // and switch branch. - if (auto *IS = dyn_cast(S)) { - if (!IS->isSyntacticallyExhaustive()) - continue; - } else if (!isa(S)) { - continue; - } - } else { - // Otherwise do the semantic checking to verify that we can wrap the - // branch. - if (!S->mayProduceSingleValue(ctx)) - continue; - } - BS->setLastElement( - SingleValueStmtExpr::createWithWrappedBranches(ctx, S, DC, mustBeExpr)); - } - return SVE; -} - -SingleValueStmtExpr * -SingleValueStmtExpr::tryDigOutSingleValueStmtExpr(Expr *E) { - while (true) { - // Look through implicit conversions. - if (auto *ICE = dyn_cast(E)) { - E = ICE->getSubExpr(); - continue; - } - // Look through coercions. - if (auto *CE = dyn_cast(E)) { - E = CE->getSubExpr(); - continue; - } - // Look through try/await (this is invalid, but we'll error on it in - // effect checking). - if (auto *TE = dyn_cast(E)) { - E = TE->getSubExpr(); - continue; - } - if (auto *AE = dyn_cast(E)) { - E = AE->getSubExpr(); - continue; - } - break; - } - return dyn_cast(E); -} - -SourceRange SingleValueStmtExpr::getSourceRange() const { - return S->getSourceRange(); -} - -SingleValueStmtExpr::Kind SingleValueStmtExpr::getStmtKind() const { - switch (getStmt()->getKind()) { - case StmtKind::If: - return Kind::If; - case StmtKind::Switch: - return Kind::Switch; - default: - llvm_unreachable("Unhandled kind!"); - } -} - -ArrayRef -SingleValueStmtExpr::getBranches(SmallVectorImpl &scratch) const { - switch (getStmtKind()) { - case Kind::If: - return cast(getStmt())->getBranches(scratch); - case Kind::Switch: - return cast(getStmt())->getBranches(scratch); - } - llvm_unreachable("Unhandled case in switch!"); -} - -ArrayRef SingleValueStmtExpr::getSingleExprBranches( - SmallVectorImpl &scratch) const { - assert(scratch.empty()); - SmallVector stmtScratch; - for (auto *branch : getBranches(stmtScratch)) { - auto *BS = dyn_cast(branch); - if (!BS) - continue; - if (auto *E = BS->getSingleExpressionElement()) - scratch.push_back(E); - } - return scratch; -} - void InterpolatedStringLiteralExpr::forEachSegment(ASTContext &Ctx, llvm::function_ref callback) { auto appendingExpr = getAppendingExpr(); @@ -2585,19 +2460,9 @@ RegexLiteralExpr::createParsed(ASTContext &ctx, SourceLoc loc, /*implicit*/ false); } -TypeJoinExpr::TypeJoinExpr(llvm::PointerUnion result, - ArrayRef elements, SingleValueStmtExpr *SVE) - : Expr(ExprKind::TypeJoin, /*implicit=*/true, Type()), Var(nullptr), - SVE(SVE) { - - if (auto *varRef = result.dyn_cast()) { - assert(varRef); - Var = varRef; - } else { - auto joinType = Type(result.get()); - assert(joinType && "expected non-null type"); - setType(joinType); - } +TypeJoinExpr::TypeJoinExpr(DeclRefExpr *varRef, ArrayRef elements) + : Expr(ExprKind::TypeJoin, /*implicit=*/true, Type()), Var(varRef) { + assert(Var); Bits.TypeJoinExpr.NumElements = elements.size(); // Copy elements. @@ -2605,20 +2470,11 @@ TypeJoinExpr::TypeJoinExpr(llvm::PointerUnion result, getTrailingObjects()); } -TypeJoinExpr *TypeJoinExpr::createImpl( - ASTContext &ctx, llvm::PointerUnion varOrType, - ArrayRef elements, AllocationArena arena, - SingleValueStmtExpr *SVE) { +TypeJoinExpr *TypeJoinExpr::create(ASTContext &ctx, DeclRefExpr *var, + ArrayRef elements) { size_t size = totalSizeToAlloc(elements.size()); - void *mem = ctx.Allocate(size, alignof(TypeJoinExpr), arena); - return new (mem) TypeJoinExpr(varOrType, elements, SVE); -} - -TypeJoinExpr * -TypeJoinExpr::forBranchesOfSingleValueStmtExpr(ASTContext &ctx, Type joinType, - SingleValueStmtExpr *SVE, - AllocationArena arena) { - return createImpl(ctx, joinType.getPointer(), /*elements*/ {}, arena, SVE); + void *mem = ctx.Allocate(size, alignof(TypeJoinExpr)); + return new (mem) TypeJoinExpr(var, elements); } SourceRange MacroExpansionExpr::getSourceRange() const { diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 6cc0e1e92fed7..078bf433cf542 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -3419,12 +3419,6 @@ void FindLocalVal::visitBraceStmt(BraceStmt *S, bool isTopLevelCode) { } for (auto elem : S->getElements()) { - // If we have a SingleValueStmtExpr, there may be local bindings in the - // wrapped statement. - if (auto *E = elem.dyn_cast()) { - if (auto *SVE = dyn_cast(E)) - visit(SVE->getStmt()); - } if (auto *S = elem.dyn_cast()) visit(S); } diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index 1baccd0bcd143..43100e1b4e919 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -292,22 +292,6 @@ ASTNode BraceStmt::findAsyncNode() { return asyncFinder.getAsyncNode(); } -Expr *BraceStmt::getSingleExpressionElement() const { - if (getElements().size() != 1) - return nullptr; - - return getElements()[0].dyn_cast(); -} - -IsSingleValueStmtResult Stmt::mayProduceSingleValue(Evaluator &eval) const { - return evaluateOrDefault(eval, IsSingleValueStmtRequest{this}, - IsSingleValueStmtResult::circularReference()); -} - -IsSingleValueStmtResult Stmt::mayProduceSingleValue(ASTContext &ctx) const { - return mayProduceSingleValue(ctx.evaluator); -} - SourceLoc ReturnStmt::getStartLoc() const { if (ReturnLoc.isInvalid() && Result) return Result->getStartLoc(); @@ -531,39 +515,6 @@ IfStmt::IfStmt(SourceLoc IfLoc, Expr *Cond, Stmt *Then, SourceLoc ElseLoc, implicit) { } -ArrayRef IfStmt::getBranches(SmallVectorImpl &scratch) const { - assert(scratch.empty()); - scratch.push_back(getThenStmt()); - - auto *elseBranch = getElseStmt(); - while (elseBranch) { - if (auto *IS = dyn_cast(elseBranch)) { - // Look through else ifs. - elseBranch = IS->getElseStmt(); - scratch.push_back(IS->getThenStmt()); - continue; - } - // An unconditional else, we're done. - scratch.push_back(elseBranch); - break; - } - return scratch; -} - -bool IfStmt::isSyntacticallyExhaustive() const { - auto *elseBranch = getElseStmt(); - while (elseBranch) { - // Look through else ifs. - if (auto *IS = dyn_cast(elseBranch)) { - elseBranch = IS->getElseStmt(); - continue; - } - // An unconditional else. - return true; - } - return false; -} - GuardStmt::GuardStmt(SourceLoc GuardLoc, Expr *Cond, BraceStmt *Body, Optional implicit, ASTContext &Ctx) : GuardStmt(GuardLoc, exprToCond(Cond, Ctx), Body, implicit) { @@ -737,14 +688,6 @@ SourceLoc swift::extractNearestSourceLoc(const Stmt *S) { return S->getStartLoc(); } -ArrayRef -SwitchStmt::getBranches(SmallVectorImpl &scratch) const { - assert(scratch.empty()); - for (auto *CS : getCases()) - scratch.push_back(CS->getBody()); - return scratch; -} - // See swift/Basic/Statistic.h for declaration: this enables tracing Stmts, is // defined here to avoid too much layering violation / circular linkage // dependency. diff --git a/lib/ASTGen/Sources/ASTGen/Exprs.swift b/lib/ASTGen/Sources/ASTGen/Exprs.swift index 121ffa0abc80f..26175ce2d74f5 100644 --- a/lib/ASTGen/Sources/ASTGen/Exprs.swift +++ b/lib/ASTGen/Sources/ASTGen/Exprs.swift @@ -62,15 +62,6 @@ extension ASTGenVisitor { return .expr(UnresolvedDotExpr_create(ctx, base, loc, name, loc)) } - public func visit(_ node: IfExprSyntax) -> ASTNode { - let stmt = makeIfStmt(node).rawValue - - // Wrap in a SingleValueStmtExpr to embed as an expression. - let sve = SingleValueStmtExpr_createWithWrappedBranches( - ctx, stmt, declContext, /*mustBeExpr*/ true) - return .expr(sve) - } - public func visit(_ node: TupleExprElementListSyntax) -> ASTNode { let elements = node.map { self.visit($0).rawValue } let labels: [BridgedIdentifier?] = node.map { diff --git a/lib/ASTGen/Sources/ASTGen/Stmts.swift b/lib/ASTGen/Sources/ASTGen/Stmts.swift index 9b8b625974520..e9f0c214c17d0 100644 --- a/lib/ASTGen/Sources/ASTGen/Stmts.swift +++ b/lib/ASTGen/Sources/ASTGen/Stmts.swift @@ -14,7 +14,7 @@ extension ASTGenVisitor { }) } - func makeIfStmt(_ node: IfExprSyntax) -> ASTNode { + public func visit(_ node: IfStmtSyntax) -> ASTNode { let conditions = node.conditions.map { self.visit($0).rawValue } assert(conditions.count == 1) // TODO: handle multiple conditions. @@ -22,22 +22,12 @@ extension ASTGenVisitor { let loc = self.base.advanced(by: node.position.utf8Offset).raw if let elseBody = node.elseBody, node.elseKeyword != nil { - return .stmt(IfStmt_create(ctx, loc, conditions.first!, body, loc, - visit(elseBody).rawValue)) + return .stmt(IfStmt_create(ctx, loc, conditions.first!, body, loc, visit(elseBody).rawValue)) } return .stmt(IfStmt_create(ctx, loc, conditions.first!, body, nil, nil)) } - public func visit(_ node: ExpressionStmtSyntax) -> ASTNode { - switch Syntax(node.expression).as(SyntaxEnum.self) { - case .ifExpr(let e): - return makeIfStmt(e) - default: - fatalError("Unhandled case!") - } - } - public func visit(_ node: ReturnStmtSyntax) -> ASTNode { let loc = self.base.advanced(by: node.position.utf8Offset).raw diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 9f04ac0f8c181..cf426cd70059c 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -499,20 +499,6 @@ ParserResult Parser::parseExprUnary(Diag<> Message, bool isExprBasic) { // First check to see if we have the start of a regex literal `/.../`. tryLexRegexLiteral(/*forUnappliedOperator*/ false); - // Try parse an 'if' or 'switch' as an expression. Note we do this here in - // parseExprUnary as we don't allow postfix syntax to hang off such - // expressions to avoid ambiguities such as postfix '.member', which can - // currently be parsed as a static dot member for a result builder. - if (Tok.isAny(tok::kw_if, tok::kw_switch)) { - auto Result = parseStmt(); - Expr *E = nullptr; - if (Result.isNonNull()) { - E = SingleValueStmtExpr::createWithWrappedBranches( - Context, Result.get(), CurDeclContext, /*mustBeExpr*/ true); - } - return makeParserResult(ParserStatus(Result), E); - } - switch (Tok.getKind()) { default: // If the next token is not an operator, just parse this as expr-postfix. @@ -1043,10 +1029,6 @@ static bool isValidTrailingClosure(bool isExprBasic, Parser &P){ if (P.isStartOfGetSetAccessor()) return false; - // If this is the start of a switch body, this isn't a trailing closure. - if (P.peekToken().is(tok::kw_case)) - return false; - // If this is a normal expression (not an expr-basic) then trailing closures // are allowed, so this is obviously one. // TODO: We could handle try to disambiguate cases like: diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index c26e28ae2ae2f..87071fd61f7e2 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -69,11 +69,6 @@ bool Parser::isStartOfStmt() { case tok::kw_try: { // "try" cannot actually start any statements, but we parse it there for // better recovery in cases like 'try return'. - - // For 'if' and 'switch' we can parse as an expression. - if (peekToken().isAny(tok::kw_if, tok::kw_switch)) { - return false; - } Parser::BacktrackingScope backtrack(*this); consumeToken(tok::kw_try); return isStartOfStmt(); @@ -139,26 +134,8 @@ ParserStatus Parser::parseExprOrStmt(ASTNode &Result) { if (isStartOfStmt()) { ParserResult Res = parseStmt(); - if (Res.isNonNull()) { - auto *S = Res.get(); - - // Special case: An 'if' or 'switch' statement followed by an 'as' must - // be an if/switch expression in a coercion. - // We could also achieve this by more eagerly attempting to parse an 'if' - // or 'switch' as an expression when in statement position, but that - // could result in less useful recovery behavior. - if ((isa(S) || isa(S)) && Tok.is(tok::kw_as)) { - auto *SVE = SingleValueStmtExpr::createWithWrappedBranches( - Context, S, CurDeclContext, /*mustBeExpr*/ true); - auto As = parseExprAs(); - if (As.isParseErrorOrHasCompletion()) - return As; - - Result = SequenceExpr::create(Context, {SVE, As.get(), As.get()}); - } else { - Result = S; - } - } + if (Res.isNonNull()) + Result = Res.get(); return Res; } @@ -746,26 +723,13 @@ ParserResult Parser::parseStmtReturn(SourceLoc tryLoc) { return Result; } - auto isStartOfReturnExpr = [&]() { - if (Tok.isAny(tok::r_brace, tok::semi, tok::eof, tok::pound_if, - tok::pound_error, tok::pound_warning, tok::pound_endif, - tok::pound_else, tok::pound_elseif)) { - return false; - } - // Allowed for if/switch expressions. - if (Tok.isAny(tok::kw_if, tok::kw_switch)) { - return true; - } - if (isStartOfStmt() || isStartOfSwiftDecl()) - return false; - - return true; - }; - // Handle the ambiguity between consuming the expression and allowing the // enclosing stmt-brace to get it by eagerly eating it unless the return is // followed by a '}', ';', statement or decl start keyword sequence. - if (isStartOfReturnExpr()) { + if (Tok.isNot(tok::r_brace, tok::semi, tok::eof, tok::pound_if, + tok::pound_error, tok::pound_warning, tok::pound_endif, + tok::pound_else, tok::pound_elseif) && + !isStartOfStmt() && !isStartOfSwiftDecl()) { SourceLoc ExprLoc = Tok.getLoc(); // Issue a warning when the returned expression is on a different line than diff --git a/lib/Refactoring/Refactoring.cpp b/lib/Refactoring/Refactoring.cpp index f9a1fcd3aa381..5ae548c420f68 100644 --- a/lib/Refactoring/Refactoring.cpp +++ b/lib/Refactoring/Refactoring.cpp @@ -5354,61 +5354,32 @@ struct CallbackClassifier { : Blocks(Blocks), Params(Params), HandledSwitches(HandledSwitches), DiagEngine(DiagEngine), CurrentBlock(&Blocks.SuccessBlock) {} - /// Attempt to apply custom classification logic to a given node, returning - /// \c true if the node was classified, otherwise \c false. - bool tryClassifyNode(ASTNode Node) { - auto *Statement = Node.dyn_cast(); - if (!Statement) - return false; + void classifyNodes(ArrayRef Nodes, SourceLoc endCommentLoc) { + for (auto I = Nodes.begin(), E = Nodes.end(); I < E; ++I) { + auto *Statement = I->dyn_cast(); + if (auto *IS = dyn_cast_or_null(Statement)) { + NodesToPrint TempNodes; + if (auto *BS = dyn_cast(IS->getThenStmt())) { + TempNodes = NodesToPrint::inBraceStmt(BS); + } else { + TempNodes = NodesToPrint({IS->getThenStmt()}, /*commentLocs*/ {}); + } - if (auto *IS = dyn_cast(Statement)) { - NodesToPrint TempNodes; - if (auto *BS = dyn_cast(IS->getThenStmt())) { - TempNodes = NodesToPrint::inBraceStmt(BS); + classifyConditional(IS, IS->getCond(), std::move(TempNodes), + IS->getElseStmt()); + } else if (auto *GS = dyn_cast_or_null(Statement)) { + classifyConditional(GS, GS->getCond(), NodesToPrint(), GS->getBody()); + } else if (auto *SS = dyn_cast_or_null(Statement)) { + classifySwitch(SS); } else { - TempNodes = NodesToPrint({IS->getThenStmt()}, /*commentLocs*/ {}); - } - - classifyConditional(IS, IS->getCond(), std::move(TempNodes), - IS->getElseStmt()); - return true; - } else if (auto *GS = dyn_cast(Statement)) { - classifyConditional(GS, GS->getCond(), NodesToPrint(), GS->getBody()); - return true; - } else if (auto *SS = dyn_cast(Statement)) { - classifySwitch(SS); - return true; - } else if (auto *RS = dyn_cast(Statement)) { - // We can look through an implicit Void return of a SingleValueStmtExpr, - // as that's semantically a statement. - if (RS->hasResult() && RS->isImplicit()) { - auto Ty = RS->getResult()->getType(); - if (Ty && Ty->isVoid()) { - if (auto *SVE = dyn_cast(RS->getResult())) - return tryClassifyNode(SVE->getStmt()); - } + CurrentBlock->addNode(*I); } - } - return false; - } - /// Classify a node, or add the node to the block if it cannot be classified. - /// Returns \c true if there was an error. - bool classifyNode(ASTNode Node) { - auto DidClassify = tryClassifyNode(Node); - if (!DidClassify) - CurrentBlock->addNode(Node); - return DiagEngine.hadAnyError(); - } - - void classifyNodes(ArrayRef Nodes, SourceLoc EndCommentLoc) { - for (auto Node : Nodes) { - auto HadError = classifyNode(Node); - if (HadError) + if (DiagEngine.hadAnyError()) return; } // Make sure to pick up any trailing comments. - CurrentBlock->addPossibleCommentLoc(EndCommentLoc); + CurrentBlock->addPossibleCommentLoc(endCommentLoc); } /// Whether any of the provided ASTNodes have a child expression that force @@ -6783,15 +6754,6 @@ class AsyncConverter : private SourceEntityWalker { } } - // A void SingleValueStmtExpr is semantically more like a statement than - // an expression, so recurse without bumping the expr depth or wrapping in - // continuation. - if (auto *SVE = dyn_cast(E)) { - auto ty = SVE->getType(); - if (!ty || ty->isVoid()) - return true; - } - // We didn't do any special conversion for this expression. If needed, wrap // it in a continuation. wrapScopeInContinationIfNecessary(E); @@ -6809,11 +6771,6 @@ class AsyncConverter : private SourceEntityWalker { } bool walkToExprPost(Expr *E) override { - if (auto *SVE = dyn_cast(E)) { - auto ty = SVE->getType(); - if (!ty || ty->isVoid()) - return true; - } NestedExprCount--; return true; } diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index e9a5bbfc74602..c93158b6edec3 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -39,7 +39,6 @@ #include "swift/AST/ProtocolConformance.h" #include "swift/AST/SubstitutionMap.h" #include "swift/AST/Types.h" -#include "swift/Basic/Defer.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/type_traits.h" #include "swift/SIL/Consumption.h" @@ -510,8 +509,6 @@ namespace { RValue visitAssignExpr(AssignExpr *E, SGFContext C); RValue visitEnumIsCaseExpr(EnumIsCaseExpr *E, SGFContext C); - RValue visitSingleValueStmtExpr(SingleValueStmtExpr *E, SGFContext C); - RValue visitBindOptionalExpr(BindOptionalExpr *E, SGFContext C); RValue visitOptionalEvaluationExpr(OptionalEvaluationExpr *E, SGFContext C); @@ -2125,39 +2122,6 @@ RValue RValueEmitter::visitEnumIsCaseExpr(EnumIsCaseExpr *E, return emitBoolLiteral(SGF, E, selected, C); } -RValue RValueEmitter::visitSingleValueStmtExpr(SingleValueStmtExpr *E, - SGFContext C) { - // A void SingleValueStmtExpr either only has Void expression branches, or - // we've decided that it should have purely statement semantics. In either - // case, we can just emit the statement as-is, and produce the void rvalue. - if (E->getType()->isVoid()) { - SGF.emitStmt(E->getStmt()); - return SGF.emitEmptyTupleRValue(E, C); - } - auto &lowering = SGF.getTypeLowering(E->getType()); - auto resultAddr = SGF.emitTemporaryAllocation(E, lowering.getLoweredType()); - - // This won't give us a useful diagnostic if the result doesn't end up - // initialized ("variable '' used before being initialized"), but it - // will at least catch a potential miscompile when the SIL verifier is - // disabled. - resultAddr = SGF.B.createMarkUninitialized( - E, resultAddr, MarkUninitializedInst::Kind::Var); - KnownAddressInitialization init(resultAddr); - - // Collect the target exprs that will be used for initialization. - SmallVector scratch; - SILGenFunction::SingleValueStmtInitialization initInfo(&init); - for (auto *E : E->getSingleExprBranches(scratch)) - initInfo.Exprs.insert(E); - - // Push the initialization for branches of the statement to initialize into. - SGF.SingleValueStmtInitStack.push_back(std::move(initInfo)); - SWIFT_DEFER { SGF.SingleValueStmtInitStack.pop_back(); }; - SGF.emitStmt(E->getStmt()); - return RValue(SGF, E, SGF.emitManagedRValueWithCleanup(resultAddr)); -} - RValue RValueEmitter::visitCoerceExpr(CoerceExpr *E, SGFContext C) { if (auto result = tryEmitAsBridgingConversion(SGF, E->getSubExpr(), true, C)) return RValue(SGF, E, *result); diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 1eb01b32a79c0..dc6e54ad17eb3 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -1153,18 +1153,6 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, VarDecl *var) { emitEpilog(loc); } -Initialization *SILGenFunction::getSingleValueStmtInit(Expr *E) { - if (SingleValueStmtInitStack.empty()) - return nullptr; - - // Check to see if this is an expression branch of an active - // SingleValueStmtExpr initialization. - if (!SingleValueStmtInitStack.back().Exprs.contains(E)) - return nullptr; - - return SingleValueStmtInitStack.back().Init; -} - void SILGenFunction::emitProfilerIncrement(ASTNode Node) { emitProfilerIncrement(ProfileCounterRef::node(Node)); } diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 925235c022cd1..e4297a7b72edf 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -310,20 +310,6 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction std::vector BreakContinueDestStack; std::vector SwitchStack; - - /// Information for a parent SingleValueStmtExpr initialization. - struct SingleValueStmtInitialization { - /// The target expressions to be used for initialization. - SmallPtrSet Exprs; - Initialization *Init; - - SingleValueStmtInitialization(Initialization *init) : Init(init) {} - }; - - /// A stack of active SingleValueStmtExpr initializations that may be - /// initialized by the branches of a statement. - std::vector SingleValueStmtInitStack; - /// Keep track of our current nested scope. /// /// The boolean tracks whether this is a binding scope, which should be @@ -644,11 +630,6 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction SmallVectorImpl &directResultsBuffer, SmallVectorImpl &cleanups); - /// Check to see if an initalization for a SingleValueStmtExpr is active, and - /// if the provided expression is for one of its branches. If so, returns the - /// initialization to use for the expression. Otherwise returns \c nullptr. - Initialization *getSingleValueStmtInit(Expr *E); - //===--------------------------------------------------------------------===// // Entry points for codegen //===--------------------------------------------------------------------===// diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index f7187eb745d98..d07281e162a56 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -415,16 +415,7 @@ void StmtEmitter::visitBraceStmt(BraceStmt *S) { if (isa(S)) StmtType = ThrowStmtType; } else if (auto *E = ESD.dyn_cast()) { - // Check to see if we have an initialization for a SingleValueStmtExpr - // active, and if so, use it for this expression branch. If the expression - // is uninhabited, we can skip this, and let unreachability checking - // handle it. - auto *init = SGF.getSingleValueStmtInit(E); - if (init && !E->getType()->isStructurallyUninhabited()) { - SGF.emitExprInto(E, init); - } else { - SGF.emitIgnoredExpr(E); - } + SGF.emitIgnoredExpr(E); } else { auto *D = ESD.get(); diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index b685570925265..a71d6e807fe3c 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -364,7 +364,8 @@ class BuilderClosureVisitor } } - void visitBraceElement(ASTNode node, SmallVectorImpl &expressions) { + VarDecl *visitBraceStmt(BraceStmt *braceStmt) { + SmallVector expressions; auto addChild = [&](VarDecl *childVar) { if (!childVar) return; @@ -372,67 +373,58 @@ class BuilderClosureVisitor expressions.push_back(builder.buildVarRef(childVar, childVar->getLoc())); }; - // Implicit returns in single-expression function bodies are treated - // as the expression. - if (auto returnStmt = - dyn_cast_or_null(node.dyn_cast())) { - assert(returnStmt->isImplicit()); - node = returnStmt->getResult(); - } - - if (auto stmt = node.dyn_cast()) { - addChild(visit(stmt)); - return; - } + for (auto node : braceStmt->getElements()) { + // Implicit returns in single-expression function bodies are treated + // as the expression. + if (auto returnStmt = + dyn_cast_or_null(node.dyn_cast())) { + assert(returnStmt->isImplicit()); + node = returnStmt->getResult(); + } - if (auto decl = node.dyn_cast()) { - // Just ignore #if; the chosen children should appear in the - // surrounding context. This isn't good for source tools but it - // at least works. - if (isa(decl)) - return; + if (auto stmt = node.dyn_cast()) { + addChild(visit(stmt)); + continue; + } - // Skip #warning/#error; we'll handle them when applying the builder. - if (isa(decl)) - return; + if (auto decl = node.dyn_cast()) { + // Just ignore #if; the chosen children should appear in the + // surrounding context. This isn't good for source tools but it + // at least works. + if (isa(decl)) + continue; - // Pattern bindings are okay so long as all of the entries are - // initialized. - if (auto patternBinding = dyn_cast(decl)) { - visitPatternBindingDecl(patternBinding); - return; - } + // Skip #warning/#error; we'll handle them when applying the builder. + if (isa(decl)) { + continue; + } - // Ignore variable declarations, because they're always handled within - // their enclosing pattern bindings. - if (isa(decl)) - return; + // Pattern bindings are okay so long as all of the entries are + // initialized. + if (auto patternBinding = dyn_cast(decl)) { + visitPatternBindingDecl(patternBinding); + continue; + } - if (!unhandledNode) - unhandledNode = decl; + // Ignore variable declarations, because they're always handled within + // their enclosing pattern bindings. + if (isa(decl)) + continue; - return; - } + if (!unhandledNode) + unhandledNode = decl; - auto expr = node.get(); - if (auto *SVE = dyn_cast(expr)) { - // This should never be treated as an expression in a result builder, it - // should have statement semantics. - visitBraceElement(SVE->getStmt(), expressions); - return; - } - if (cs && builder.supports(ctx.Id_buildExpression)) { - expr = buildCallIfWanted(expr->getLoc(), ctx.Id_buildExpression, - {expr}, {Identifier()}); - } + continue; + } - addChild(captureExpr(expr, /*oneWay=*/true, node.get())); - } + auto expr = node.get(); + if (cs && builder.supports(ctx.Id_buildExpression)) { + expr = buildCallIfWanted(expr->getLoc(), ctx.Id_buildExpression, + { expr }, { Identifier() }); + } - VarDecl *visitBraceStmt(BraceStmt *braceStmt) { - SmallVector expressions; - for (auto node : braceStmt->getElements()) - visitBraceElement(node, expressions); + addChild(captureExpr(expr, /*oneWay=*/true, node.get())); + } if (!cs || hadError) return nullptr; @@ -1062,12 +1054,6 @@ class ResultBuilderTransform } auto *expr = element.get(); - if (auto *SVE = dyn_cast(expr)) { - // This should never be treated as an expression in a result builder, it - // should have statement semantics. - return transformBraceElement(SVE->getStmt(), newBody, - buildBlockArguments); - } if (builder.supports(ctx.Id_buildExpression)) { expr = builder.buildCall(expr->getLoc(), ctx.Id_buildExpression, {expr}, {Identifier()}); @@ -1776,118 +1762,107 @@ class BuilderClosureRewriter solution(solution), dc(dc), builderTransform(builderTransform), rewriteTarget(rewriteTarget) { } - /// Visit the element of a brace statement, returning \c false if the element - /// was rewritten successfully, or \c true if there was an error. - bool visitBraceElement(ASTNode node, std::vector &newElements) { - // Implicit returns in single-expression function bodies are treated - // as the expression. - if (auto returnStmt = - dyn_cast_or_null(node.dyn_cast())) { - assert(returnStmt->isImplicit()); - node = returnStmt->getResult(); + NullablePtr + visitBraceStmt(BraceStmt *braceStmt, ResultBuilderTarget target, + Optional innerTarget = None) { + std::vector newElements; + + // If there is an "inner" target corresponding to this brace, declare + // it's temporary variable if needed. + if (innerTarget) { + declareTemporaryVariable(innerTarget->captured.first, newElements); } - if (auto expr = node.dyn_cast()) { - if (auto *SVE = dyn_cast(expr)) { - // This should never be treated as an expression in a result builder, it - // should have statement semantics. - return visitBraceElement(SVE->getStmt(), newElements); + for (auto node : braceStmt->getElements()) { + // Implicit returns in single-expression function bodies are treated + // as the expression. + if (auto returnStmt = + dyn_cast_or_null(node.dyn_cast())) { + assert(returnStmt->isImplicit()); + node = returnStmt->getResult(); } - // Skip error expressions. - if (isa(expr)) - return false; - // Each expression turns into a 'let' that captures the value of - // the expression. - auto recorded = takeCapturedExpr(expr); + if (auto expr = node.dyn_cast()) { + // Skip error expressions. + if (isa(expr)) + continue; - // Rewrite the expression - Expr *finalExpr = rewriteExpr(recorded.generatedExpr); + // Each expression turns into a 'let' that captures the value of + // the expression. + auto recorded = takeCapturedExpr(expr); - // Form a new pattern binding to bind the temporary variable to the - // transformed expression. - declareTemporaryVariable(recorded.temporaryVar, newElements, finalExpr); - return false; - } + // Rewrite the expression + Expr *finalExpr = rewriteExpr(recorded.generatedExpr); - if (auto stmt = node.dyn_cast()) { - // "throw" statements produce no value. Transform them directly. - if (auto throwStmt = dyn_cast(stmt)) { - if (auto newStmt = visitThrowStmt(throwStmt)) { - newElements.push_back(newStmt.get()); - } - return false; + // Form a new pattern binding to bind the temporary variable to the + // transformed expression. + declareTemporaryVariable(recorded.temporaryVar, newElements, finalExpr); + continue; } - // Each statement turns into a (potential) temporary variable - // binding followed by the statement itself. - auto captured = takeCapturedStmt(stmt); - - declareTemporaryVariable(captured.first, newElements); + if (auto stmt = node.dyn_cast()) { + // "throw" statements produce no value. Transform them directly. + if (auto throwStmt = dyn_cast(stmt)) { + if (auto newStmt = visitThrowStmt(throwStmt)) { + newElements.push_back(newStmt.get()); + } + continue; + } - auto finalStmt = - visit(stmt, ResultBuilderTarget{ResultBuilderTarget::TemporaryVar, - std::move(captured)}); + // Each statement turns into a (potential) temporary variable + // binding followed by the statement itself. + auto captured = takeCapturedStmt(stmt); - // Re-write of statements that envolve type-checking - // could fail, such a failure terminates the walk. - if (!finalStmt) - return true; + declareTemporaryVariable(captured.first, newElements); - newElements.push_back(finalStmt.get()); - return false; - } + auto finalStmt = visit( + stmt, + ResultBuilderTarget{ResultBuilderTarget::TemporaryVar, + std::move(captured)}); - auto decl = node.get(); + // Re-write of statements that envolve type-checking + // could fail, such a failure terminates the walk. + if (!finalStmt) + return nullptr; - // Skip #if declarations. - if (isa(decl)) { - newElements.push_back(decl); - return false; - } + newElements.push_back(finalStmt.get()); + continue; + } - // Diagnose #warning / #error during application. - if (auto poundDiag = dyn_cast(decl)) { - TypeChecker::typeCheckDecl(poundDiag); - newElements.push_back(decl); - return false; - } + auto decl = node.get(); - // Skip variable declarations; they're always part of a pattern - // binding. - if (isa(decl)) { - TypeChecker::typeCheckDecl(decl); - newElements.push_back(decl); - return false; - } - - // Handle pattern bindings. - if (auto patternBinding = dyn_cast(decl)) { - auto resultTarget = - rewriteTarget(SolutionApplicationTarget{patternBinding}); - assert(resultTarget.has_value() && - "Could not rewrite pattern binding entries!"); - TypeChecker::typeCheckDecl(resultTarget->getAsPatternBinding()); - newElements.push_back(resultTarget->getAsPatternBinding()); - return false; - } + // Skip #if declarations. + if (isa(decl)) { + newElements.push_back(decl); + continue; + } - llvm_unreachable("Cannot yet handle declarations"); - } + // Diagnose #warning / #error during application. + if (auto poundDiag = dyn_cast(decl)) { + TypeChecker::typeCheckDecl(poundDiag); + newElements.push_back(decl); + continue; + } - NullablePtr - visitBraceStmt(BraceStmt *braceStmt, ResultBuilderTarget target, - Optional innerTarget = None) { - std::vector newElements; + // Skip variable declarations; they're always part of a pattern + // binding. + if (isa(decl)) { + TypeChecker::typeCheckDecl(decl); + newElements.push_back(decl); + continue; + } - // If there is an "inner" target corresponding to this brace, declare - // it's temporary variable if needed. - if (innerTarget) - declareTemporaryVariable(innerTarget->captured.first, newElements); + // Handle pattern bindings. + if (auto patternBinding = dyn_cast(decl)) { + auto resultTarget = rewriteTarget(SolutionApplicationTarget{patternBinding}); + assert(resultTarget.has_value() + && "Could not rewrite pattern binding entries!"); + TypeChecker::typeCheckDecl(resultTarget->getAsPatternBinding()); + newElements.push_back(resultTarget->getAsPatternBinding()); + continue; + } - for (auto node : braceStmt->getElements()) { - if (visitBraceElement(node, newElements)) - return nullptr; + llvm_unreachable("Cannot yet handle declarations"); } // If there is an "inner" target corresponding to this brace, initialize diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index f494c0918fea9..fab19bf723a0d 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -5370,10 +5370,6 @@ namespace { llvm_unreachable("found KeyPathDotExpr in CSApply"); } - Expr *visitSingleValueStmtExpr(SingleValueStmtExpr *E) { - llvm_unreachable("Handled by the walker directly"); - } - Expr *visitOneWayExpr(OneWayExpr *E) { auto type = simplifyType(cs.getType(E)); return coerceToType(E->getSubExpr(), type, cs.getConstraintLocator(E)); @@ -8525,12 +8521,7 @@ namespace { return Action::SkipChildren(closure); } - if (auto *SVE = dyn_cast(expr)) { - rewriteSingleValueStmtExpr(SVE); - return Action::SkipChildren(SVE); - } - - if (auto tap = dyn_cast(expr)) { + if (auto tap = dyn_cast_or_null(expr)) { // We remember the DeclContext because the code to handle // single-expression-body closures above changes it. TapsToTypeCheck.push_back(std::make_pair(tap, Rewriter.dc)); @@ -8649,36 +8640,6 @@ namespace { } } } - - bool rewriteSingleValueStmtExpr(SingleValueStmtExpr *SVE) { - auto &solution = Rewriter.solution; - auto resultTy = solution.getResolvedType(SVE); - Rewriter.cs.setType(SVE, resultTy); - - SmallVector scratch; - SmallPtrSet exprBranches; - for (auto *branch : SVE->getSingleExprBranches(scratch)) - exprBranches.insert(branch); - - return Rewriter.cs.applySolutionToSingleValueStmt( - solution, SVE, solution.getDC(), [&](SolutionApplicationTarget target) { - // We need to fixup the conversion type to the full result type, - // not the branch result type. This is necessary as there may be - // an additional conversion required for the branch. - if (auto *E = target.getAsExpr()) { - if (exprBranches.contains(E)) - target.setExprConversionType(resultTy); - } - auto resultTarget = rewriteTarget(target); - if (!resultTarget) - return resultTarget; - - if (auto expr = resultTarget->getAsExpr()) - solution.setExprTypes(expr); - - return resultTarget; - }); - } }; } // end anonymous namespace @@ -9135,27 +9096,26 @@ ExprWalker::rewriteTarget(SolutionApplicationTarget target) { case CTP_ExprPattern: case CTP_ForEachStmt: case CTP_ForEachSequence: - case CTP_ReturnSingleExpr: - case CTP_YieldByValue: - case CTP_YieldByReference: - case CTP_ThrowStmt: - case CTP_EnumCaseRawValue: - case CTP_DefaultParameter: - case CTP_AutoclosureDefaultParameter: - case CTP_CalleeResult: - case CTP_CallArgument: - case CTP_ClosureResult: - case CTP_ArrayElement: - case CTP_DictionaryKey: - case CTP_DictionaryValue: - case CTP_CoerceOperand: - case CTP_AssignSource: - case CTP_SubscriptAssignSource: - case CTP_Condition: - case CTP_WrappedProperty: - case CTP_ComposedPropertyWrapper: - case CTP_CannotFail: - case CTP_SingleValueStmtBranch: + case swift::CTP_ReturnSingleExpr: + case swift::CTP_YieldByValue: + case swift::CTP_YieldByReference: + case swift::CTP_ThrowStmt: + case swift::CTP_EnumCaseRawValue: + case swift::CTP_DefaultParameter: + case swift::CTP_AutoclosureDefaultParameter: + case swift::CTP_CalleeResult: + case swift::CTP_CallArgument: + case swift::CTP_ClosureResult: + case swift::CTP_ArrayElement: + case swift::CTP_DictionaryKey: + case swift::CTP_DictionaryValue: + case swift::CTP_CoerceOperand: + case swift::CTP_AssignSource: + case swift::CTP_SubscriptAssignSource: + case swift::CTP_Condition: + case swift::CTP_WrappedProperty: + case swift::CTP_ComposedPropertyWrapper: + case swift::CTP_CannotFail: result.setExpr(rewrittenExpr); break; } @@ -9369,50 +9329,37 @@ ExprWalker::rewriteTarget(SolutionApplicationTarget target) { // expression function which got deduced to be `Never`. Type convertType = target.getExprConversionType(); auto shouldCoerceToContextualType = [&]() { - if (!convertType) - return false; - - if (convertType->hasPlaceholder()) - return false; - - if (target.isOptionalSomePatternInit()) - return false; - - if (solution.getResolvedType(resultExpr)->isUninhabited() || - solution.simplifyType(convertType)->isVoid()) { - auto contextPurpose = cs.getContextualTypePurpose(target.getAsExpr()); - if (contextPurpose == CTP_ReturnSingleExpr || - contextPurpose == CTP_SingleValueStmtBranch) { - return false; - } - } - return true; + return convertType && + !convertType->hasPlaceholder() && + !target.isOptionalSomePatternInit() && + !(solution.getResolvedType(resultExpr)->isUninhabited() && + cs.getContextualTypePurpose(target.getAsExpr()) + == CTP_ReturnSingleExpr); }; // If we're supposed to convert the expression to some particular type, // do so now. if (shouldCoerceToContextualType()) { - auto contextualTypePurpose = target.getExprContextualTypePurpose(); + ConstraintLocator *locator = nullptr; - auto *locator = target.getExprConvertTypeLocator(); - if (!locator) { - // Bodies of single-expression closures use a special locator - // for contextual type conversion to make sure that result is - // convertible to `Void` when `return` is not used explicitly. - auto *closure = dyn_cast(target.getDeclContext()); - if (closure && closure->hasSingleExpressionBody() && - contextualTypePurpose == CTP_ClosureResult) { - auto *returnStmt = - castToStmt(closure->getBody()->getLastElement()); - - locator = cs.getConstraintLocator( - closure, LocatorPathElt::ClosureBody( - /*hasReturn=*/!returnStmt->isImplicit())); - } else { - locator = cs.getConstraintLocator( - expr, LocatorPathElt::ContextualType(contextualTypePurpose)); - } + auto contextualTypePurpose = target.getExprContextualTypePurpose(); + // Bodies of single-expression closures use a special locator + // for contextual type conversion to make sure that result is + // convertible to `Void` when `return` is not used explicitly. + auto *closure = dyn_cast(target.getDeclContext()); + if (closure && closure->hasSingleExpressionBody() && + contextualTypePurpose == CTP_ClosureResult) { + auto *returnStmt = + castToStmt(closure->getBody()->getLastElement()); + + locator = cs.getConstraintLocator( + closure, LocatorPathElt::ClosureBody( + /*hasReturn=*/!returnStmt->isImplicit())); + } else { + locator = cs.getConstraintLocator( + expr, LocatorPathElt::ContextualType(contextualTypePurpose)); } + assert(locator); resultExpr = Rewriter.coerceToType( diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index c881b2bffdb93..5189a15fee835 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -725,7 +725,6 @@ Optional> GenericArgumentsMismatchFailure::getDiagnosticFor( case CTP_CalleeResult: case CTP_EnumCaseRawValue: case CTP_ExprPattern: - case CTP_SingleValueStmtBranch: break; } return None; @@ -2464,10 +2463,7 @@ bool ContextualFailure::diagnoseAsError() { diagnostic = diag::ternary_expr_cases_mismatch; break; } - case ConstraintLocator::SingleValueStmtBranch: { - diagnostic = diag::single_value_stmt_branches_mismatch; - break; - } + case ConstraintLocator::ContextualType: { if (diagnoseConversionToBool()) return true; @@ -2667,7 +2663,6 @@ getContextualNilDiagnostic(ContextualTypePurpose CTP) { case CTP_WrappedProperty: case CTP_ComposedPropertyWrapper: case CTP_ExprPattern: - case CTP_SingleValueStmtBranch: return None; case CTP_EnumCaseRawValue: @@ -3422,9 +3417,6 @@ ContextualFailure::getDiagnosticFor(ContextualTypePurpose context, case CTP_WrappedProperty: return diag::wrapped_value_mismatch; - case CTP_SingleValueStmtBranch: - return diag::cannot_convert_initializer_value; - case CTP_CaseStmt: case CTP_ThrowStmt: case CTP_ForEachStmt: diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 8e4fe561a2f4f..2b7b5bb8652d2 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3658,10 +3658,6 @@ namespace { llvm_unreachable("found KeyPathDotExpr in CSGen"); } - Type visitSingleValueStmtExpr(SingleValueStmtExpr *E) { - llvm_unreachable("Handled by the walker directly"); - } - Type visitOneWayExpr(OneWayExpr *expr) { auto locator = CS.getConstraintLocator(expr); auto resultTypeVar = CS.createTypeVariable(locator, 0); @@ -3691,42 +3687,12 @@ namespace { SmallVector, 4> elements; elements.reserve(expr->getNumElements()); - if (auto *SVE = expr->getSingleValueStmtExpr()) { - // If we have a SingleValueStmtExpr, form a join of the branch types. - SmallVector scratch; - auto branches = SVE->getSingleExprBranches(scratch); - for (auto idx : indices(branches)) { - auto *eltLoc = CS.getConstraintLocator( - SVE, {LocatorPathElt::SingleValueStmtBranch(idx)}); - elements.emplace_back(CS.getType(branches[idx]), eltLoc); - } - } else { - for (auto *element : expr->getElements()) { - elements.emplace_back(CS.getType(element), - CS.getConstraintLocator(element)); - } - } - - Type resultTy; - - if (auto *var = expr->getVar()) { - resultTy = CS.getType(var); - } else { - resultTy = expr->getType(); - } - - assert(resultTy); - - // If we have a single branch of a SingleValueStmtExpr, we want a - // conversion of the result, not a join, which would skip the conversion. - // This is needed to ensure we apply the Void/Never conversions. - if (elements.size() == 1 && expr->getSingleValueStmtExpr()) { - auto &elt = elements[0]; - CS.addConstraint(ConstraintKind::Conversion, elt.first, - resultTy, elt.second); - return resultTy; + for (auto *element : expr->getElements()) { + elements.emplace_back(CS.getType(element), + CS.getConstraintLocator(element)); } + auto resultTy = CS.getType(expr->getVar()); // The type of a join expression is obtained by performing // a "join-meet" operation on deduced types of its elements // and the underlying variable. @@ -3977,12 +3943,6 @@ namespace { return Action::SkipChildren(expr); } - if (auto *SVE = dyn_cast(expr)) { - if (CS.generateConstraints(SVE)) - return Action::Stop(); - return Action::SkipChildren(expr); - } - // Note that the subexpression of a #selector expression is // unevaluated. if (auto sel = dyn_cast(expr)) { @@ -4429,14 +4389,8 @@ bool ConstraintSystem::generateConstraints( // constraint. if (Type convertType = target.getExprConversionType()) { ContextualTypePurpose ctp = target.getExprContextualTypePurpose(); - - // If a custom locator wasn't specified, create a locator anchored on - // the expression itself. - auto *convertTypeLocator = target.getExprConvertTypeLocator(); - if (!convertTypeLocator) { - convertTypeLocator = - getConstraintLocator(expr, LocatorPathElt::ContextualType(ctp)); - } + auto *convertTypeLocator = + getConstraintLocator(expr, LocatorPathElt::ContextualType(ctp)); auto getLocator = [&](Type ty) -> ConstraintLocator * { // If we have a placeholder originating from a PlaceholderTypeRepr, @@ -4474,8 +4428,7 @@ bool ConstraintSystem::generateConstraints( }); } - addContextualConversionConstraint(expr, convertType, ctp, - convertTypeLocator); + addContextualConversionConstraint(expr, convertType, ctp); } // For an initialization target, generate constraints for the pattern. diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 1156423d4fd53..89b0c3c51bef2 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -6234,17 +6234,13 @@ bool ConstraintSystem::repairFailures( break; } - case ConstraintLocator::TernaryBranch: - case ConstraintLocator::SingleValueStmtBranch: { + case ConstraintLocator::TernaryBranch: { recordAnyTypeVarAsPotentialHole(lhs); recordAnyTypeVarAsPotentialHole(rhs); - if (lhs->hasPlaceholder() || rhs->hasPlaceholder()) - return true; - - // If there's a contextual type, let's consider it the source of truth and - // produce a contextual mismatch instead of per-branch failure, because - // it's a better pointer than potential then-to-else type mismatch. + // If `if` expression has a contextual type, let's consider it a source of + // truth and produce a contextual mismatch instead of per-branch failure, + // because it's a better pointer than potential then-to-else type mismatch. if (auto contextualType = getContextualType(anchor, /*forConstraint=*/false)) { auto purpose = getContextualTypePurpose(anchor); @@ -6266,7 +6262,7 @@ bool ConstraintSystem::repairFailures( } // If there is no contextual type, this is most likely a contextual type - // mismatch between the branches. + // mismatch between then/else branches of ternary operator. conversionsOrFixes.push_back(ContextualMismatch::create( *this, lhs, rhs, getConstraintLocator(locator))); break; @@ -7389,29 +7385,6 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, return getTypeMatchSuccess(); } } - - // We also need to propagate this conversion into the branches for single - // value statements. - // - // As with the previous checks, we only allow the Void conversion in - // an implicit single-expression closure. In the more general case, we - // only allow the Never conversion. - auto *loc = getConstraintLocator(locator); - if (auto branchKind = loc->isForSingleValueStmtBranch()) { - bool allowConversion = false; - switch (*branchKind) { - case SingleValueStmtBranchKind::Regular: - allowConversion = type1->isUninhabited(); - break; - case SingleValueStmtBranchKind::InSingleExprClosure: - allowConversion = true; - break; - } - if (allowConversion) { - increaseScore(SK_FunctionConversion); - return getTypeMatchSuccess(); - } - } } } @@ -14089,13 +14062,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( if (branchElt->forElse()) impact = 10; } - using SingleValueStmtBranch = LocatorPathElt::SingleValueStmtBranch; - if (auto branchElt = locator->getLastElementAs()) { - // Similar to a ternary, except we have N branches. Let's prefer the fix - // on the first branch, and discount subsequent branches by index. - if (branchElt->getExprBranchIndex() > 0) - impact = 9 + branchElt->getExprBranchIndex(); - } + // Increase impact of invalid conversions to `Any` and `AnyHashable` // associated with collection elements (i.e. for-in sequence element) // because it means that other side is structurally incompatible. @@ -14517,8 +14484,7 @@ void ConstraintSystem::addConstraint(ConstraintKind kind, Type first, } void ConstraintSystem::addContextualConversionConstraint( - Expr *expr, Type conversionType, ContextualTypePurpose purpose, - ConstraintLocator *locator) { + Expr *expr, Type conversionType, ContextualTypePurpose purpose) { if (conversionType.isNull()) return; @@ -14571,13 +14537,14 @@ void ConstraintSystem::addContextualConversionConstraint( case CTP_WrappedProperty: case CTP_ComposedPropertyWrapper: case CTP_ExprPattern: - case CTP_SingleValueStmtBranch: break; } // Add the constraint. - auto openedType = openOpaqueType(conversionType, purpose, locator); - addConstraint(constraintKind, getType(expr), openedType, locator, + auto *convertTypeLocator = + getConstraintLocator(expr, LocatorPathElt::ContextualType(purpose)); + auto openedType = openOpaqueType(conversionType, purpose, convertTypeLocator); + addConstraint(constraintKind, getType(expr), openedType, convertTypeLocator, /*isFavored*/ true); } diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 4e864390ef3d4..7c60c517835dc 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -991,11 +991,6 @@ void ConstraintSystem::shrink(Expr *expr) { return Action::SkipChildren(expr); } - // Same as TapExpr and ClosureExpr, we'll handle SingleValueStmtExprs - // separately. - if (isa(expr)) - return Action::SkipChildren(expr); - if (auto coerceExpr = dyn_cast(expr)) { if (coerceExpr->isLiteralInit()) ApplyExprs.push_back({coerceExpr, 1}); diff --git a/lib/Sema/CSStep.cpp b/lib/Sema/CSStep.cpp index fb33bba9f1749..107623b949959 100644 --- a/lib/Sema/CSStep.cpp +++ b/lib/Sema/CSStep.cpp @@ -1091,41 +1091,3 @@ void ConjunctionStep::restoreOuterState(const Score &solutionScore) const { constraint.setActive(true); } } - -void ConjunctionStep::SolverSnapshot::applySolution(const Solution &solution) { - CS.applySolution(solution); - - if (!CS.shouldAttemptFixes()) - return; - - // If inference succeeded, we are done. - auto score = solution.getFixedScore(); - if (score.Data[SK_Fix] == 0) - return; - - auto holeify = [&](Type componentTy) { - if (auto *typeVar = componentTy->getAs()) { - CS.assignFixedType( - typeVar, PlaceholderType::get(CS.getASTContext(), typeVar)); - } - }; - - // If this conjunction represents a closure and inference - // has failed, let's bind all of unresolved type variables - // in its interface type to holes to avoid extraneous - // fixes produced by outer context. - auto locator = Conjunction->getLocator(); - if (locator->directlyAt()) { - auto closureTy = - CS.getClosureType(castToExpr(locator->getAnchor())); - - CS.simplifyType(closureTy).visit(holeify); - } - - // Same for a SingleValueStmtExpr, turn any unresolved type variables present - // in its type into holes. - if (locator->isForSingleValueStmtConjunction()) { - auto *SVE = castToExpr(locator->getAnchor()); - CS.simplifyType(CS.getType(SVE)).visit(holeify); - } -} diff --git a/lib/Sema/CSStep.h b/lib/Sema/CSStep.h index d358a66156f9f..5138412100741 100644 --- a/lib/Sema/CSStep.h +++ b/lib/Sema/CSStep.h @@ -895,7 +895,35 @@ class ConjunctionStep : public BindingStep { CG.addConstraint(&constraint); } - void applySolution(const Solution &solution); + void applySolution(const Solution &solution) { + CS.applySolution(solution); + + if (!CS.shouldAttemptFixes()) + return; + + // If inference succeeded, we are done. + auto score = solution.getFixedScore(); + if (score.Data[SK_Fix] == 0) + return; + + // If this conjunction represents a closure and inference + // has failed, let's bind all of unresolved type variables + // in its interface type to holes to avoid extraneous + // fixes produced by outer context. + + auto locator = Conjunction->getLocator(); + if (locator->directlyAt()) { + auto closureTy = + CS.getClosureType(castToExpr(locator->getAnchor())); + + CS.simplifyType(closureTy).visit([&](Type componentTy) { + if (auto *typeVar = componentTy->getAs()) { + CS.assignFixedType( + typeVar, PlaceholderType::get(CS.getASTContext(), typeVar)); + } + }); + } + } }; /// Best solution solver reached so far. diff --git a/lib/Sema/CSSyntacticElement.cpp b/lib/Sema/CSSyntacticElement.cpp index a44ffd992c424..4d59d2bbc5b1d 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -49,11 +49,9 @@ class TypeVariableRefFinder : public ASTWalker { public: TypeVariableRefFinder( - ConstraintSystem &cs, ASTNode parent, ContextualTypeInfo context, + ConstraintSystem &cs, ASTNode parent, llvm::SmallPtrSetImpl &referencedVars) : CS(cs), Parent(parent), ReferencedVars(referencedVars) { - if (auto ty = context.getType()) - inferVariables(ty); if (auto *closure = getAsExpr(Parent)) ClosureDCs.push_back(closure); } @@ -63,14 +61,6 @@ class TypeVariableRefFinder : public ASTWalker { ClosureDCs.push_back(closure); } - if (auto *joinExpr = dyn_cast(expr)) { - // If this join is over a known type, let's - // analyze it too because it can contain type - // variables. - if (!joinExpr->getVar()) - inferVariables(joinExpr->getType()); - } - if (auto *DRE = dyn_cast(expr)) { auto *decl = DRE->getDecl(); @@ -227,24 +217,24 @@ class TypeVariableRefFinder : public ASTWalker { } }; -/// Find any references to not yet resolved outer VarDecls (including closure -/// parameters) used in the body of the inner closure. This is required because +/// Find any references to not yet resolved outer closure parameters +/// used in the body of the inner closure. This is required because /// isolated conjunctions, just like single-expression closures, have /// to be connected to type variables they are going to use, otherwise /// they'll get placed in a separate solver component and would never /// produce a solution. -class UnresolvedVarCollector : public ASTWalker { +class UnresolvedClosureParameterCollector : public ASTWalker { ConstraintSystem &CS; llvm::SmallSetVector Vars; public: - UnresolvedVarCollector(ConstraintSystem &cs) : CS(cs) {} + UnresolvedClosureParameterCollector(ConstraintSystem &cs) : CS(cs) {} PreWalkResult walkToExprPre(Expr *expr) override { if (auto *DRE = dyn_cast(expr)) { auto *decl = DRE->getDecl(); - if (isa(decl)) { + if (isa(decl)) { if (auto type = CS.getTypeIfAvailable(decl)) { if (auto *typeVar = type->getAs()) { Vars.insert(typeVar); @@ -333,16 +323,7 @@ static void createConjunction(ConstraintSystem &cs, isIsolated = true; } - if (locator->isForSingleValueStmtConjunction()) { - auto *SVE = castToExpr(locator->getAnchor()); - referencedVars.push_back(cs.getType(SVE)->castTo()); - - // Single value statement conjunctions are always isolated, as we want to - // solve the branches independently of the rest of the system. - isIsolated = true; - } - - UnresolvedVarCollector paramCollector(cs); + UnresolvedClosureParameterCollector paramCollector(cs); for (const auto &entry : elements) { ASTNode element = std::get<0>(entry); @@ -383,23 +364,11 @@ ElementInfo makeElement(ASTNode node, ConstraintLocator *locator, return std::make_tuple(node, context, isDiscarded, locator); } -ElementInfo makeJoinElement(ConstraintSystem &cs, TypeJoinExpr *join, - ConstraintLocator *locator) { - - return makeElement( - join, cs.getConstraintLocator(locator, - {LocatorPathElt::SyntacticElement(join)})); -} - struct SyntacticElementContext - : public llvm::PointerUnion { + : public llvm::PointerUnion { // Inherit the constructors from PointerUnion. using PointerUnion::PointerUnion; - /// A join that should be applied to the elements of a SingleValueStmtExpr. - NullablePtr ElementJoin; - static SyntacticElementContext forFunctionRef(AnyFunctionRef ref) { if (auto *decl = ref.getAbstractFunctionDecl()) { return {decl}; @@ -416,21 +385,11 @@ struct SyntacticElementContext return {func}; } - static SyntacticElementContext - forSingleValueStmtExpr(SingleValueStmtExpr *SVE, - TypeJoinExpr *Join = nullptr) { - auto context = SyntacticElementContext{SVE}; - context.ElementJoin = Join; - return context; - } - DeclContext *getAsDeclContext() const { if (auto *fn = this->dyn_cast()) { return fn; } else if (auto *closure = this->dyn_cast()) { return closure; - } else if (auto *SVE = dyn_cast()) { - return SVE->getDeclContext(); } else { llvm_unreachable("unsupported kind"); } @@ -459,13 +418,11 @@ struct SyntacticElementContext } } - Stmt *getStmt() const { + BraceStmt *getBody() const { if (auto *fn = this->dyn_cast()) { return fn->getBody(); } else if (auto *closure = this->dyn_cast()) { return closure->getBody(); - } else if (auto *SVE = dyn_cast()) { - return SVE->getStmt(); } else { llvm_unreachable("unsupported kind"); } @@ -769,12 +726,17 @@ class SyntacticElementConstraintGenerator // Other declarations will be handled at application time. } - // These statements don't require any type-checking. - void visitBreakStmt(BreakStmt *breakStmt) {} - void visitContinueStmt(ContinueStmt *continueStmt) {} - void visitDeferStmt(DeferStmt *deferStmt) {} - void visitFallthroughStmt(FallthroughStmt *fallthroughStmt) {} - void visitFailStmt(FailStmt *fail) {} + void visitBreakStmt(BreakStmt *breakStmt) { + } + + void visitContinueStmt(ContinueStmt *continueStmt) { + } + + void visitDeferStmt(DeferStmt *deferStmt) { + } + + void visitFallthroughStmt(FallthroughStmt *fallthroughStmt) { + } void visitStmtCondition(LabeledConditionalStmt *S, SmallVectorImpl &elements, @@ -805,10 +767,6 @@ class SyntacticElementConstraintGenerator elements.push_back(makeElement(ifStmt->getElseStmt(), elseLoc)); } - // Inject a join if we have one. - if (auto *join = context.ElementJoin.getPtrOrNull()) - elements.push_back(makeJoinElement(cs, join, locator)); - createConjunction(cs, elements, locator); } @@ -910,10 +868,6 @@ class SyntacticElementConstraintGenerator elements.push_back(makeElement(rawCase, switchLoc)); } - // Inject a join if we have one. - if (auto *join = context.ElementJoin.getPtrOrNull()) - elements.push_back(makeJoinElement(cs, join, switchLoc)); - createConjunction(cs, elements, switchLoc); } @@ -1030,6 +984,10 @@ class SyntacticElementConstraintGenerator SmallVector elements; for (auto element : braceStmt->getElements()) { + bool isDiscarded = + element.is() && + (!ctx.LangOpts.Playground && !ctx.LangOpts.DebuggerSupport); + if (auto *decl = element.dyn_cast()) { if (auto *PDB = dyn_cast(decl)) { visitPatternBinding(PDB, elements); @@ -1037,30 +995,11 @@ class SyntacticElementConstraintGenerator } } - bool isDiscarded = false; - auto contextInfo = cs.getContextualTypeInfo(element); - - if (element.is() && - !ctx.LangOpts.Playground && !ctx.LangOpts.DebuggerSupport) { - isDiscarded = !contextInfo || contextInfo->purpose == CTP_Unused; - } - - // For an if/switch expression, if the contextual type for the branch is - // still a type variable, we can drop it. This avoids needlessly - // propagating the type of the branch to subsequent branches, instead - // we'll let the join handle the conversion. - if (contextInfo && isExpr(locator->getAnchor())) { - auto contextualFixedTy = cs.getFixedTypeRecursive( - contextInfo->getType(), /*wantRValue*/ true); - if (contextualFixedTy->isTypeVariableOrMember()) - contextInfo = None; - } - - elements.push_back(makeElement( - element, - cs.getConstraintLocator(locator, - LocatorPathElt::SyntacticElement(element)), - contextInfo.getValueOr(ContextualTypeInfo()), isDiscarded)); + elements.push_back( + makeElement(element, + cs.getConstraintLocator( + locator, LocatorPathElt::SyntacticElement(element)), + /*contextualInfo=*/{}, isDiscarded)); } createConjunction(cs, elements, locator); @@ -1123,7 +1062,7 @@ class SyntacticElementConstraintGenerator } ContextualTypeInfo getContextualResultInfo() const { - auto funcRef = AnyFunctionRef::fromDeclContext(context.getAsDeclContext()); + auto funcRef = context.getAsAnyFunctionRef(); if (!funcRef) return {Type(), CTP_Unused}; @@ -1141,6 +1080,7 @@ class SyntacticElementConstraintGenerator llvm_unreachable("Unsupported statement kind " #STMT); \ } UNSUPPORTED_STMT(Yield) + UNSUPPORTED_STMT(Fail) #undef UNSUPPORTED_STMT private: @@ -1247,92 +1187,6 @@ bool ConstraintSystem::generateConstraints(AnyFunctionRef fn, BraceStmt *body) { return generator.hadError; } -bool ConstraintSystem::generateConstraints(SingleValueStmtExpr *E) { - auto *S = E->getStmt(); - auto &ctx = getASTContext(); - - auto *loc = getConstraintLocator(E); - Type resultTy = createTypeVariable(loc, /*options*/ 0); - setType(E, resultTy); - - // Assign contextual types for each of the expression branches. - SmallVector scratch; - auto branches = E->getSingleExprBranches(scratch); - for (auto *branch : branches) { - setContextualType(branch, TypeLoc::withoutLoc(resultTy), - CTP_SingleValueStmtBranch); - } - - TypeJoinExpr *join = nullptr; - if (branches.empty()) { - // If we only have statement branches, the expression is typed as Void. This - // should only be the case for 'if' and 'switch' statements that must be - // expressions that have branches that all end in a throw, and we'll warn - // that we've inferred Void. - addConstraint(ConstraintKind::Bind, resultTy, ctx.getVoidType(), loc); - } else { - // Otherwise, we join the result types for each of the branches. - join = TypeJoinExpr::forBranchesOfSingleValueStmtExpr( - ctx, resultTy, E, AllocationArena::ConstraintSolver); - } - - // If this is the single expression body of a closure, we need to account - // for the fact that the result type may be bound to Void. This is necessary - // to correctly handle the following case: - // - // func foo(_ fn: () -> T) {} - // foo { - // if .random() { 0 } else { "" } - // } - // - // Before if/switch expressions, this was treated as a regular statement, - // with the branches being discarded (and we'd warn). We need to ensure we - // maintain compatibility by continuing to infer T as Void in the case where - // the branches mismatch. This example is contrived, but can occur in the real - // world with e.g branches that insert and remove elements from a set, in both - // cases the methods have mismatching discardable returns. - // - // To maintain this behavior, form a disjunction that will attempt to either - // bind the expression type to the closure result type, or bind it to Void. - // Only if we fail to solve with the closure result type will we attempt with - // Void. We can't rely on the usual defaulting of the closure result type, - // as we need to solve the conjunction before trying defaults. - // - // This only needs to happen for cases where the return is implicit, we don't - // need to do this with 'return if'. We also don't need to do it for function - // decls, as we proactively avoid transforming the if/switch into an - // expression if the result is known to be Void. - if (auto *CE = dyn_cast(E->getDeclContext())) { - if (CE->hasSingleExpressionBody() && !hasExplicitResult(CE) && - CE->getSingleExpressionBody()->getSemanticsProvidingExpr() == E) { - assert(!getAppliedResultBuilderTransform(CE) && - "Should have applied the builder with statement semantics"); - - // We may not have a closure type if we're solving a sub-expression - // independently for e.g code completion. - // TODO: This won't be necessary once we stop doing the fallback - // type-check. - if (auto *closureTy = getClosureTypeIfAvailable(CE)) { - auto closureResultTy = closureTy->getResult(); - auto *bindToClosure = Constraint::create( - *this, ConstraintKind::Bind, resultTy, closureResultTy, loc); - bindToClosure->setFavored(); - - auto *bindToVoid = Constraint::create(*this, ConstraintKind::Bind, - resultTy, ctx.getVoidType(), loc); - - addDisjunctionConstraint({bindToClosure, bindToVoid}, loc); - } - } - } - - // Generate the conjunction for the branches. - auto context = SyntacticElementContext::forSingleValueStmtExpr(E, join); - SyntacticElementConstraintGenerator generator(*this, context, loc); - generator.visit(S); - return generator.hadError; -} - bool ConstraintSystem::isInResultBuilderContext(ClosureExpr *closure) const { if (!closure->hasSingleExpressionBody()) { auto *DC = closure->getParent(); @@ -1382,8 +1236,6 @@ ConstraintSystem::simplifySyntacticElementConstraint( context = SyntacticElementContext::forClosure(closure); } else if (auto *fn = getAsDecl(anchor)) { context = SyntacticElementContext::forFunction(fn); - } else if (auto *SVE = getAsExpr(anchor)) { - context = SyntacticElementContext::forSingleValueStmtExpr(SVE); } else { return SolutionKind::Error; } @@ -1392,26 +1244,9 @@ ConstraintSystem::simplifySyntacticElementConstraint( getConstraintLocator(locator)); if (auto *expr = element.dyn_cast()) { - auto ctpElt = LocatorPathElt::ContextualType(contextInfo.purpose); - auto *contextualTypeLoc = getConstraintLocator(expr, {ctpElt}); - - // If this is a branch expression in a SingleValueStmtExpr, form a locator - // based on the branch index. - if (auto *SVE = getAsExpr(locator.getAnchor())) { - SmallVector scratch; - auto branches = SVE->getSingleExprBranches(scratch); - for (auto idx : indices(branches)) { - if (expr == branches[idx]) { - contextualTypeLoc = getConstraintLocator( - SVE, {LocatorPathElt::SingleValueStmtBranch(idx), ctpElt}); - break; - } - } - } - SolutionApplicationTarget target(expr, context->getAsDeclContext(), contextInfo.purpose, contextInfo.getType(), - contextualTypeLoc, isDiscarded); + isDiscarded); if (generateConstraints(target, FreeTypeVariableBinding::Disallow)) return SolutionKind::Error; @@ -1459,21 +1294,10 @@ class SyntacticElementSolutionApplication SyntacticElementSolutionApplication(Solution &solution, SyntacticElementContext context, + Type resultType, RewriteTargetFn rewriteTarget) - : solution(solution), context(context), rewriteTarget(rewriteTarget) { - if (auto fn = AnyFunctionRef::fromDeclContext(context.getAsDeclContext())) { - if (auto transform = solution.getAppliedBuilderTransform(*fn)) { - resultType = solution.simplifyType(transform->bodyResultType); - } else if (auto *closure = - getAsExpr(fn->getAbstractClosureExpr())) { - resultType = solution.getResolvedType(closure) - ->castTo() - ->getResult(); - } else { - resultType = fn->getBodyResultType(); - } - } - } + : solution(solution), context(context), resultType(resultType), + rewriteTarget(rewriteTarget) {} virtual ~SyntacticElementSolutionApplication() {} @@ -1551,10 +1375,6 @@ class SyntacticElementSolutionApplication return fallthroughStmt; } - ASTNode visitFailStmt(FailStmt *failStmt) { - return failStmt; - } - ASTNode visitDeferStmt(DeferStmt *deferStmt) { TypeChecker::typeCheckDecl(deferStmt->getTempDecl()); @@ -1764,7 +1584,7 @@ class SyntacticElementSolutionApplication return caseStmt; } - virtual ASTNode visitBraceElement(ASTNode node) { + ASTNode visitBraceElement(ASTNode node) { auto &cs = solution.getConstraintSystem(); if (auto *expr = node.dyn_cast()) { // Rewrite the expression. @@ -1940,14 +1760,6 @@ class SyntacticElementSolutionApplication // Evaluate the expression, then produce a return statement that // returns nothing. TypeChecker::checkIgnoredExpr(resultExpr); - - // For a single expression closure, we can just preserve the result expr, - // and leave the return as implied. This avoids neededing to jump through - // nested brace statements to dig out the single expression in - // ClosureExpr::getSingleExpressionBody. - if (context.isSingleExpressionClosure(cs)) - return resultExpr; - auto &ctx = solution.getConstraintSystem().getASTContext(); auto newReturnStmt = new (ctx) ReturnStmt( @@ -1972,12 +1784,13 @@ class SyntacticElementSolutionApplication llvm_unreachable("Unsupported statement kind " #STMT); \ } UNSUPPORTED_STMT(Yield) + UNSUPPORTED_STMT(Fail) #undef UNSUPPORTED_STMT public: - /// Apply the solution to the context and return updated statement. - Stmt *apply() { - auto body = visit(context.getStmt()); + /// Apply solution to the closure and return updated body. + ASTNode apply() { + auto body = visit(context.getBody()); // Since local functions can capture variables that are declared // after them, let's type-check them after all of the pattern @@ -1985,7 +1798,7 @@ class SyntacticElementSolutionApplication for (auto *func : LocalFuncs) TypeChecker::typeCheckDecl(func); - return body ? body.get() : nullptr; + return body; } }; @@ -1998,11 +1811,11 @@ class ResultBuilderRewriter : public SyntacticElementSolutionApplication { RewriteTargetFn rewriteTarget) : SyntacticElementSolutionApplication( solution, SyntacticElementContext::forFunctionRef(context), - rewriteTarget), + transform.bodyResultType, rewriteTarget), Transform(transform) {} bool apply() { - auto body = visit(context.getStmt()); + auto body = visit(context.getBody()); if (!body || hadError) return true; @@ -2034,15 +1847,6 @@ class ResultBuilderRewriter : public SyntacticElementSolutionApplication { return doStmt; } - ASTNode visitBraceElement(ASTNode node) override { - if (auto *SVE = getAsExpr(node)) { - // This should never be treated as an expression in a result builder, - // it should have statement semantics. - return visitBraceElement(SVE->getStmt()); - } - return SyntacticElementSolutionApplication::visitBraceElement(node); - } - NullablePtr transformDo(DoStmt *doStmt) { if (!doStmt->isImplicit()) return nullptr; @@ -2388,15 +2192,28 @@ bool ConstraintSystem::applySolutionToBody(Solution &solution, // transformations. llvm::SaveAndRestore savedDC(currentDC, fn.getAsDeclContext()); + Type resultTy; + + if (auto transform = solution.getAppliedBuilderTransform(fn)) { + resultTy = solution.simplifyType(transform->bodyResultType); + } else if (auto *closure = + getAsExpr(fn.getAbstractClosureExpr())) { + resultTy = + solution.getResolvedType(closure)->castTo()->getResult(); + } else { + resultTy = fn.getBodyResultType(); + } + SyntacticElementSolutionApplication application( - solution, SyntacticElementContext::forFunctionRef(fn), rewriteTarget); + solution, SyntacticElementContext::forFunctionRef(fn), resultTy, + rewriteTarget); - auto *body = application.apply(); + auto body = application.apply(); if (!body || application.hadError) return true; - fn.setTypecheckedBody(cast(body), + fn.setTypecheckedBody(castToStmt(body), solution.getAppliedBuilderTransform(fn) ? false : fn.hasSingleExpressionBody()); @@ -2418,31 +2235,6 @@ bool ConjunctionElement::mightContainCodeCompletionToken( } } -bool ConstraintSystem::applySolutionToSingleValueStmt( - Solution &solution, SingleValueStmtExpr *SVE, DeclContext *DC, - RewriteTargetFn rewriteTarget) { - - auto context = SyntacticElementContext::forSingleValueStmtExpr(SVE); - SyntacticElementSolutionApplication application(solution, context, - rewriteTarget); - auto *stmt = application.apply(); - if (!stmt || application.hadError) - return true; - - // If the expression was typed as Void, its branches are effectively - // discarded, so treat them as ignored expressions. This doesn't happen in - // the solution application walker as we consider all the branches to have - // contextual types. - if (solution.getResolvedType(SVE)->lookThroughAllOptionalTypes()->isVoid()) { - SmallVector scratch; - for (auto *branch : SVE->getSingleExprBranches(scratch)) - TypeChecker::checkIgnoredExpr(branch); - } - - SVE->setStmt(stmt); - return false; -} - void ConjunctionElement::findReferencedVariables( ConstraintSystem &cs, SmallPtrSetImpl &typeVars) const { auto referencedVars = Element->getTypeVariables(); @@ -2454,16 +2246,7 @@ void ConjunctionElement::findReferencedVariables( ASTNode element = Element->getSyntacticElement(); auto *locator = Element->getLocator(); - ASTNode parent = locator->getAnchor(); - if (auto *SVE = getAsExpr(parent)) { - // Use a parent closure if we have one. This is needed to correctly handle - // return statements that refer to an outer closure. - if (auto *CE = dyn_cast(SVE->getDeclContext())) - parent = CE; - } - - TypeVariableRefFinder refFinder(cs, parent, Element->getElementContext(), - typeVars); + TypeVariableRefFinder refFinder(cs, locator->getAnchor(), typeVars); // If this is a pattern of `for-in` statement, let's walk into `for-in` // sequence expression because both elements are type-checked together. diff --git a/lib/Sema/CompletionContextFinder.cpp b/lib/Sema/CompletionContextFinder.cpp index e84ddcf931ee0..84188d07eeb8e 100644 --- a/lib/Sema/CompletionContextFinder.cpp +++ b/lib/Sema/CompletionContextFinder.cpp @@ -28,8 +28,7 @@ CompletionContextFinder::walkToExprPre(Expr *E) { Contexts.push_back({ContextKind::StringInterpolation, E}); } - if (isa(E) || isa(E) || - isa(E)) { + if (isa(E) || isa(E)) { Contexts.push_back({ContextKind::FallbackExpression, E}); } @@ -64,8 +63,7 @@ CompletionContextFinder::walkToExprPre(Expr *E) { ASTWalker::PostWalkResult CompletionContextFinder::walkToExprPost(Expr *E) { if (isa(E) || isa(E) || - isa(E) || isa(E) || isa(E) || - isa(E)) { + isa(E) || isa(E) || isa(E)) { assert(Contexts.back().E == E); Contexts.pop_back(); } diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index 1abaab2651db5..481f48d12eee6 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -1089,11 +1089,7 @@ Constraint *Constraint::createSyntacticElement(ConstraintSystem &cs, ContextualTypeInfo context, ConstraintLocator *locator, bool isDiscarded) { - // Collect type variables. SmallPtrSet typeVars; - if (auto contextTy = context.getType()) - contextTy->getTypeVariables(typeVars); - unsigned size = totalSizeToAlloc(typeVars.size()); void *mem = cs.getAllocator().Allocate(size, alignof(Constraint)); return new (mem) Constraint(node, context, isDiscarded, locator, typeVars); diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index f30a0e59abc59..150309c0bb95c 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -102,7 +102,6 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { case ConstraintLocator::PackExpansionPattern: case ConstraintLocator::PatternBindingElement: case ConstraintLocator::NamedPatternDecl: - case ConstraintLocator::SingleValueStmtBranch: case ConstraintLocator::AnyPatternDecl: return 0; @@ -383,13 +382,6 @@ void LocatorPathElt::dump(raw_ostream &out) const { break; } - case ConstraintLocator::SingleValueStmtBranch: { - auto branch = elt.castTo(); - out << "expr branch [" << branch.getExprBranchIndex() << "] of " - "single value stmt"; - break; - } - case ConstraintLocator::PatternMatch: out << "pattern match"; break; @@ -605,46 +597,6 @@ bool ConstraintLocator::isForResultBuilderBodyResult() const { return isFirstElement(); } -bool ConstraintLocator::isForSingleValueStmtConjunction() const { - auto *SVE = getAsExpr(getAnchor()); - if (!SVE) - return false; - - // Ignore a trailing SyntacticElement path element for the statement. - auto path = getPath(); - if (auto elt = getLastElementAs()) { - if (elt->getElement() == ASTNode(SVE->getStmt())) - path = path.drop_back(); - } - - // Other than the trailing SyntaticElement, we must be at the anchor. - return path.empty(); -} - -Optional -ConstraintLocator::isForSingleValueStmtBranch() const { - // Ignore a trailing ContextualType path element. - auto path = getPath(); - if (auto elt = getLastElementAs()) - path = path.drop_back(); - - if (path.empty()) - return None; - - if (!path.back().is()) - return None; - - auto *SVE = getAsExpr(getAnchor()); - if (!SVE) - return None; - - if (auto *CE = dyn_cast(SVE->getDeclContext())) { - if (CE->hasSingleExpressionBody() && !hasExplicitResult(CE)) - return SingleValueStmtBranchKind::InSingleExprClosure; - } - return SingleValueStmtBranchKind::Regular; -} - GenericTypeParamType *ConstraintLocator::getGenericParameter() const { // Check whether we have a path that terminates at a generic parameter. return isForGenericParameter() ? diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index f522713310007..021dbdb77d457 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -5395,16 +5395,6 @@ void constraints::simplifyLocator(ASTNode &anchor, continue; } - case ConstraintLocator::SingleValueStmtBranch: { - auto branchElt = path[0].castTo(); - auto exprIdx = branchElt.getExprBranchIndex(); - auto *SVE = castToExpr(anchor); - SmallVector scratch; - anchor = SVE->getSingleExprBranches(scratch)[exprIdx]; - path = path.slice(1); - continue; - } - case ConstraintLocator::KeyPathDynamicMember: case ConstraintLocator::ImplicitDynamicMemberSubscript: { // Key path dynamic member lookup should be completely transparent. @@ -6383,8 +6373,7 @@ void SolutionApplicationTargetsKey::dump(raw_ostream &OS) const { SolutionApplicationTarget::SolutionApplicationTarget( Expr *expr, DeclContext *dc, ContextualTypePurpose contextualPurpose, - TypeLoc convertType, ConstraintLocator *convertTypeLocator, - bool isDiscarded) { + TypeLoc convertType, bool isDiscarded) { // Verify that a purpose was specified if a convertType was. Note that it is // ok to have a purpose without a convertType (which is used for call // return types). @@ -6407,7 +6396,6 @@ SolutionApplicationTarget::SolutionApplicationTarget( expression.dc = dc; expression.contextualPurpose = contextualPurpose; expression.convertType = convertType; - expression.convertTypeLocator = convertTypeLocator; expression.pattern = nullptr; expression.propertyWrapper.wrappedVar = nullptr; expression.propertyWrapper.innermostWrappedValueInit = nullptr; @@ -6516,7 +6504,7 @@ SolutionApplicationTarget SolutionApplicationTarget::forInitialization( SolutionApplicationTarget target( initializer, dc, CTP_Initialization, contextualType, - /*convertTypeLocator*/ nullptr, /*isDiscarded=*/false); + /*isDiscarded=*/false); target.expression.pattern = pattern; target.expression.bindPatternVarsOneWay = bindPatternVarsOneWay; target.maybeApplyPropertyWrapper(); @@ -6631,7 +6619,6 @@ bool SolutionApplicationTarget::contextualTypeIsOnlyAHint() const { case CTP_ComposedPropertyWrapper: case CTP_CannotFail: case CTP_ExprPattern: - case CTP_SingleValueStmtBranch: return false; } llvm_unreachable("invalid contextual type"); diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 55447a7ea39d5..dc1b49968f399 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -101,8 +101,6 @@ bool BaseDiagnosticWalker::shouldWalkIntoDeclInClosureContext(Decl *D) { /// invalid positions. /// - Marker protocols cannot occur as the type of an as? or is expression. /// - KeyPath expressions cannot refer to effectful properties / subscripts -/// - SingleValueStmtExprs may only appear in certain places and has -/// restrictions on the control flow allowed. /// - Move expressions must have a declref expr subvalue. /// static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, @@ -3763,140 +3761,6 @@ void VarDeclUsageChecker::handleIfConfig(IfConfigDecl *ICD) { } } -namespace { -class SingleValueStmtUsageChecker final : public ASTWalker { - ASTContext &Ctx; - DiagnosticEngine &Diags; - llvm::DenseSet ValidSingleValueStmtExprs; - -public: - SingleValueStmtUsageChecker(ASTContext &ctx) : Ctx(ctx), Diags(ctx.Diags) {} - -private: - /// Mark a given expression as a valid position for a SingleValueStmtExpr. - void markValidSingleValueStmt(Expr *E) { - if (!E) - return; - - if (auto *SVE = SingleValueStmtExpr::tryDigOutSingleValueStmtExpr(E)) - ValidSingleValueStmtExprs.insert(SVE); - } - - PreWalkResult walkToExprPre(Expr *E) override { - if (auto *SVE = dyn_cast(E)) { - // Diagnose a SingleValueStmtExpr in a context that we do not currently - // support. - if (!ValidSingleValueStmtExprs.contains(SVE)) { - Diags.diagnose(SVE->getLoc(), diag::single_value_stmt_out_of_place, - SVE->getStmt()->getKind()); - } - - // Nested SingleValueStmtExprs are allowed. - SmallVector scratch; - for (auto *branch : SVE->getSingleExprBranches(scratch)) - markValidSingleValueStmt(branch); - - // Diagnose invalid SingleValueStmtExprs. This should only happen for - // expressions in positions that we didn't support before - // (e.g assignment or *explicit* return). - auto *S = SVE->getStmt(); - auto mayProduceSingleValue = S->mayProduceSingleValue(Ctx); - switch (mayProduceSingleValue.getKind()) { - case IsSingleValueStmtResult::Kind::Valid: - break; - case IsSingleValueStmtResult::Kind::UnterminatedBranches: { - for (auto *branch : mayProduceSingleValue.getUnterminatedBranches()) { - Diags.diagnose(branch->getEndLoc(), - diag::single_value_stmt_branch_must_end_in_throw, - S->getKind()); - } - break; - } - case IsSingleValueStmtResult::Kind::NonExhaustiveIf: { - Diags.diagnose(S->getStartLoc(), - diag::if_expr_must_be_syntactically_exhaustive); - break; - } - case IsSingleValueStmtResult::Kind::HasLabel: { - // FIXME: We should offer a fix-it to remove (currently we don't track - // the colon SourceLoc). - auto label = cast(S)->getLabelInfo(); - Diags.diagnose(label.Loc, - diag::single_value_stmt_must_be_unlabeled, S->getKind()) - .highlight(label.Loc); - break; - } - case IsSingleValueStmtResult::Kind::InvalidJumps: { - // Diagnose each invalid jump. - for (auto *jump : mayProduceSingleValue.getInvalidJumps()) { - Diags.diagnose(jump->getStartLoc(), - diag::cannot_jump_in_single_value_stmt, - jump->getKind(), S->getKind()) - .highlight(jump->getSourceRange()); - } - break; - } - case IsSingleValueStmtResult::Kind::NoExpressionBranches: - // This is fine, we will have typed the expression as Void (we verify - // as such in the ASTVerifier). - break; - case IsSingleValueStmtResult::Kind::CircularReference: - // Already diagnosed. - break; - case IsSingleValueStmtResult::Kind::UnhandledStmt: - break; - } - return Action::Continue(E); - } - - // Valid as the source of an assignment, as long as it's not a nested - // expression (as otherwise this would be effectively allowing it in an - // arbitrary expression position). - if (auto *AE = dyn_cast(E)) { - if (!Parent.getAsExpr()) - markValidSingleValueStmt(AE->getSrc()); - } - - // Valid as a single expression body of a closure. This is needed in - // addition to ReturnStmt checking, as we will remove the return if the - // expression is inferred to be Never. - if (auto *ACE = dyn_cast(E)) { - if (ACE->hasSingleExpressionBody()) - markValidSingleValueStmt(ACE->getSingleExpressionBody()); - } - return Action::Continue(E); - } - - PreWalkResult walkToStmtPre(Stmt *S) override { - // Valid in a return/throw. - if (auto *RS = dyn_cast(S)) { - if (RS->hasResult()) - markValidSingleValueStmt(RS->getResult()); - } - if (auto *TS = dyn_cast(S)) - markValidSingleValueStmt(TS->getSubExpr()); - - return Action::Continue(S); - } - - PreWalkAction walkToDeclPre(Decl *D) override { - // Valid as an initializer of a pattern binding. - if (auto *PBD = dyn_cast(D)) { - for (auto idx : range(PBD->getNumPatternEntries())) - markValidSingleValueStmt(PBD->getInit(idx)); - } - // Valid as a single expression body of a function. This is needed in - // addition to ReturnStmt checking, as we will remove the return if the - // expression is inferred to be Never. - if (auto *AFD = dyn_cast(D)) { - if (AFD->hasSingleExpressionBody()) - markValidSingleValueStmt(AFD->getSingleExpressionBody()); - } - return Action::Continue(); - } -}; -} // end anonymous namespace - /// Apply the warnings managed by VarDeclUsageChecker to the top level /// code declarations that haven't been checked yet. void swift:: @@ -3904,8 +3768,6 @@ performTopLevelDeclDiagnostics(TopLevelCodeDecl *TLCD) { auto &ctx = TLCD->getDeclContext()->getASTContext(); VarDeclUsageChecker checker(TLCD, ctx.Diags); TLCD->walk(checker); - SingleValueStmtUsageChecker sveChecker(ctx); - TLCD->walk(sveChecker); } /// Perform diagnostics for func/init/deinit declarations. @@ -3913,18 +3775,14 @@ void swift::performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD) { // Don't produce these diagnostics for implicitly generated code. if (AFD->getLoc().isInvalid() || AFD->isImplicit() || AFD->isInvalid()) return; - + + // Check for unused variables, as well as variables that are could be + // declared as constants. Skip local functions though, since they will + // be checked as part of their parent function or TopLevelCodeDecl. if (!AFD->getDeclContext()->isLocalContext()) { - // Check for unused variables, as well as variables that are could be - // declared as constants. Skip local functions though, since they will - // be checked as part of their parent function or TopLevelCodeDecl. auto &ctx = AFD->getDeclContext()->getASTContext(); VarDeclUsageChecker checker(AFD, ctx.Diags); AFD->walk(checker); - - // Do a similar walk to check for out of place SingleValueStmtExprs. - SingleValueStmtUsageChecker sveChecker(ctx); - AFD->walk(sveChecker); } auto *body = AFD->getBody(); diff --git a/lib/Sema/PreCheckExpr.cpp b/lib/Sema/PreCheckExpr.cpp index 32496bbe65de1..26eef67de1985 100644 --- a/lib/Sema/PreCheckExpr.cpp +++ b/lib/Sema/PreCheckExpr.cpp @@ -971,9 +971,6 @@ namespace { /// The current number of nested \c SequenceExprs that we're within. unsigned SequenceExprDepth = 0; - /// The current number of nested \c SingleValueStmtExprs that we're within. - unsigned SingleValueStmtExprDepth = 0; - /// Simplify expressions which are type sugar productions that got parsed /// as expressions due to the parser not knowing which identifiers are /// type names. @@ -1097,13 +1094,6 @@ namespace { if (auto closure = dyn_cast(expr)) return finish(walkToClosureExprPre(closure), expr); - if (auto *SVE = dyn_cast(expr)) { - // Record the scope of a single value stmt expr, as we want to skip - // pre-checking of any patterns, similar to closures. - SingleValueStmtExprDepth += 1; - return finish(true, expr); - } - if (auto unresolved = dyn_cast(expr)) { TypeChecker::checkForForbiddenPrefix( getASTContext(), unresolved->getName().getBaseName()); @@ -1220,10 +1210,6 @@ namespace { DC = ce->getParent(); } - // Restore the depth for the single value stmt counter. - if (isa(expr)) - SingleValueStmtExprDepth -= 1; - // A 'self.init' or 'super.init' application inside a constructor will // evaluate to void, with the initializer's result implicitly rebound // to 'self'. Recognize the unresolved constructor expression and @@ -1367,17 +1353,6 @@ namespace { } PreWalkResult walkToStmtPre(Stmt *stmt) override { - if (auto *RS = dyn_cast(stmt)) { - // Pre-check a return statement, which includes potentially turning it - // into a FailStmt. - auto &eval = Ctx.evaluator; - auto *S = evaluateOrDefault(eval, PreCheckReturnStmtRequest{RS, DC}, - nullptr); - if (!S) - return Action::Stop(); - - return Action::Continue(S); - } return Action::Continue(stmt); } @@ -1387,10 +1362,9 @@ namespace { PreWalkResult walkToPatternPre(Pattern *pattern) override { // Constraint generation is responsible for pattern verification and - // type-checking in the body of the closure and single value stmt expr, - // so there is no need to walk into patterns. - return Action::SkipChildrenIf( - isa(DC) || SingleValueStmtExprDepth > 0, pattern); + // type-checking in the body of the closure, so there is no need to + // walk into patterns. + return Action::SkipChildrenIf(isa(DC), pattern); } }; } // end anonymous namespace @@ -1398,23 +1372,6 @@ namespace { /// Perform prechecking of a ClosureExpr before we dive into it. This returns /// true when we want the body to be considered part of this larger expression. bool PreCheckExpression::walkToClosureExprPre(ClosureExpr *closure) { - // If we have a single statement that can become an expression, turn it - // into an expression now. This needs to happen before we check - // LeaveClosureBodiesUnchecked, as the closure may become a single expression - // closure. - auto *body = closure->getBody(); - if (body->getNumElements() == 1) { - if (auto *S = body->getLastElement().dyn_cast()) { - if (S->mayProduceSingleValue(Ctx)) { - auto *SVE = SingleValueStmtExpr::createWithWrappedBranches( - Ctx, S, /*DC*/ closure, /*mustBeExpr*/ false); - auto *RS = new (Ctx) ReturnStmt(SourceLoc(), SVE); - body->setLastElement(RS); - closure->setBody(body, /*isSingleExpression*/ true); - } - } - } - // If we won't be checking the body of the closure, don't walk into it here. if (!closure->hasSingleExpressionBody()) { if (LeaveClosureBodiesUnchecked) diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index 03858c0f9fc07..8caeee650a2f6 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -2709,12 +2709,11 @@ class CheckEffectsCoverage : public EffectsHandlingWalker // course we're in a context that could never handle an 'async'. Then, we // produce an error. if (!Flags.has(ContextFlags::HasAnyAsyncSite)) { - if (CurContext.handlesAsync(ConditionalEffectKind::Conditional)) { - diagnoseRedundantAwait(E); - } else { + if (CurContext.handlesAsync(ConditionalEffectKind::Conditional)) + Ctx.Diags.diagnose(E->getAwaitLoc(), diag::no_async_in_await); + else CurContext.diagnoseUnhandledAsyncSite(Ctx.Diags, E, None, /*forAwait=*/ true); - } } // Inform the parent of the walk that an 'await' exists here. @@ -2732,7 +2731,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker // Warn about 'try' expressions that weren't actually needed. if (!Flags.has(ContextFlags::HasTryThrowSite)) { if (!E->isImplicit()) - diagnoseRedundantTry(E); + Ctx.Diags.diagnose(E->getTryLoc(), diag::no_throw_in_try); // Diagnose all the call sites within a single unhandled 'try' // at the same time. @@ -2752,8 +2751,9 @@ class CheckEffectsCoverage : public EffectsHandlingWalker E->getSubExpr()->walk(*this); // Warn about 'try' expressions that weren't actually needed. - if (!Flags.has(ContextFlags::HasTryThrowSite)) - diagnoseRedundantTry(E); + if (!Flags.has(ContextFlags::HasTryThrowSite)) { + Ctx.Diags.diagnose(E->getLoc(), diag::no_throw_in_try); + } scope.preserveCoverageFromOptionalOrForcedTryOperand(); return ShouldNotRecurse; @@ -2767,8 +2767,9 @@ class CheckEffectsCoverage : public EffectsHandlingWalker E->getSubExpr()->walk(*this); // Warn about 'try' expressions that weren't actually needed. - if (!Flags.has(ContextFlags::HasTryThrowSite)) - diagnoseRedundantTry(E); + if (!Flags.has(ContextFlags::HasTryThrowSite)) { + Ctx.Diags.diagnose(E->getLoc(), diag::no_throw_in_try); + } scope.preserveCoverageFromOptionalOrForcedTryOperand(); return ShouldNotRecurse; @@ -2806,30 +2807,6 @@ class CheckEffectsCoverage : public EffectsHandlingWalker return ShouldRecurse; } - void diagnoseRedundantTry(AnyTryExpr *E) const { - if (auto *SVE = SingleValueStmtExpr::tryDigOutSingleValueStmtExpr(E)) { - // For an if/switch expression, produce an error instead of a warning. - Ctx.Diags.diagnose(E->getTryLoc(), - diag::effect_marker_on_single_value_stmt, - "try", SVE->getStmt()->getKind()) - .highlight(E->getTryLoc()); - return; - } - Ctx.Diags.diagnose(E->getTryLoc(), diag::no_throw_in_try); - } - - void diagnoseRedundantAwait(AwaitExpr *E) const { - if (auto *SVE = SingleValueStmtExpr::tryDigOutSingleValueStmtExpr(E)) { - // For an if/switch expression, produce an error instead of a warning. - Ctx.Diags.diagnose(E->getAwaitLoc(), - diag::effect_marker_on_single_value_stmt, - "await", SVE->getStmt()->getKind()) - .highlight(E->getAwaitLoc()); - return; - } - Ctx.Diags.diagnose(E->getAwaitLoc(), diag::no_async_in_await); - } - void diagnoseUncoveredAsyncSite(const Expr *anchor) const { auto asyncPointIter = uncoveredAsync.find(anchor); if (asyncPointIter == uncoveredAsync.end()) diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 990de1878a1b4..d41324551f696 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -757,20 +757,20 @@ class StmtChecker : public StmtVisitor { Stmt *visitBraceStmt(BraceStmt *BS); Stmt *visitReturnStmt(ReturnStmt *RS) { - // First, let's do a pre-check, and bail if the return is completely - // invalid. - auto &eval = getASTContext().evaluator; - auto *S = - evaluateOrDefault(eval, PreCheckReturnStmtRequest{RS, DC}, nullptr); - - // We do a cast here as it may have been turned into a FailStmt. We should - // return that without doing anything else. - RS = dyn_cast_or_null(S); - if (!RS) - return S; - auto TheFunc = AnyFunctionRef::fromDeclContext(DC); - assert(TheFunc && "Should have bailed from pre-check if this is None"); + + if (!TheFunc.has_value()) { + getASTContext().Diags.diagnose(RS->getReturnLoc(), + diag::return_invalid_outside_func); + return nullptr; + } + + // If the return is in a defer, then it isn't valid either. + if (isInDefer()) { + getASTContext().Diags.diagnose(RS->getReturnLoc(), + diag::jump_out_of_defer, "return"); + return nullptr; + } Type ResultTy = TheFunc->getBodyResultType(); if (!ResultTy || ResultTy->hasError()) @@ -808,6 +808,40 @@ class StmtChecker : public StmtVisitor { } Expr *E = RS->getResult(); + + // In an initializer, the only expression allowed is "nil", which indicates + // failure from a failable initializer. + if (auto ctor = dyn_cast_or_null( + TheFunc->getAbstractFunctionDecl())) { + // The only valid return expression in an initializer is the literal + // 'nil'. + auto nilExpr = dyn_cast(E->getSemanticsProvidingExpr()); + if (!nilExpr) { + getASTContext().Diags.diagnose(RS->getReturnLoc(), + diag::return_init_non_nil) + .highlight(E->getSourceRange()); + RS->setResult(nullptr); + return RS; + } + + // "return nil" is only permitted in a failable initializer. + if (!ctor->isFailable()) { + getASTContext().Diags.diagnose(RS->getReturnLoc(), + diag::return_non_failable_init) + .highlight(E->getSourceRange()); + getASTContext().Diags.diagnose(ctor->getLoc(), diag::make_init_failable, + ctor->getName()) + .fixItInsertAfter(ctor->getLoc(), "?"); + RS->setResult(nullptr); + return RS; + } + + // Replace the "return nil" with a new 'fail' statement. + return new (getASTContext()) FailStmt(RS->getReturnLoc(), + nilExpr->getLoc(), + RS->isImplicit()); + } + TypeCheckExprOptions options = {}; if (LeaveBraceStmtBodyUnchecked) { @@ -1260,62 +1294,6 @@ class StmtChecker : public StmtVisitor { }; } // end anonymous namespace -Stmt *PreCheckReturnStmtRequest::evaluate(Evaluator &evaluator, ReturnStmt *RS, - DeclContext *DC) const { - auto &ctx = DC->getASTContext(); - auto fn = AnyFunctionRef::fromDeclContext(DC); - - // Not valid outside of a function. - if (!fn) { - ctx.Diags.diagnose(RS->getReturnLoc(), diag::return_invalid_outside_func); - return nullptr; - } - - // If the return is in a defer, then it isn't valid either. - if (isDefer(DC)) { - ctx.Diags.diagnose(RS->getReturnLoc(), diag::jump_out_of_defer, "return"); - return nullptr; - } - - // The rest of the checks only concern return statements with results. - if (!RS->hasResult()) - return RS; - - auto *E = RS->getResult(); - - // In an initializer, the only expression allowed is "nil", which indicates - // failure from a failable initializer. - if (auto *ctor = - dyn_cast_or_null(fn->getAbstractFunctionDecl())) { - - // The only valid return expression in an initializer is the literal - // 'nil'. - auto *nilExpr = dyn_cast(E->getSemanticsProvidingExpr()); - if (!nilExpr) { - ctx.Diags.diagnose(RS->getReturnLoc(), diag::return_init_non_nil) - .highlight(E->getSourceRange()); - RS->setResult(nullptr); - return RS; - } - - // "return nil" is only permitted in a failable initializer. - if (!ctor->isFailable()) { - ctx.Diags.diagnose(RS->getReturnLoc(), diag::return_non_failable_init) - .highlight(E->getSourceRange()); - ctx.Diags - .diagnose(ctor->getLoc(), diag::make_init_failable, ctor->getName()) - .fixItInsertAfter(ctor->getLoc(), "?"); - RS->setResult(nullptr); - return RS; - } - - // Replace the "return nil" with a new 'fail' statement. - return new (ctx) - FailStmt(RS->getReturnLoc(), nilExpr->getLoc(), RS->isImplicit()); - } - return RS; -} - static bool isDiscardableType(Type type) { return (type->hasError() || type->isUninhabited() || @@ -2175,22 +2153,8 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &evaluator, } else if (func->hasSingleExpressionBody() && func->getResultInterfaceType()->isVoid()) { // The function returns void. We don't need an explicit return, no matter - // what the type of the expression is. Take the inserted return back out. + // what the type of the expression is. Take the inserted return back out. body->setLastElement(func->getSingleExpressionBody()); - } else if (func->getBody()->getNumElements() == 1 && - !func->getResultInterfaceType()->isVoid()) { - // If there is a single statement in the body that can be turned into a - // single expression return, do so now. - if (auto *S = func->getBody()->getLastElement().dyn_cast()) { - if (S->mayProduceSingleValue(evaluator)) { - auto *SVE = SingleValueStmtExpr::createWithWrappedBranches( - ctx, S, /*DC*/ func, /*mustBeExpr*/ false); - auto *RS = new (ctx) ReturnStmt(SourceLoc(), SVE); - body->setLastElement(RS); - func->setHasSingleExpressionBody(); - func->setSingleExpressionBody(SVE); - } - } } } else if (auto *ctor = dyn_cast(AFD)) { // If this is user-defined constructor that requires `_storage` @@ -2317,145 +2281,6 @@ void TypeChecker::typeCheckTopLevelCodeDecl(TopLevelCodeDecl *TLCD) { performTopLevelDeclDiagnostics(TLCD); } -/// Whether the given brace statement ends with a throw. -static bool doesBraceEndWithThrow(BraceStmt *BS) { - auto elts = BS->getElements(); - if (elts.empty()) - return false; - - auto lastElt = elts.back(); - auto *S = lastElt.dyn_cast(); - if (!S) - return false; - - return isa(S); -} - -namespace { -/// An ASTWalker that searches for any break/continue/return statements that -/// jump out of the context the walker starts at. -class JumpOutOfContextFinder : public ASTWalker { - TinyPtrVector &Jumps; - SmallPtrSet ParentLabeledStmts; - -public: - JumpOutOfContextFinder(TinyPtrVector &jumps) : Jumps(jumps) {} - - PreWalkResult walkToStmtPre(Stmt *S) override { - if (auto *LS = dyn_cast(S)) - ParentLabeledStmts.insert(LS); - - // Cannot 'break', 'continue', or 'return' out of the statement. A jump to - // a statement within a branch however is fine. - if (auto *BS = dyn_cast(S)) { - if (!ParentLabeledStmts.contains(BS->getTarget())) - Jumps.push_back(BS); - } - if (auto *CS = dyn_cast(S)) { - if (!ParentLabeledStmts.contains(CS->getTarget())) - Jumps.push_back(CS); - } - if (isa(S) || isa(S)) - Jumps.push_back(S); - - return Action::Continue(S); - } - PostWalkResult walkToStmtPost(Stmt *S) override { - if (auto *LS = dyn_cast(S)) { - auto removed = ParentLabeledStmts.erase(LS); - assert(removed); - (void)removed; - } - return Action::Continue(S); - } - - PreWalkResult walkToExprPre(Expr *E) override { - // We don't need to walk into closures, you can't jump out of them. - return Action::SkipChildrenIf(isa(E), E); - } - PreWalkAction walkToDeclPre(Decl *D) override { - // We don't need to walk into any nested local decls. - return Action::VisitChildrenIf(isa(D)); - } -}; -} // end anonymous namespace - -IsSingleValueStmtResult -areBranchesValidForSingleValueStmt(Evaluator &eval, ArrayRef branches) { - TinyPtrVector invalidJumps; - TinyPtrVector unterminatedBranches; - JumpOutOfContextFinder jumpFinder(invalidJumps); - - // Must have a single expression brace, and non-single-expression branches - // must end with a throw. - bool hadSingleExpr = false; - for (auto *branch : branches) { - auto *BS = dyn_cast(branch); - if (!BS) - return IsSingleValueStmtResult::unhandledStmt(); - - // Check to see if there are any invalid jumps. - BS->walk(jumpFinder); - - if (BS->getSingleExpressionElement()) { - hadSingleExpr = true; - continue; - } - - // We also allow single value statement branches, which we can wrap in - // a SingleValueStmtExpr. - auto elts = BS->getElements(); - if (elts.size() == 1) { - if (auto *S = elts.back().dyn_cast()) { - if (S->mayProduceSingleValue(eval)) { - hadSingleExpr = true; - continue; - } - } - } - if (!doesBraceEndWithThrow(BS)) - unterminatedBranches.push_back(BS); - } - - if (!invalidJumps.empty()) - return IsSingleValueStmtResult::invalidJumps(std::move(invalidJumps)); - - if (!unterminatedBranches.empty()) { - return IsSingleValueStmtResult::unterminatedBranches( - std::move(unterminatedBranches)); - } - - if (!hadSingleExpr) - return IsSingleValueStmtResult::noExpressionBranches(); - - return IsSingleValueStmtResult::valid(); -} - -IsSingleValueStmtResult -IsSingleValueStmtRequest::evaluate(Evaluator &eval, const Stmt *S) const { - if (!isa(S) && !isa(S)) - return IsSingleValueStmtResult::unhandledStmt(); - - // Statements must be unlabeled. - auto *LS = cast(S); - if (LS->getLabelInfo()) - return IsSingleValueStmtResult::hasLabel(); - - if (auto *IS = dyn_cast(S)) { - // Must be exhaustive. - if (!IS->isSyntacticallyExhaustive()) - return IsSingleValueStmtResult::nonExhaustiveIf(); - - SmallVector scratch; - return areBranchesValidForSingleValueStmt(eval, IS->getBranches(scratch)); - } - if (auto *SS = dyn_cast(S)) { - SmallVector scratch; - return areBranchesValidForSingleValueStmt(eval, SS->getBranches(scratch)); - } - llvm_unreachable("Unhandled case"); -} - void swift::checkUnknownAttrRestrictions( ASTContext &ctx, CaseStmt *caseBlock, bool &limitExhaustivityChecks) { diff --git a/test/ASTGen/verify-parse.swift b/test/ASTGen/verify-parse.swift index ec5004eb12972..b0f81367b2cdf 100644 --- a/test/ASTGen/verify-parse.swift +++ b/test/ASTGen/verify-parse.swift @@ -24,16 +24,3 @@ func test3(y: Int) -> Int { let x = y return x } - -func test4(_ b: Bool) -> Int { - if b { 0 } else { 1 } -} - -func test5(_ b: Bool) -> Int { - return if b { 0 } else { 1 } -} - -func test6(_ b: Bool) -> Int { - let x = if b { 0 } else { 1 } - return x -} diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index 5e474a26eae3f..8cb972ed32d73 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -1149,11 +1149,9 @@ struct R_76250381 { // rdar://77022842 - crash due to a missing argument to a ternary operator func rdar77022842(argA: Bool? = nil, argB: Bool? = nil) { if let a = argA ?? false, if let b = argB ?? { - // expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - // expected-error@-2 {{initializer for conditional binding must have Optional type, not 'Bool'}} - // expected-error@-3 {{cannot convert value of type '() -> ()' to expected argument type 'Bool?'}} - // expected-error@-4 {{cannot convert value of type 'Void' to expected condition type 'Bool'}} - // expected-error@-5 {{'if' must have an unconditional 'else' to be used as expression}} + // expected-error@-1 {{initializer for conditional binding must have Optional type, not 'Bool'}} + // expected-error@-2 {{cannot convert value of type '() -> ()' to expected argument type 'Bool?'}} + // expected-error@-3 {{expected expression in conditional}} } // expected-error {{expected '{' after 'if' condition}} } diff --git a/test/Constraints/if_expr.swift b/test/Constraints/if_expr.swift deleted file mode 100644 index 2599f6ef80a72..0000000000000 --- a/test/Constraints/if_expr.swift +++ /dev/null @@ -1,569 +0,0 @@ -// RUN: %target-typecheck-verify-swift - -enum E { - case e - case f - case g(Int) -} - -func testDotSyntax1() -> E { - if .random() { .e } else { .f } -} -func testDotSyntax2() -> E? { - if .random() { .e } else { .f } -} -func testDotSyntax3() -> E? { - if .random() { .e } else { .none } -} -func testDotSyntax4() -> Int { - let i = if .random() { 0 } else { .random() } - // expected-error@-1 {{cannot infer contextual base in reference to member 'random'}} - - return i -} - -let testVar1: E = if .random() { .e } else { .f } -let testVar2: E? = if .random() { .e } else { .f } -let testVar3: E? = if .random() { .e } else { .none } -let testVar4: E? = if .random() { nil } else { .e } - -let testVar5 = if .random() { 0 } else { 1.0 } -// expected-error@-1 {{branches have mismatching types 'Int' and 'Double'}} - -let testVar6: Double = if .random() { 0 } else { 1.0 } - -let testVar7: Double = if .random() { 0 + 1 } else if .random() { 1.0 + 3 } else { 9 + 0.0 } - -let testContextualMismatch1: String = if .random() { 1 } else { "" } -// expected-error@-1 {{cannot convert value of type 'Int' to specified type 'String'}} - -let testContextualMismatch2: String = if .random() { 1 } else { 2 } -// expected-error@-1 {{cannot convert value of type 'Int' to specified type 'String'}} - -let testMismatch1 = if .random() { 1 } else if .random() { "" } else { 0.0 } -// expected-error@-1 {{branches have mismatching types 'Int' and 'Double'}} -// expected-error@-2 {{branches have mismatching types 'String' and 'Double'}} - -func testMismatch2() -> Double { - let x = if .random() { - 0 // expected-error {{branches have mismatching types 'Int' and 'Double'}} - } else if .random() { - 0 - } else { - 1.0 - } - return x -} -func testOptionalBinding1(_ x: Int?) -> Int { - if let x = x { x } else { 0 } -} -func testOptionalBinding2(_ x: Int?, _ y: Int?) -> Int { - if let x = x, let y = y { x + y } else { 0 } -} -func testPatternBinding2(_ e: E) -> Int { - if case .g(let i) = e { i } else { 0 } -} - -func testTernary() -> Int { - if .random() { .random() ? 1 : 0 } else { .random() ? 3 : 2 } -} - -func testReturn() -> Int { - if .random() { - return 0 - } else { - 1 // expected-warning {{integer literal is unused}} - } -} - -func testNil1(_ x: Bool) { - let _ = if x { 42 } else { nil } // expected-error {{'nil' requires a contextual type}} -} -func testNil2(_ x: Bool) { - let _ = if x { nil } else { 42 } // expected-error {{'nil' requires a contextual type}} -} - -func testNil3(_ x: Bool) { - // In this case, we allow propagating the type from the first branch into - // later branches. - let _: _? = if x { 42 } else { nil } -} -func testNil4(_ x: Bool) { - // FIXME: Bad diagnostic (#63130) - let _: _? = if x { nil } else { 42 } // expected-error {{type of expression is ambiguous without more context}} -} - -enum F { - // expected-note@-1 {{arguments to generic parameter 'T' ('Double' and 'Int') are expected to be equal}} - // expected-note@-2 {{'T' declared as parameter to type 'F'}} - case x(T), y -} - -func testUnboundGeneric1() -> F { - let x: F = if .random() { .x(5) } else { .x(1) } - return x -} - -func testUnboundGeneric2() -> F { - let x: F = if .random() { .x(5) } else { .y } - return x -} - -func testUnboundGeneric3() -> F { - let x: F = if .random() { .y } else { .y } - // expected-error@-1 {{generic parameter 'T' could not be inferred}} - return x -} - -func testUnboundGeneric4() -> F { - let x: F = if .random() { .x(5.0) } else { .x(5.0) } - return x - // expected-error@-1 {{cannot convert return expression of type 'F' to return type 'F'}} -} - -func testUnboundGeneric5() -> F { - let x: F = if .random() { .x(5) } else { .x(5.0) } - // expected-error@-1 {{cannot convert value of type 'Double' to expected argument type 'Int'}} - return x -} - -protocol Q { - associatedtype X -} - -struct SQ : Q { - typealias X = String -} - -func testAssociatedTypeReturn1() { - func fn(_ fn: (T) -> T.X) {} - fn { x in // expected-error {{unable to infer type of a closure parameter 'x' in the current context}} - if .random() { "" } else { "" } - } - fn { (x: SQ) in - if .random() { "" } else { "" } - } - fn { (x: SQ) in - if .random() { "" } else { 0 } // expected-error {{cannot convert value of type 'Int' to specified type 'SQ.X' (aka 'String')}} - } -} - -func testNeverConversion1() -> Int { - if .random() { - 1 - } else { - fatalError() - } -} - -func testNeverConversion2() -> Int { - return if .random() { - 1 - } else { - fatalError() - } -} - -func testNeverConversion3() -> Int { - if .random() { - 1 - } else { - if .random() { - fatalError() - } else { - 2 - } - } -} - -func testNeverConversion4() -> Int { - return if .random() { - 1 - } else { - if .random() { - fatalError() - } else { - 2 - } - } -} - -func testNeverConversion5() -> Int { - { - if .random() { - 1 - } else { - if .random() { - fatalError() - } else { - 2 - } - } - }() -} - -func testVoidConversion() { - func foo(_ fn: () -> Void) {} - func bar(_ fn: () -> T) {} - - // Okay for an implicit return, including nested as this preserves source - // compatibility. - foo { - if .random() { - 0 // expected-warning {{integer literal is unused}} - } else { - 0 // expected-warning {{integer literal is unused}} - } - } - foo { - if .random() { - 0 // expected-warning {{integer literal is unused}} - } else { - if .random() { - 0 // expected-warning {{integer literal is unused}} - } else { - 0 // expected-warning {{integer literal is unused}} - } - } - } - bar { - if .random() { - 0 - } else { - 0 - } - } - bar { - if .random() { - 0 - } else { - if .random() { - 0 - } else { - 0 - } - } - } - bar { - if .random() { - () - } else { - 0 // expected-warning {{integer literal is unused}} - } - } - bar { - if .random() { - 0 // expected-warning {{integer literal is unused}} - } else { - if .random() { - () - } else { - 0 // expected-warning {{integer literal is unused}} - } - } - } - // We allow the branches to mismatch to preserve source compatibility. - // (this example is silly, but this occurs in the wild for more innocuous - // things like branches that do set insertions and removals). - bar { - if .random() { 0 } else { "" } - // expected-warning@-1 {{integer literal is unused}} - // expected-warning@-2 {{string literal is unused}} - } - bar { - if .random() { - if .random() { - 0 // expected-warning {{integer literal is unused}} - } else { - [0] // expected-warning {{expression of type '[Int]' is unused}} - } - } else { - "" // expected-warning {{string literal is unused}} - } - } - bar { () -> Void in - if .random() { 0 } else { "" } - // expected-warning@-1 {{integer literal is unused}} - // expected-warning@-2 {{string literal is unused}} - } - bar { () -> Void in - if .random() { - if .random() { - 0 // expected-warning {{integer literal is unused}} - } else { - [0] // expected-warning {{expression of type '[Int]' is unused}} - } - } else { - "" // expected-warning {{string literal is unused}} - } - } - bar { () -> Int in - if .random() { 0 } else { "" } // expected-error {{cannot convert value of type 'String' to specified type 'Int'}} - } - bar { () -> Int in - if .random() { - if .random() { - 0 - } else { - [0] // expected-error {{cannot convert value of type '[Int]' to specified type 'Int'}} - } - } else { - "" - } - } - - // Not okay for an explicit return. - foo { - return if .random() { - 0 // expected-error {{cannot convert value of type 'Int' to specified type 'Void'}} - } else { - 0 - } - } - foo { - return if .random() { - 0 // expected-error {{cannot convert value of type 'Int' to specified type 'Void'}} - } else { - if .random() { - 0 - } else { - 0 - } - } - } - bar { - return if .random() { - 0 - } else { - 0 - } - } - bar { - return if .random() { - 0 - } else { - if .random() { - 0 - } else { - 0 - } - } - } - bar { - return if .random() { - () // expected-error {{branches have mismatching types '()' and 'Int'}} - } else { - 0 - } - } - bar { - return if .random() { - 0 - } else { - if .random() { - () // expected-error {{branches have mismatching types '()' and 'Int'}} - } else { - 0 - } - } - } - bar { - return if .random() { 0 } else { "" } // expected-error {{branches have mismatching types 'Int' and 'String'}} - } - bar { - return if .random() { - if .random() { - 0 // expected-error {{branches have mismatching types 'Int' and '[Int]'}} - } else { - [0] - } - } else { - "" - } - } -} - -func testReturnMismatch() { - let _ = if .random() { - return 1 // expected-error {{unexpected non-void return value in void function}} - // expected-note@-1 {{did you mean to add a return type?}} - // expected-error@-2 {{cannot 'return' in 'if' when used as expression}} - } else { - 0 - } -} - -func testOptionalGeneric() { - func bar(_ fn: () -> T?) -> T? { fn() } - bar { - if .random() { - () - } else { - () - } - } -} - -func testNestedOptional() -> Int? { - if .random() { - 1 - } else { - if .random() { - 1 - } else { - nil - } - } -} - -let neverVar = if .random() { fatalError() } else { fatalError() } -// expected-warning@-1 {{constant 'neverVar' inferred to have type 'Never'}} -// expected-note@-2 {{add an explicit type annotation to silence this warning}} - -func testNonVoidToVoid() { - if .random() { 0 } else { 1 } // expected-warning 2{{integer literal is unused}} -} - -func uninferableNil() { - let _ = if .random() { nil } else { 2.0 } // expected-error {{'nil' requires a contextual type}} -} - -func testAssignment() { - var d: Double = if .random() { 0 } else { 1.0 } - d = if .random() { 0 } else { 1 } - _ = d -} - -struct TestBadReturn { - var y = if .random() { return } else { 0 } // expected-error {{return invalid outside of a func}} -} - -struct SomeError: Error {} - -func testThrowInference() { - func notThrowing(_ fn: () -> Int) {} - notThrowing { // expected-error {{invalid conversion from throwing function of type '() throws -> Int' to non-throwing function type '() -> Int'}} - if .random() { - 0 - } else { - throw SomeError() - } - } - - @discardableResult - func rethrowing(_ fn: () throws -> T) rethrows -> T { try fn() } - rethrowing { - // expected-error@-1 {{call can throw, but it is not marked with 'try' and the error is not handled}} - // expected-note@-2 {{call is to 'rethrows' function, but argument function can throw}} - if .random() { - 0 - } else { - throw SomeError() - } - } -} - -// MARK: Subtyping - -class A {} -class B : A {} -class C : A {} - -func testSubtyping1() -> A { - // We can join to A. - let x = if .random() { B() } else { C() } - let y = .random() ? B() : C() - if .random() { - return x - } else { - return y - } -} - -// MARK: Opaque result types - -protocol P {} -extension Int : P {} - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -func testOpaqueReturn1() -> some P { - if .random() { - 0 - } else { - 1 - } -} - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -func testOpaqueReturn2() -> some P { - if .random() { - 0 - } else { - fatalError() - } -} - -// MARK: Result builders - -enum Either { - case first(T), second(U) -} - -@resultBuilder -struct Builder { - static func buildBlock(_ x: T) -> T { x } - static func buildBlock(_ x: T, _ y: U) -> (T, U) { (x, y) } - - static func buildEither(first x: T) -> Either { .first(x) } - static func buildEither(second x: U) -> Either { .second(x) } - - static func buildExpression(_ x: Double) -> Double { x } - static func buildExpression(_ x: T) -> T { x } -} - -@Builder -func singleExprBuilder() -> Either { - if .random() { - "" - } else { - 1 - } -} - -@Builder -func builderStaticMember() -> (Either, Double) { - if .random() { - "" - } else { - 1 - } - .pi // This becomes a static member ref, not a member on an if expression. -} - -@Builder -func builderNotPostfix() -> (Either, Bool) { - if .random() { "" } else { 1 } !.random() // expected-error {{consecutive statements on a line must be separated by ';'}} -} - -@Builder -func builderWithBinding() -> Either { - // Make sure the binding gets type-checked as an if expression, but the - // other if block gets type-checked as a stmt. - let str = if .random() { "a" } else { "b" } - if .random() { - str - } else { - 1 - } -} - -func builderInClosure() { - func build(@Builder _ fn: () -> Either) {} - build { - if .random() { - "" - } else { - 1 - } - } -} diff --git a/test/Constraints/if_switch_expr_cgfloat_double_conversion.swift b/test/Constraints/if_switch_expr_cgfloat_double_conversion.swift deleted file mode 100644 index b1fa52540a979..0000000000000 --- a/test/Constraints/if_switch_expr_cgfloat_double_conversion.swift +++ /dev/null @@ -1,47 +0,0 @@ -// RUN: %target-typecheck-verify-swift %clang-importer-sdk - -// REQUIRES: objc_interop - -import Foundation - -func testReturn1(_ d: Double) -> CGFloat { - if .random() { d } else { 0 } -} - -func testReturn2(_ d: Double) -> CGFloat { - return if .random() { d } else { 0 } -} - -func testReturn3(_ d: Double) -> CGFloat { - switch Bool.random() { case true: d case false: 0.0 } -} - -func testClosure(_ d: CGFloat) { - func fn(_: () -> Double) {} - fn { - if .random() { d } else { 0.0 } - } - fn { - if .random() { 0 } else { d } - } - fn { - if .random() { d } else { d } - } - fn { - return if .random() { d } else { d } - } - fn { - switch Bool.random() { - case true: - d - case false: - 0.0 - } - } -} - -func testAssignment(_ d: CGFloat) -> Double { - let d1: Double = if .random() { d } else { 0.0 } - let d2: Double = switch Bool.random() { case true: d case false: 0.0 } - return .random() ? d1 : d2 -} diff --git a/test/Constraints/rdar105080067.swift b/test/Constraints/rdar105080067.swift deleted file mode 100644 index 4235129140602..0000000000000 --- a/test/Constraints/rdar105080067.swift +++ /dev/null @@ -1,6 +0,0 @@ -// RUN: %target-typecheck-verify-swift - -// rdar://105080067 - This isn't currently allowed but make sure we don't -// emit a spurious "cannot reference invalid declaration" error. -for b in [true] where if b { true } else { false } {} -// expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} diff --git a/test/Constraints/switch_expr.swift b/test/Constraints/switch_expr.swift deleted file mode 100644 index ae7bc8f62434f..0000000000000 --- a/test/Constraints/switch_expr.swift +++ /dev/null @@ -1,679 +0,0 @@ -// RUN: %target-typecheck-verify-swift - -enum E { - case e - case f - case g(Int) -} - -func testDotSyntax1() -> E { - switch Bool.random() { case true: .e case false: .f } -} -func testDotSyntax2() -> E? { - switch Bool.random() { case true: .e case false: .f } -} -func testDotSyntax3() -> E? { - switch Bool.random() { case true: .e case false: .none } -} -func testDotSyntax4() -> Int { - let i = switch Bool.random() { case true: 0 case false: .random() } - // expected-error@-1 {{cannot infer contextual base in reference to member 'random'}} - - return i -} - -let testVar1: E = switch Bool.random() { case true: .e case false: .f } -let testVar2: E? = switch Bool.random() { case true: .e case false: .f } -let testVar3: E? = switch Bool.random() { case true: .e case false: .none } -let testVar4: E? = switch Bool.random() { case true: nil case false: .e } - -let testVar5 = switch Bool.random() { case true: 0 case false: 1.0 } -// expected-error@-1 {{branches have mismatching types 'Int' and 'Double'}} - -let testVar6: Double = switch Bool.random() { case true: 0 case false: 1.0 } - -let testVar7: Double = switch 0 { - case 1: 0 + 1 - case 2: 1.0 + 3 - default: 9 + 0.0 -} - -let testContextualMismatch1: String = switch Bool.random() { case true: 1 case false: "" } -// expected-error@-1 {{cannot convert value of type 'Int' to specified type 'String'}} - -let testContextualMismatch2: String = switch Bool.random() { case true: 1 case false: 2 } -// expected-error@-1 {{cannot convert value of type 'Int' to specified type 'String'}} - -func proposalExample1(_ x: Int) -> Float { - let y = switch x { - case 0..<0x80: 1 // expected-error {{branches have mismatching types 'Int' and 'Double'}} - case 0x80..<0x0800: 2.0 - case 0x0800..<0x1_0000: 3.0 - default: 4.5 - } - return y -} - -func proposalExample2(_ x: Int) -> Float { - let y: Float = switch x { - case 0..<0x80: 1 - case 0x80..<0x0800: 2.0 - case 0x0800..<0x1_0000: 3.0 - default: 4.5 - } - return y -} - -enum Node { case B, R } - -enum Tree { - indirect case node(Node, Tree, Tree, Tree) - case leaf - - func proposalExample3(_ z: Tree, d: Tree) -> Tree { - switch self { - case let .node(.B, .node(.R, .node(.R, a, x, b), y, c), z, d): - .node(.R, .node(.B,a,x,b),y,.node(.B,c,z,d)) - case let .node(.B, .node(.R, a, x, .node(.R, b, y, c)), z, d): - .node(.R, .node(.B,a,x,b),y,.node(.B,c,z,d)) - case let .node(.B, a, x, .node(.R, .node(.R, b, y, c), z, d)): - .node(.R, .node(.B,a,x,b),y,.node(.B,c,z,d)) - case let .node(.B, a, x, .node(.R, b, y, .node(.R, c, z, d))): - .node(.R, .node(.B,a,x,b),y,.node(.B,c,z,d)) - default: - self - } - } -} - -enum F { - case a(Int) -} - -func overloadedWithGenericAndInt(_ x: T) -> T { x } -func overloadedWithGenericAndInt(_ x: Int) -> Int { x } - -struct S { - var f: F - mutating func foo() -> Int { - switch f { - case .a(let x): - // Make sure we don't try and shrink, which would lead to trying to - // type-check the switch early. - overloadedWithGenericAndInt(x + x) - } - } -} - -func testSingleCaseReturn(_ f: F) -> Int { - switch f { - case .a(let i): i - } -} - -func testSingleCaseReturnClosure(_ f: F) -> Int { - let fn = { - switch f { - case .a(let i): i - } - } - return fn() -} - -func testWhereClause(_ f: F) -> Int { - switch f { - case let .a(x) where x.isMultiple(of: 2): - return 0 - default: - return 1 - } -} - -func testNestedOptional() -> Int? { - switch Bool.random() { - case true: - 1 - case false: - if .random() { - 1 - } else { - nil - } - } -} - -func testNestedOptionalSwitch() -> Int? { - switch Bool.random() { - case true: - 1 - case false: - switch Bool.random() { - case true: - 1 - case false: - nil - } - } -} - -func testNestedOptionalMismatch1() -> Int? { - switch Bool.random() { - case true: - 1 - case false: - if .random() { - 1 - } else { - "" // expected-error {{cannot convert value of type 'String' to specified type 'Int'}} - } - } -} - - -func testNestedOptionalMismatch2() -> Int { - switch Bool.random() { - case true: - 1 - case false: - if .random() { - 1 - } else { - // FIXME: Seems like we could do better here - nil // expected-error {{cannot convert value of type 'ExpressibleByNilLiteral' to specified type 'Int'}} - } - } -} - -func testAssignment() { - var d: Double = switch Bool.random() { case true: 0 case false: 1.0 } - d = switch Bool.random() { case true: 0 case false: 1 } - _ = d -} - -struct TestBadReturn { - var y = switch Bool.random() { case true: return case false: 0 } // expected-error {{return invalid outside of a func}} -} - -func testNil1(_ x: Bool) { - let _ = switch x { case true: 42 case false: nil } // expected-error {{'nil' requires a contextual type}} -} -func testNil2(_ x: Bool) { - let _ = switch x { case true: nil case false: 42 } // expected-error {{'nil' requires a contextual type}} -} - -func testNil3(_ x: Bool) { - // In this case, we allow propagating the type from the first branch into - // later branches. - let _: _? = switch x { case true: 42 case false: nil } -} -func testNil4(_ x: Bool) { - // FIXME: Bad diagnostic (#63130) - let _: _? = switch x { case true: nil case false: 42 } // expected-error {{type of expression is ambiguous without more context}} -} - -enum G { - // expected-note@-1 {{arguments to generic parameter 'T' ('Double' and 'Int') are expected to be equal}} - // expected-note@-2 {{'T' declared as parameter to type 'G'}} - case x(T), y -} - -func testUnboundGeneric1() -> G { - let x: G = switch Bool.random() { case true: .x(5) case false: .x(1) } - return x -} - -func testUnboundGeneric2() -> G { - let x: G = switch Bool.random() { case true: .x(5) case false: .y } - return x -} - -func testUnboundGeneric3() -> G { - let x: G = switch Bool.random() { case true: .y case false: .y } - // expected-error@-1 {{generic parameter 'T' could not be inferred}} - return x -} - -func testUnboundGeneric4() -> G { - let x: G = switch Bool.random() { case true: .x(5.0) case false: .x(5.0) } - return x - // expected-error@-1 {{cannot convert return expression of type 'G' to return type 'G'}} -} - -func testUnboundGeneric5() -> G { - let x: G = switch Bool.random() { case true: .x(5) case false: .x(5.0) } - // expected-error@-1 {{cannot convert value of type 'Double' to expected argument type 'Int'}} - return x -} - -func testNeverConversion1() -> Int { - switch Bool.random() { - case true: - 1 - case false: - fatalError() - } -} - -func testNeverConversion2() -> Int { - return switch Bool.random() { - case true: - 1 - case false: - fatalError() - } -} - -func testNeverConversion3() -> Int { - switch Bool.random() { - case true: - 1 - case false: - if .random() { - fatalError() - } else { - 2 - } - } -} - -func testNeverConversion4() -> Int { - return switch Bool.random() { - case true: - 1 - case false: - if .random() { - fatalError() - } else { - 2 - } - } -} - -func testNeverConversion5() -> Int { - { - switch Bool.random() { - case true: - 1 - case false: - if .random() { - fatalError() - } else { - 2 - } - } - }() -} - -func testNeverConversion6(_ e: E) -> String { - switch e { - default: - fatalError() - } -} - -func testVoidConversion() { - func foo(_ fn: () -> Void) {} - func bar(_ fn: () -> T) {} - - // Okay for an implicit return, including nested as this preserves source - // compatibility. - foo { - switch Bool.random() { - case true: - 0 // expected-warning {{integer literal is unused}} - case false: - 0 // expected-warning {{integer literal is unused}} - } - } - foo { - switch Bool.random() { - case true: - 0 // expected-warning {{integer literal is unused}} - case false: - if .random() { - 0 // expected-warning {{integer literal is unused}} - } else { - 0 // expected-warning {{integer literal is unused}} - } - } - } - foo { - switch Bool.random() { - default: - 0 // expected-warning {{integer literal is unused}} - } - } - bar { - switch Bool.random() { - case true: - 0 - case false: - 0 - } - } - bar { - switch Bool.random() { - case true: - 0 - case false: - if .random() { - 0 - } else { - 0 - } - } - } - bar { - switch Bool.random() { - case true: - () - case false: - 0 // expected-warning {{integer literal is unused}} - } - } - bar { - switch Bool.random() { - case true: - 0 // expected-warning {{integer literal is unused}} - case false: - if .random() { - () - } else { - 0 // expected-warning {{integer literal is unused}} - } - } - } - // We allow the branches to mismatch to preserve source compatibility. - // (this example is silly, but this occurs in the wild for more innocuous - // things like branches that do set insertions and removals). - bar { - switch Bool.random() { case true: 0 case false: "" } - // expected-warning@-1 {{integer literal is unused}} - // expected-warning@-2 {{string literal is unused}} - } - bar { - switch Bool.random() { - case true: - switch Bool.random() { - case true: - 0 // expected-warning {{integer literal is unused}} - case false: - [0] // expected-warning {{expression of type '[Int]' is unused}} - } - case false: - "" // expected-warning {{string literal is unused}} - } - } - bar { () -> Void in - switch Bool.random() { case true: 0 case false: "" } - // expected-warning@-1 {{integer literal is unused}} - // expected-warning@-2 {{string literal is unused}} - } - bar { () -> Void in - switch Bool.random() { - case true: - switch Bool.random() { - case true: - 0 // expected-warning {{integer literal is unused}} - case false: - [0] // expected-warning {{expression of type '[Int]' is unused}} - } - case false: - "" // expected-warning {{string literal is unused}} - } - } - bar { () -> Int in - switch Bool.random() { case true: 0 case false: "" } // expected-error {{cannot convert value of type 'String' to specified type 'Int'}} - } - bar { () -> Int in - switch Bool.random() { - case true: - switch Bool.random() { - case true: - 0 - case false: - [0] // expected-error {{cannot convert value of type '[Int]' to specified type 'Int'}} - } - case false: - "" - } - } - - // Not okay for an explicit return. - foo { - return switch Bool.random() { - case true: - 0 // expected-error {{cannot convert value of type 'Int' to specified type 'Void'}} - case false: - 0 - } - } - foo { - return switch Bool.random() { - case true: - 0 // expected-error {{cannot convert value of type 'Int' to specified type 'Void'}} - case false: - if .random() { - 0 - } else { - 0 - } - } - } - bar { - return switch Bool.random() { - case true: - 0 - case false: - 0 - } - } - bar { - return switch Bool.random() { - case true: - 0 - case false: - if .random() { - 0 - } else { - 0 - } - } - } - bar { - return switch Bool.random() { - case true: - () // expected-error {{branches have mismatching types '()' and 'Int'}} - case false: - 0 - } - } - bar { - return switch Bool.random() { - case true: - 0 - case false: - if .random() { - () // expected-error {{branches have mismatching types '()' and 'Int'}} - } else { - 0 - } - } - } - bar { - return switch Bool.random() { case true: 0 case false: "" } // expected-error {{branches have mismatching types 'Int' and 'String'}} - } - bar { - return switch Bool.random() { - case true: - switch Bool.random() { - case true: - 0 // expected-error {{branches have mismatching types 'Int' and '[Int]'}} - case false: - [0] - } - case false: - "" - } - } -} - -struct SomeError: Error {} - -func testThrowInference() { - func notThrowing(_ fn: () -> Int) {} - notThrowing { // expected-error {{invalid conversion from throwing function of type '() throws -> Int' to non-throwing function type '() -> Int'}} - switch Bool.random() { - case true: - 0 - case false: - throw SomeError() - } - } - - @discardableResult - func rethrowing(_ fn: () throws -> T) rethrows -> T { try fn() } - rethrowing { - // expected-error@-1 {{call can throw, but it is not marked with 'try' and the error is not handled}} - // expected-note@-2 {{call is to 'rethrows' function, but argument function can throw}} - switch Bool.random() { - case true: - 0 - case false: - throw SomeError() - } - } -} - -// MARK: Subtyping - -class A {} -class B : A {} -class C : A {} - -func testSubtyping1() -> A { - // We can join to A. - let x = switch Bool.random() { - case true: - B() - case false: - C() - } - let y = .random() ? B() : C() - if .random() { - return x - } else { - return y - } -} - -// MARK: Opaque result types - -protocol P {} -extension P { - @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) - func foo() -> some P { 0 } -} -extension Int : P {} -extension Never : P {} - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -func testOpaqueReturn1() -> some P { - switch Bool.random() { - case true: - 0 - case false: - 1 - } -} - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -func testOpaqueReturn2() -> some P { - switch Bool.random() { - case true: - 0 - case false: - fatalError() - } -} - -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -func testOpaqueReturn3(_ x: Int) -> some P { - switch Bool.random() { - case true: - return x.foo() - case false: - fatalError() - } -} - -// MARK: Result builders - -enum Either { - case first(T), second(U) -} - -@resultBuilder -struct Builder { - static func buildBlock(_ x: T) -> T { x } - static func buildBlock(_ x: T, _ y: U) -> (T, U) { (x, y) } - - static func buildEither(first x: T) -> Either { .first(x) } - static func buildEither(second x: U) -> Either { .second(x) } - - static func buildExpression(_ x: Double) -> Double { x } - static func buildExpression(_ x: T) -> T { x } -} - -@Builder -func singleExprBuilder() -> Either { - switch Bool.random() { - case true: - "" - case false: - 1 - } -} - -@Builder -func builderStaticMember() -> (Either, Double) { - switch Bool.random() { - case true: - "" - case false: - 1 - } - .pi // This becomes a static member ref, not a member on an if expression. -} - -@Builder -func builderNotPostfix() -> (Either, Bool) { - switch Bool.random() { case true: "" case false: 1 } !.random() // expected-error {{consecutive statements on a line must be separated by ';'}} -} - -@Builder -func builderWithBinding() -> Either { - // Make sure the binding gets type-checked as an if expression, but the - // other if block gets type-checked as a stmt. - let str = switch Bool.random() { - case true: "a" - case false: "b" - } - if .random() { - str - } else { - 1 - } -} - -func builderInClosure() { - func build(@Builder _ fn: () -> Either) {} - build { - switch Bool.random() { - case true: - "" - case false: - 1 - } - } -} diff --git a/test/PCMacro/nested_function.swift b/test/PCMacro/nested_function.swift index 382e3af3d6478..674fb920718a6 100644 --- a/test/PCMacro/nested_function.swift +++ b/test/PCMacro/nested_function.swift @@ -47,5 +47,7 @@ foo() // CHECK-NEXT: [19:3-19:8] pc before // CHECK-NEXT: [14:5-14:6] pc before // CHECK-NEXT: [14:5-14:6] pc after +// CHECK-NEXT: [14:5-14:6] pc before +// CHECK-NEXT: [14:5-14:6] pc after // CHECK-NEXT: [19:3-19:8] pc after // CHECK-NEXT: [22:1-22:6] pc after diff --git a/test/Parse/recovery.swift b/test/Parse/recovery.swift index 8f45e0dd39a0d..4885555477873 100644 --- a/test/Parse/recovery.swift +++ b/test/Parse/recovery.swift @@ -77,13 +77,10 @@ class ClassWithStaticDecls { //===--- Recovery for missing controlling expression in statements. func missingControllingExprInIf() { - if + if // expected-error {{expected expression, var, or let in 'if' condition}} if { // expected-error {{missing condition in 'if' statement}} - } // expected-error {{expected '{' after 'if' condition}} - // expected-error@-2 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - // expected-error@-3 {{cannot convert value of type 'Void' to expected condition type 'Bool'}} - // expected-error@-4 {{'if' must have an unconditional 'else' to be used as expression}} + } if // expected-error {{missing condition in 'if' statement}} { @@ -237,10 +234,10 @@ func missingControllingExprInForEach() { } func missingControllingExprInSwitch() { - switch + switch // expected-error {{expected expression in 'switch' statement}} - switch { // expected-error {{expected expression in 'switch' statement}} expected-error {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - } // expected-error {{expected '{' after 'switch' subject expression}} + switch { // expected-error {{expected expression in 'switch' statement}} expected-error {{'switch' statement body must have at least one 'case' or 'default' block}} + } switch // expected-error {{expected expression in 'switch' statement}} expected-error {{'switch' statement body must have at least one 'case' or 'default' block}} { diff --git a/test/Profiler/coverage_if_expr.swift b/test/Profiler/coverage_if_expr.swift deleted file mode 100644 index 81dfe258b98bf..0000000000000 --- a/test/Profiler/coverage_if_expr.swift +++ /dev/null @@ -1,21 +0,0 @@ -// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -emit-sorted-sil -emit-sil -module-name coverage_if_expr %s | %FileCheck %s -// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -emit-ir %s - -// CHECK-LABEL: sil_coverage_map {{.*}} "$s16coverage_if_expr0b1_C0SiyF" -func if_expr() -> Int { // CHECK: [[@LINE]]:23 -> {{[0-9]+}}:2 : 0 - if .random() { // CHECK-NEXT: [[@LINE]]:6 -> [[@LINE]]:15 : 0 - 0 // CHECK-NEXT: [[@LINE-1]]:16 -> [[@LINE+4]]:4 : 1 - // CHECK-NEXT: [[@LINE+3]]:4 -> {{[0-9]+}}:2 : 0 - - // FIXME: The below line is incorrect, it should be (0 - 1), but that's an existing bug with else ifs. - } else if .random() { // CHECK-NEXT: [[@LINE]]:13 -> [[@LINE]]:22 : 0 - 1 // CHECK-NEXT: [[@LINE-1]]:23 -> [[@LINE+1]]:4 : 2 - } else { // CHECK-NEXT: [[@LINE]]:4 -> {{[0-9]+}}:2 : 0 - - // FIXME: The below line is incorrect, it should be ((0 - 1) - 2), but that's an existing bug with else ifs. - 2 // CHECK-NEXT: [[@LINE-3]]:10 -> [[@LINE+4]]:4 : (0 - 2) - - // FIXME: Also incorrect - // CHECK-NEXT: [[@LINE+1]]:4 -> [[@LINE+2]]:2 : 0 - } // CHECK-NEXT: } -} diff --git a/test/Profiler/coverage_switch_expr.swift b/test/Profiler/coverage_switch_expr.swift deleted file mode 100644 index 7923c30f1c58b..0000000000000 --- a/test/Profiler/coverage_switch_expr.swift +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -emit-sorted-sil -emit-sil -module-name coverage_switch_expr %s | %FileCheck %s -// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -emit-ir %s - -// CHECK-LABEL: sil_coverage_map {{.*}} "$s20coverage_switch_expr0b1_C0ySiSbSgF" -func switch_expr(_ b: Bool?) -> Int { // CHECK-NEXT: [[@LINE]]:37 -> {{[0-9]+}}:2 : 0 - switch b { // CHECK-NEXT: [[@LINE]]:10 -> [[@LINE]]:11 : 0 - case true?: // CHECK-NEXT: [[@LINE]]:3 -> [[@LINE+1]]:6 : 1 - 0 - case false?: // CHECK-NEXT: [[@LINE]]:3 -> [[@LINE+1]]:6 : 2 - 1 - case nil: // CHECK-NEXT: [[@LINE]]:3 -> [[@LINE+1]]:6 : 3 - 2 // FIXME: The following is incorrect - } // CHECK-NEXT: [[@LINE]]:4 -> [[@LINE+1]]:2 : ((1 + 2) + 3) -} // CHECK-NEXT: } diff --git a/test/SILGen/if_expr.swift b/test/SILGen/if_expr.swift deleted file mode 100644 index ac85cf7bf0518..0000000000000 --- a/test/SILGen/if_expr.swift +++ /dev/null @@ -1,232 +0,0 @@ -// RUN: %target-swift-emit-silgen %s | %FileCheck %s -// RUN: %target-swift-emit-ir %s - -func foo() -> Int { - if .random() { 1 } else { 2 } -} - -// CHECK-LABEL: sil hidden [ossa] @$s7if_expr3fooSiyF : $@convention(thin) () -> Int -// CHECK: [[RESULT_STORAGE:%[0-9]+]] = alloc_stack $Int -// CHECK: [[RESULT:%[0-9]+]] = mark_uninitialized [var] [[RESULT_STORAGE]] : $*Int -// CHECK: cond_br {{%[0-9]+}}, [[TRUEBB:bb[0-9]+]], [[FALSEBB:bb[0-9]+]] -// -// CHECK: [[TRUEBB]]: -// CHECK: [[ONE_BUILTIN:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 1 -// CHECK: [[ONE:%[0-9]+]] = apply {{%[0-9]+}}([[ONE_BUILTIN]], {{%[0-9]+}}) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int -// CHECK: store [[ONE]] to [trivial] [[RESULT]] : $*Int -// CHECK: br [[EXITBB:bb[0-9]+]] -// -// CHECK: [[FALSEBB]]: -// CHECK: [[TWO_BUILTIN:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 2 -// CHECK: [[TWO:%[0-9]+]] = apply {{%[0-9]+}}([[TWO_BUILTIN]], {{%[0-9]+}}) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int -// CHECK: store [[TWO]] to [trivial] [[RESULT]] : $*Int -// CHECK: br [[EXITBB]] -// -// CHECK: [[EXITBB]]: -// CHECK: [[VAL:%[0-9]+]] = load [trivial] [[RESULT]] : $*Int -// CHECK: dealloc_stack [[RESULT_STORAGE]] : $*Int -// CHECK: return [[VAL]] : $Int - -class C {} - -func bar(_ x: C) -> C { - if .random() { x } else { C() } -} - -// CHECK-LABEL: sil hidden [ossa] @$s7if_expr3baryAA1CCADF : $@convention(thin) (@guaranteed C) -> @owned C -// CHECK: bb0([[CPARAM:%[0-9]+]] : @guaranteed $C): -// CHECK: [[RESULT_STORAGE:%[0-9]+]] = alloc_stack $C -// CHECK: [[RESULT:%[0-9]+]] = mark_uninitialized [var] [[RESULT_STORAGE]] : $*C -// CHECK: cond_br {{%[0-9]+}}, [[TRUEBB:bb[0-9]+]], [[FALSEBB:bb[0-9]+]] -// -// CHECK: [[TRUEBB]]: -// CHECK: [[C:%[0-9]+]] = copy_value [[CPARAM]] : $C -// CHECK: store [[C]] to [init] [[RESULT]] : $*C -// CHECK: br [[EXITBB:bb[0-9]+]] -// -// CHECK: [[FALSEBB]]: -// CHECK: [[CTOR:%[0-9]+]] = function_ref @$s7if_expr1CCACycfC : $@convention(method) (@thick C.Type) -> @owned C -// CHECK: [[C:%[0-9]+]] = apply [[CTOR]]({{%[0-9]+}}) : $@convention(method) (@thick C.Type) -> @owned C -// CHECK: store [[C]] to [init] [[RESULT]] : $*C -// CHECK: br [[EXITBB]] -// -// CHECK: [[EXITBB]]: -// CHECK: [[VAL:%[0-9]+]] = load [take] [[RESULT]] : $*C -// CHECK: dealloc_stack [[RESULT_STORAGE]] : $*C -// CHECK: return [[VAL]] : $C - -struct Err: Error {} - -func baz() throws -> Int { - if .random() { - 0 - } else if .random() { - throw Err() - } else { - 2 - } -} - -// CHECK-LABEL: sil hidden [ossa] @$s7if_expr3bazSiyKF : $@convention(thin) () -> (Int, @error any Error) -// CHECK: [[RESULT_STORAGE:%[0-9]+]] = alloc_stack $Int -// CHECK: [[RESULT:%[0-9]+]] = mark_uninitialized [var] [[RESULT_STORAGE]] : $*Int -// CHECK: cond_br {{%[0-9]+}}, [[TRUEBB:bb[0-9]+]], [[FALSEBB:bb[0-9]+]] -// -// CHECK: [[FALSEBB]]: -// CHECK: cond_br {{%[0-9]+}}, [[FALSETRUEBB:bb[0-9]+]], [[FALSEFALSEBB:bb[0-9]+]] -// -// CHECK: [[FALSETRUEBB]]: -// CHECK: throw {{%[0-9]+}} : $any Error -// -// CHECK: [[FALSEFALSEBB]]: -// CHECK: br [[EXITBB:bb[0-9]+]] -// -// CHECK: [[EXITBB]]: -// CHECK: [[VAL:%[0-9]+]] = load [trivial] [[RESULT]] : $*Int -// CHECK: dealloc_stack [[RESULT_STORAGE]] : $*Int -// CHECK: return [[VAL]] : $Int - -func qux() throws -> Int { - if .random() { 0 } else { try baz() } -} - -// CHECK-LABEL: sil hidden [ossa] @$s7if_expr3quxSiyKF : $@convention(thin) () -> (Int, @error any Error) -// CHECK: [[RESULT_STORAGE:%[0-9]+]] = alloc_stack $Int -// CHECK: [[RESULT:%[0-9]+]] = mark_uninitialized [var] [[RESULT_STORAGE]] : $*Int -// CHECK: cond_br {{%[0-9]+}}, [[TRUEBB:bb[0-9]+]], [[FALSEBB:bb[0-9]+]] -// -// CHECK: [[FALSEBB]]: -// CHECK: try_apply {{%[0-9]+}}() : $@convention(thin) () -> (Int, @error any Error), normal [[NORMALBB:bb[0-9]+]], error [[ERRORBB:bb[0-9]+]] -// -// CHECK: [[NORMALBB]]([[BAZVAL:%[0-9]+]] : $Int): -// CHECK: store [[BAZVAL]] to [trivial] [[RESULT]] : $*Int -// CHECK: br [[EXITBB:bb[0-9]+]] -// -// CHECK: [[EXITBB]]: -// CHECK: [[VAL:%[0-9]+]] = load [trivial] [[RESULT]] : $*Int -// CHECK: dealloc_stack [[RESULT_STORAGE]] : $*Int -// CHECK: return [[VAL]] : $Int -// -// CHECK: [[ERRORBB]]([[ERR:%[0-9]+]] : @owned $any Error): -// CHECK: dealloc_stack [[RESULT_STORAGE]] : $*Int -// CHECK: throw [[ERR]] : $any Error - -func optionalVoidCrash() { - func takesClosure(_ x: () -> T) {} - - struct S { - func bar() {} - } - - var s: S? - takesClosure { - if true { - s?.bar() - } else { - () - } - } -} - -func testClosure() throws -> Int { - let fn = { - if .random() { - 0 - } else { - try baz() - } - } - return try fn() -} - -// CHECK-LABEL: sil private [ossa] @$s7if_expr11testClosureSiyKFSiyKcfU_ : $@convention(thin) () -> (Int, @error any Error) -// CHECK: [[RESULT_STORAGE:%[0-9]+]] = alloc_stack $Int -// CHECK: [[RESULT:%[0-9]+]] = mark_uninitialized [var] [[RESULT_STORAGE]] : $*Int -// CHECK: cond_br {{%[0-9]+}}, [[TRUEBB:bb[0-9]+]], [[FALSEBB:bb[0-9]+]] -// -// CHECK: [[FALSEBB]]: -// CHECK: try_apply {{%[0-9]+}}() : $@convention(thin) () -> (Int, @error any Error), normal [[NORMALBB:bb[0-9]+]], error [[ERRORBB:bb[0-9]+]] -// -// CHECK: [[NORMALBB]]([[BAZVAL:%[0-9]+]] : $Int): -// CHECK: store [[BAZVAL]] to [trivial] [[RESULT]] : $*Int -// CHECK: br [[EXITBB:bb[0-9]+]] -// -// CHECK: [[EXITBB]]: -// CHECK: [[VAL:%[0-9]+]] = load [trivial] [[RESULT]] : $*Int -// CHECK: dealloc_stack [[RESULT_STORAGE]] : $*Int -// CHECK: return [[VAL]] : $Int -// -// CHECK: [[ERRORBB]]([[ERR:%[0-9]+]] : @owned $any Error): -// CHECK: dealloc_stack [[RESULT_STORAGE]] : $*Int -// CHECK: throw [[ERR]] : $any Error - -func testNested() throws -> Int { - if .random() { - 0 - } else { - if .random() { - throw Err() - } else { - 2 - } - } -} - -// CHECK-LABEL: sil hidden [ossa] @$s7if_expr10testNestedSiyKF : $@convention(thin) () -> (Int, @error any Error) -// CHECK: [[RESULT_STORAGE:%[0-9]+]] = alloc_stack $Int -// CHECK: [[RESULT:%[0-9]+]] = mark_uninitialized [var] [[RESULT_STORAGE]] : $*Int -// CHECK: cond_br {{%[0-9]+}}, [[TRUEBB:bb[0-9]+]], [[FALSEBB:bb[0-9]+]] -// -// CHECK: [[FALSEBB]]: -// CHECK: cond_br {{%[0-9]+}}, [[FALSETRUEBB:bb[0-9]+]], [[FALSEFALSEBB:bb[0-9]+]] -// -// CHECK: [[FALSETRUEBB]]: -// CHECK: throw {{%[0-9]+}} : $any Error -// -// CHECK: [[FALSEFALSEBB]]: -// CHECK: br [[EXITBB:bb[0-9]+]] -// -// CHECK: [[EXITBB]]: -// CHECK: [[VAL:%[0-9]+]] = load [trivial] [[RESULT]] : $*Int -// CHECK: dealloc_stack [[RESULT_STORAGE]] : $*Int -// CHECK: return [[VAL]] : $Int - -func testVar() -> Int { - let x = if .random() { 1 } else { 2 } - return x -} - -func testCatch() -> Int { - do { - let x = if .random() { - 0 - } else { - throw Err() - } - return x - } catch { - return 0 - } -} - -struct TestPropertyInit { - var x = if .random() { 1 } else { 0 } - lazy var y = if .random() { 1 } else { 0 } -} - -func testAssignment() { - var x = 0 - x = if .random() { 0 } else { 1 } - let fn = { - x = if .random() { 0 } else { 1 } - } -} - -func nestedType() throws -> Int { - if .random() { - struct S: Error {} - throw S() - } else { - 0 - } -} diff --git a/test/SILGen/switch_expr.swift b/test/SILGen/switch_expr.swift deleted file mode 100644 index 162dbe886e00f..0000000000000 --- a/test/SILGen/switch_expr.swift +++ /dev/null @@ -1,325 +0,0 @@ -// RUN: %target-swift-emit-silgen %s | %FileCheck %s -// RUN: %target-swift-emit-ir %s - -func foo() -> Int { - switch Bool.random() { - case true: - 1 - case false: - 2 - } -} - -// CHECK-LABEL: sil hidden [ossa] @$s11switch_expr3fooSiyF : $@convention(thin) () -> Int -// CHECK: [[RESULT_STORAGE:%[0-9]+]] = alloc_stack $Int -// CHECK: [[RESULT:%[0-9]+]] = mark_uninitialized [var] [[RESULT_STORAGE]] : $*Int -// CHECK: switch_value {{%[0-9]+}} : $Builtin.Int1, case {{%[0-9]+}}: [[TRUEBB:bb[0-9]+]], case {{%[0-9]+}}: [[FALSEBB:bb[0-9]+]] -// -// CHECK: [[TRUEBB]]: -// CHECK: [[ONE_BUILTIN:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 1 -// CHECK: [[ONE:%[0-9]+]] = apply {{%[0-9]+}}([[ONE_BUILTIN]], {{%[0-9]+}}) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int -// CHECK: store [[ONE]] to [trivial] [[RESULT]] : $*Int -// CHECK: br [[EXITBB:bb[0-9]+]] -// -// CHECK: [[FALSEBB]]: -// CHECK: [[TWO_BUILTIN:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 2 -// CHECK: [[TWO:%[0-9]+]] = apply {{%[0-9]+}}([[TWO_BUILTIN]], {{%[0-9]+}}) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int -// CHECK: store [[TWO]] to [trivial] [[RESULT]] : $*Int -// CHECK: br [[EXITBB]] -// -// CHECK: [[EXITBB]]: -// CHECK: [[VAL:%[0-9]+]] = load [trivial] [[RESULT]] : $*Int -// CHECK: dealloc_stack [[RESULT_STORAGE]] : $*Int -// CHECK: return [[VAL]] : $Int - -class C {} - -func bar(_ x: C) -> C { - switch Bool.random() { - case true: - x - case false: - C() - } -} - -// CHECK-LABEL: sil hidden [ossa] @$s11switch_expr3baryAA1CCADF : $@convention(thin) (@guaranteed C) -> @owned C -// CHECK: bb0([[CPARAM:%[0-9]+]] : @guaranteed $C): -// CHECK: [[RESULT_STORAGE:%[0-9]+]] = alloc_stack $C -// CHECK: [[RESULT:%[0-9]+]] = mark_uninitialized [var] [[RESULT_STORAGE]] : $*C -// CHECK: switch_value {{%[0-9]+}} : $Builtin.Int1, case {{%[0-9]+}}: [[TRUEBB:bb[0-9]+]], case {{%[0-9]+}}: [[FALSEBB:bb[0-9]+]] -// -// CHECK: [[TRUEBB]]: -// CHECK: [[C:%[0-9]+]] = copy_value [[CPARAM]] : $C -// CHECK: store [[C]] to [init] [[RESULT]] : $*C -// CHECK: br [[EXITBB:bb[0-9]+]] -// -// CHECK: [[FALSEBB]]: -// CHECK: [[CTOR:%[0-9]+]] = function_ref @$s11switch_expr1CCACycfC : $@convention(method) (@thick C.Type) -> @owned C -// CHECK: [[C:%[0-9]+]] = apply [[CTOR]]({{%[0-9]+}}) : $@convention(method) (@thick C.Type) -> @owned C -// CHECK: store [[C]] to [init] [[RESULT]] : $*C -// CHECK: br [[EXITBB]] -// -// CHECK: [[EXITBB]]: -// CHECK: [[VAL:%[0-9]+]] = load [take] [[RESULT]] : $*C -// CHECK: dealloc_stack [[RESULT_STORAGE]] : $*C -// CHECK: return [[VAL]] : $C - -struct Err: Error {} - -enum E { case a, b, c } - -func baz(_ e: E) throws -> Int { - switch e { - case .a: - 1 - case .b: - throw Err() - default: - 2 - } -} - -// CHECK-LABEL: sil hidden [ossa] @$s11switch_expr3bazySiAA1EOKF : $@convention(thin) (E) -> (Int, @error any Error) -// CHECK: [[RESULT_STORAGE:%[0-9]+]] = alloc_stack $Int -// CHECK: [[RESULT:%[0-9]+]] = mark_uninitialized [var] [[RESULT_STORAGE]] : $*Int -// CHECK: switch_enum %0 : $E, case #E.a!enumelt: [[ABB:bb[0-9]+]], case #E.b!enumelt: [[BBB:bb[0-9]+]], default [[DEFBB:bb[0-9]+]] -// -// CHECK: [[ABB]]: -// CHECK: [[ONE_BUILTIN:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 1 -// CHECK: [[ONE:%[0-9]+]] = apply {{%[0-9]+}}([[ONE_BUILTIN]], {{%[0-9]+}}) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int -// CHECK: store [[ONE]] to [trivial] [[RESULT]] : $*Int -// CHECK: br [[EXITBB:bb[0-9]+]] -// -// CHECK: [[BBB]]: -// CHECK: throw {{%[0-9]+}} : $any Error -// -// CHECK: [[DEFBB]]: -// CHECK: [[TWO_BUILTIN:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 2 -// CHECK: [[TWO:%[0-9]+]] = apply {{%[0-9]+}}([[TWO_BUILTIN]], {{%[0-9]+}}) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int -// CHECK: store [[TWO]] to [trivial] [[RESULT]] : $*Int -// CHECK: br [[EXITBB:bb[0-9]+]] -// -// CHECK: [[EXITBB]]: -// CHECK: [[VAL:%[0-9]+]] = load [trivial] [[RESULT]] : $*Int -// CHECK: dealloc_stack [[RESULT_STORAGE]] : $*Int -// CHECK: return [[VAL]] : $Int - -func qux() throws -> Int { - switch Bool.random() { - case true: - 0 - case false: - try baz(.a) - } -} - -// CHECK-LABEL: sil hidden [ossa] @$s11switch_expr3quxSiyKF : $@convention(thin) () -> (Int, @error any Error) -// CHECK: [[RESULT_STORAGE:%[0-9]+]] = alloc_stack $Int -// CHECK: [[RESULT:%[0-9]+]] = mark_uninitialized [var] [[RESULT_STORAGE]] : $*Int -// CHECK: switch_value {{%[0-9]+}} : $Builtin.Int1, case {{%[0-9]+}}: [[TRUEBB:bb[0-9]+]], case {{%[0-9]+}}: [[FALSEBB:bb[0-9]+]] -// -// CHECK: [[FALSEBB]]: -// CHECK: try_apply {{%[0-9]+}}({{%[0-9]+}}) : $@convention(thin) (E) -> (Int, @error any Error), normal [[NORMALBB:bb[0-9]+]], error [[ERRORBB:bb[0-9]+]] -// -// CHECK: [[NORMALBB]]([[BAZVAL:%[0-9]+]] : $Int): -// CHECK: store [[BAZVAL]] to [trivial] [[RESULT]] : $*Int -// CHECK: br [[EXITBB:bb[0-9]+]] -// -// CHECK: [[EXITBB]]: -// CHECK: [[VAL:%[0-9]+]] = load [trivial] [[RESULT]] : $*Int -// CHECK: dealloc_stack [[RESULT_STORAGE]] : $*Int -// CHECK: return [[VAL]] : $Int -// -// CHECK: [[ERRORBB]]([[ERR:%[0-9]+]] : @owned $any Error): -// CHECK: dealloc_stack [[RESULT_STORAGE]] : $*Int -// CHECK: throw [[ERR]] : $any Error - -func testFallthrough() throws -> Int { - switch Bool.random() { - case true: - if .random() { fallthrough } - throw Err() - case false: - 1 - } -} - -// CHECK-LABEL: sil hidden [ossa] @$s11switch_expr15testFallthroughSiyKF : $@convention(thin) () -> (Int, @error any Error) -// CHECK: [[RESULT_STORAGE:%[0-9]+]] = alloc_stack $Int -// CHECK: [[RESULT:%[0-9]+]] = mark_uninitialized [var] [[RESULT_STORAGE]] : $*Int -// CHECK: switch_value {{%[0-9]+}} : $Builtin.Int1, case {{%[0-9]+}}: [[TRUEBB:bb[0-9]+]], case {{%[0-9]+}}: [[FALSEBB:bb[0-9]+]] -// -// CHECK: [[TRUEBB]]: -// CHECK: cond_br {{.*}}, [[IFTRUEBB:bb[0-9]+]], [[IFFALSEBB:bb[0-9]+]] -// -// CHECK: [[IFTRUEBB]]: -// CHECK: br [[ACTUALFALSEBB:bb[0-9]+]] -// -// CHECK: [[IFFALSEBB]]: -// CHECK: throw {{%[0-9]+}} : $any Error -// -// CHECK: [[FALSEBB]]: -// CHECK: br [[ACTUALFALSEBB]] -// -// CHECK: [[ACTUALFALSEBB]]: -// CHECK: [[ONE_BUILTIN:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 1 -// CHECK: [[ONE:%[0-9]+]] = apply {{%[0-9]+}}([[ONE_BUILTIN]], {{%[0-9]+}}) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int -// CHECK: store [[ONE]] to [trivial] [[RESULT]] : $*Int -// CHECK: [[VAL:%[0-9]+]] = load [trivial] [[RESULT]] : $*Int -// CHECK: dealloc_stack [[RESULT_STORAGE]] : $*Int -// CHECK: return [[VAL]] : $Int - -func testClosure() throws -> Int { - let fn = { - switch Bool.random() { - case true: - 0 - case false: - try baz(.a) - } - } - return try fn() -} - -// CHECK-LABEL: sil private [ossa] @$s11switch_expr11testClosureSiyKFSiyKcfU_ : $@convention(thin) () -> (Int, @error any Error) -// CHECK: [[RESULT_STORAGE:%[0-9]+]] = alloc_stack $Int -// CHECK: [[RESULT:%[0-9]+]] = mark_uninitialized [var] [[RESULT_STORAGE]] : $*Int -// CHECK: switch_value {{%[0-9]+}} : $Builtin.Int1, case {{%[0-9]+}}: [[TRUEBB:bb[0-9]+]], case {{%[0-9]+}}: [[FALSEBB:bb[0-9]+]] -// -// CHECK: [[FALSEBB]]: -// CHECK: try_apply {{%[0-9]+}}({{%[0-9]+}}) : $@convention(thin) (E) -> (Int, @error any Error), normal [[NORMALBB:bb[0-9]+]], error [[ERRORBB:bb[0-9]+]] -// -// CHECK: [[NORMALBB]]([[BAZVAL:%[0-9]+]] : $Int): -// CHECK: store [[BAZVAL]] to [trivial] [[RESULT]] : $*Int -// CHECK: br [[EXITBB:bb[0-9]+]] -// -// CHECK: [[EXITBB]]: -// CHECK: [[VAL:%[0-9]+]] = load [trivial] [[RESULT]] : $*Int -// CHECK: dealloc_stack [[RESULT_STORAGE]] : $*Int -// CHECK: return [[VAL]] : $Int -// -// CHECK: [[ERRORBB]]([[ERR:%[0-9]+]] : @owned $any Error): -// CHECK: dealloc_stack [[RESULT_STORAGE]] : $*Int -// CHECK: throw [[ERR]] : $any Error - -func testVar1() -> Int { - let x = switch Bool.random() { - case let b where b == true: - 1 - case false: - 0 - default: - 2 - } - return x -} - -func testVar2() -> Int { - let x = switch Bool.random() { - case let b where b == true: - 1 - case false: - 0 - default: - 2 - } - return x -} - -func testCatch() -> Int { - do { - let x = switch Bool.random() { - case true: - 0 - case false: - throw Err() - } - return x - } catch { - return 0 - } -} - -struct TestPropertyInit { - var x = switch Bool.random() { - case let b where b == true: - 1 - case false: - 0 - default: - 2 - } - lazy var y = switch Bool.random() { - case let b where b == true: - 1 - case false: - 0 - default: - 2 - } -} - -func testNested(_ e: E) throws -> Int { - switch e { - case .a: - 1 - default: - switch e { - case .b: - throw Err() - default: - 2 - } - } -} - -// CHECK-LABEL: sil hidden [ossa] @$s11switch_expr10testNestedySiAA1EOKF : $@convention(thin) (E) -> (Int, @error any Error) -// CHECK: [[RESULT_STORAGE:%[0-9]+]] = alloc_stack $Int -// CHECK: [[RESULT:%[0-9]+]] = mark_uninitialized [var] [[RESULT_STORAGE]] : $*Int -// CHECK: switch_enum %0 : $E, case #E.a!enumelt: [[ABB:bb[0-9]+]], default [[DEFBB:bb[0-9]+]] -// -// CHECK: [[ABB]]: -// CHECK: [[ONE_BUILTIN:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 1 -// CHECK: [[ONE:%[0-9]+]] = apply {{%[0-9]+}}([[ONE_BUILTIN]], {{%[0-9]+}}) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int -// CHECK: store [[ONE]] to [trivial] [[RESULT]] : $*Int -// CHECK: br [[EXITBB:bb[0-9]+]] -// -// CHECK: [[DEFBB]]({{.*}}): -// CHECK: [[NESTEDRESULT_STORAGE:%[0-9]+]] = alloc_stack $Int -// CHECK: [[NESTEDRESULT:%[0-9]+]] = mark_uninitialized [var] [[NESTEDRESULT_STORAGE]] : $*Int -// CHECK: switch_enum %0 : $E, case #E.b!enumelt: [[BBB:bb[0-9]+]], default [[NESTEDDEFBB:bb[0-9]+]] - -// CHECK: [[BBB]]: -// CHECK: throw {{%[0-9]+}} : $any Error -// -// CHECK: [[NESTEDDEFBB]]({{.*}}): -// CHECK: [[TWO_BUILTIN:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 2 -// CHECK: [[TWO:%[0-9]+]] = apply {{%[0-9]+}}([[TWO_BUILTIN]], {{%[0-9]+}}) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int -// CHECK: store [[TWO]] to [trivial] [[NESTEDRESULT]] : $*Int -// CHECK: [[TMP:%[0-9]+]] = load [trivial] [[NESTEDRESULT]] : $*Int -// CHECK: store [[TMP]] to [trivial] [[RESULT]] : $*Int -// CHECK: br [[EXITBB:bb[0-9]+]] -// -// CHECK: [[EXITBB]]: -// CHECK: [[VAL:%[0-9]+]] = load [trivial] [[RESULT]] : $*Int -// CHECK: dealloc_stack [[RESULT_STORAGE]] : $*Int -// CHECK: return [[VAL]] : $Int - -func testAssignment() { - var x = 0 - x = switch Bool.random() { case true: 0 case false: 1 } - let fn = { - x = switch Bool.random() { case true: 0 case false: 1 } - } -} - -func nestedType() throws -> Int { - switch Bool.random() { - case true: - struct S: Error {} - throw S() - case false: - 0 - } -} diff --git a/test/SILOptimizer/generalized_accessors.swift b/test/SILOptimizer/generalized_accessors.swift index dd343f76f171a..dc6e60ffe0c09 100644 --- a/test/SILOptimizer/generalized_accessors.swift +++ b/test/SILOptimizer/generalized_accessors.swift @@ -358,7 +358,7 @@ struct TestExplicitReturn { if flag { stored = 2 } - return; // expected-error {{accessor must yield before returning}} + return // expected-error {{accessor must yield before returning}} if !flag { // expected-warning {{code after 'return' will never be executed}} stored = 3 diff --git a/test/SILOptimizer/if_expr.swift b/test/SILOptimizer/if_expr.swift deleted file mode 100644 index 3efb7bb3d8476..0000000000000 --- a/test/SILOptimizer/if_expr.swift +++ /dev/null @@ -1,23 +0,0 @@ -// RUN: %target-swift-emit-sil -verify %s -o /dev/null - -func takesGenericReturningFn(_ fn: () -> R) {} - -func testOuterClosureReturn() { - takesGenericReturningFn { - if .random() { - return - } else { - () - } - } -} - -func testNeverToVoid() { - takesGenericReturningFn { - if .random() { // This does not turn into an expression due to the 'do'. - fatalError() - } else { - do {} - } - } -} diff --git a/test/SILOptimizer/switch_expr.swift b/test/SILOptimizer/switch_expr.swift deleted file mode 100644 index 37d0940cd03c1..0000000000000 --- a/test/SILOptimizer/switch_expr.swift +++ /dev/null @@ -1,23 +0,0 @@ -// RUN: %target-swift-emit-sil -verify %s -o /dev/null - -func foo() -> Int { - switch Bool.random() { - case true: - 0 // expected-warning {{integer literal is unused}} - case false: - do {} - } -} // expected-error {{missing return in global function expected to return 'Int'}} - -enum E { - case x(T), y - - func foo() -> E { - switch self { - case .x: - while true {} - case .y: - fatalError() - } - } -} diff --git a/test/SourceKit/CodeComplete/complete_invalid_singlevaluestmtexpr.swift b/test/SourceKit/CodeComplete/complete_invalid_singlevaluestmtexpr.swift deleted file mode 100644 index 4c4d5858afc2f..0000000000000 --- a/test/SourceKit/CodeComplete/complete_invalid_singlevaluestmtexpr.swift +++ /dev/null @@ -1,19 +0,0 @@ - -@resultBuilder -struct Builder { - static func buildBlock(_ components: T...) -> T { - fatalError() - } -} - -@Builder -func bar() { - foo { x in - switch x { - case let .success(y): - 0 - } - } -} -// Make sure we don't crash when attempting to solve the fallback. -// RUN: %sourcekitd-test -req=complete -pos=13:15 %s -- %s diff --git a/test/expr/unary/if_expr.swift b/test/expr/unary/if_expr.swift deleted file mode 100644 index 77b4caa5e4845..0000000000000 --- a/test/expr/unary/if_expr.swift +++ /dev/null @@ -1,771 +0,0 @@ -// RUN: %target-typecheck-verify-swift -disable-availability-checking - -// MARK: Functions - -func foo() -> Int { - if .random() { 1 } else { 2 } -} - -func foo2() -> Int { - return if .random() { 1 } else { 2 } -} - -func foo3() -> Int { - if .random() { 1 } else { 2 } as Int -} - -func foo4() -> Int { - return if .random() { 1 } else { 2 } as Int -} - -func foo5() -> Int { - // We only allow coercions as a narrow case in the parser, so attempting to - // double them up is invalid. - if .random() { 1 } else { 2 } as Int as Int - // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} - // expected-error@-2 {{expected expression}} -} - -func foo6() -> Int { - let x = if .random() { 1 } else { 2 } as Int as Int - return x -} - -func foo7() -> String { - if .random() { 1 } else { 2 } as String - // expected-error@-1 {{cannot convert value of type 'Int' to specified type 'String'}} -} - -func foo8() -> Int { - return (if .random() { 1 } else { 2 } as Int) - // expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} -} - -func foo9() -> String? { - if .random() { 1 as Any } else { 2 as Any } as? String - // expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} -} - -func foo10() -> String { - if .random() { 1 as Any } else { 2 as Any } as! String - // expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} -} - -func foo11() -> Bool { - // We don't parse this. - if .random() { 1 as Any } else { 2 as Any } is String - // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} - // expected-error@-2 {{expected expression}} - // expected-error@-3 {{cannot convert value of type 'Any' to specified type 'Bool'}} -} - -func foo12() -> Bool { - let x = if .random() { 1 as Any } else { 2 as Any } is String - // expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - return x -} - -func bar() -> Int { - if .random() { - fatalError() - } else { - 2 - } -} - -func baz() -> Int { - if .random() { - "" // expected-error {{cannot convert value of type 'String' to specified type 'Int'}} - } else { - 0 - } -} - -func qux(_ x: Int?) -> Int { - if let x = x { x } else { 0 } -} - -func quux(_ x: Int?) -> Int { - if case let x? = x { x } else { 0 } -} - -func elseIf(_ x: Int?) -> Int { - if .random() { - 0 - } else if let x = x { - x - } else if .random() { - 1 - } else { - 7 + 8 - } -} - -func takesValue(_ x: T) {} - -// expected-error@+1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} -takesValue(if .random() { - 0 -} else { - 1 -}) -takesValue(if .random() { 0 } else { 1 }) -// expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - -// Cannot parse labeled if as expression. -do { - takesValue(x: if .random() { 0 } else { 1 }) - // expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - // expected-error@-2 {{extraneous argument label 'x:' in call}} - - takesValue(_: x: if .random() { 0 } else { 1 }) - // expected-error@-1 {{expected expression in list of expressions}} - // expected-error@-2 {{expected ',' separator}} - // expected-error@-3 {{cannot find 'x' in scope}} -} -func takesValueWithLabel(x: T) {} -do { - takesValueWithLabel(x: if .random() { 1 } else { 2 }) - // expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - - takesValueWithLabel(x: y: if .random() { 1 } else { 2 }) - // expected-error@-1 {{expected expression in list of expressions}} - // expected-error@-2 {{expected ',' separator}} - // expected-error@-3 {{cannot find 'y' in scope}} -} -func takesValueAndTrailingClosure(_ x: T, _ fn: () -> Int) {} -takesValueAndTrailingClosure(if .random() { 0 } else { 1 }) { 2 } -// expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - -func takesInOut(_ x: inout T) {} -takesInOut(&if .random() { 1 } else { 2 }) -// expected-error@-1 {{cannot pass immutable value of type 'Int' as inout argument}} -// expected-error@-2 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - -struct HasSubscript { - static subscript(x: Int) -> Void { () } - - subscript(x: Int...) -> Void { () } -} -HasSubscript[if .random() { 1 } else { 2 }] -// expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - -func proposalExample1(isRoot: Bool, count: Int, willExpand: Bool, maxDepth: Int) -> String { - let bullet = - if isRoot && (count == 0 || !willExpand) { "" } - else if count == 0 { "- " } - else if maxDepth <= 0 { "▹ " } - else { "▿ " } - return bullet -} - -var inAComputedVar: String { - if .random() { "a" } else { "b" } -} - -// MARK: Explicit returns - -func explicitReturn1() -> Int { - print("hello") - return if .random() { 0 } else { 1 } -} - -func explicitReturn2() -> Int { - return - if .random() { 0 } else { 1 } - // expected-warning@-1 {{expression following 'return' is treated as an argument of the 'return'}} - // expected-note@-2 {{indent the expression to silence this warning}} -} - -func explicitReturn3() -> Int { - return - if .random() { 0 } else { 1 } -} - -func explicitReturn4() { - // This used to be legal, but is now treated as a return of the if expression. - return - if .random() { 0 } else { 1 } - // expected-error@-1 {{unexpected non-void return value in void function}} - // expected-note@-2 {{did you mean to add a return type?}} -} - -func explicitReturn5() { - return; - if .random() { 0 } else { 1 } // expected-warning 2{{integer literal is unused}} -} - -func explicitReturn6() { - return () - if .random() { 0 } else { 1 } // expected-warning 2{{integer literal is unused}} -} - -var explicitReturn7: String { - return if .random() { "a" } else { "b" } -} - -struct AsPropertyInit { - var x: Int = if Bool.random() { 1 } else { 0 } - var y = if .random() { 1 } else { 0 } -} - -func testNestedAssignment() { - var x = 0 - x = if .random() { 0 } else { 1 } // Okay - let fn = { - x = if .random() { 0 } else { 1 } // Also okay - } - - // We don't allow in a nested assignment. - // TODO: We could improve this error. - print(x = if .random() { 0 } else { 1 }) - // expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - - _ = x; _ = fn -} - -struct TestFailableInit { - init?(_ x: Bool) { - let y = if x { - 0 - } else { - return nil // expected-error {{cannot 'return' in 'if' when used as expression}} - } - _ = y - } -} - -struct TestFailableInitFatalError { - init?() { - // In this case, the if does not become an expression. - if .random() { - fatalError() - } else { - return nil - } - } -} - -// MARK: Expressions - -let a = if .random() { - 0 -} else { - 1 -} - -let b = (if .random() { 1 } else { 2 }) // expected-error {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - -let c = (if .random() { 1 } else { 2 }, k: if .random() { 1 } else { 2 }) // expected-error 2{{'if' may only be used as expression in return, throw, or as the source of an assignment}} - -var d = if .random() { if .random() { 1 } else { 2 } } else { 3 } - -d = if .random() { 0 } else { 1 } - -let e = "\(if .random() { 1 } else { 2 })" // expected-error {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - -let f = { if .random() { 1 } else { 2 } } - -func throwsError() throws { - struct E: Error {} - throw if .random() { E() } else { E() } -} - -// FIXME: If we ever support this, we need to fix the premature inference of '[Any]'/'[AnyHashable: Any]'. -// The issue is that we're attempting to bind defaults to type variables before solving the conjuction. -let g = [if .random() { "a" } else { "b" }] -// expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} -// expected-error@-2 {{heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}} - -let h = [if .random() { 1 } else { 2 } : if .random() { "a" } else { "b" }] -// expected-error@-1 2{{'if' may only be used as expression in return, throw, or as the source of an assignment}} -// expected-error@-2 {{heterogeneous collection literal could only be inferred to '[AnyHashable : Any]'; add explicit type annotation if this is intentional}} - -let i = [if .random() { 1 } else { 2 }: if .random() { "a" } else { "b" }] -// expected-error@-1 2{{'if' may only be used as expression in return, throw, or as the source of an assignment}} -// expected-error@-2 {{heterogeneous collection literal could only be inferred to '[AnyHashable : Any]'; add explicit type annotation if this is intentional}} - -let j = [if .random() { 1 } else { 2 }:if .random() { "a" } else { "b" }] -// expected-error@-1 2{{'if' may only be used as expression in return, throw, or as the source of an assignment}} -// expected-error@-2 {{heterogeneous collection literal could only be inferred to '[AnyHashable : Any]'; add explicit type annotation if this is intentional}} - -let k = [if .random() { 1 } else { 2 } :if .random() { "a" } else { "b" }] -// expected-error@-1 2{{'if' may only be used as expression in return, throw, or as the source of an assignment}} -// expected-error@-2 {{heterogeneous collection literal could only be inferred to '[AnyHashable : Any]'; add explicit type annotation if this is intentional}} - -let l = if .random() { 1 } else { 2 } as Any - -let _ = type(of: if .random() { 1 } else { 2 }) // expected-error {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - -let _ = (if .random() { () } else { () }) // expected-error {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - -let _ = if .random() { 0 } // expected-error {{'if' must have an unconditional 'else' to be used as expression}} -let _ = if .random() { 0 } else if .random() { 1 } // expected-error {{'if' must have an unconditional 'else' to be used as expression}} - -func testNonExhaustiveInFunction() { - if .random() { 0 } // expected-warning {{integer literal is unused}} -} - -func testLabelRejection1() -> Int { - // This was legal before, so remains legal. - x: if .random() { 0 } else { 1 } - // expected-warning@-1 2{{integer literal is unused}} -} - -func testLabelRejection2() -> Int { - // This was never legal, so reject. - x: if .random() { 0 } else { 1 } as Int - // expected-error@-1 {{'if' cannot have a jump label when used as expression}} -} - -do { - if .random() { 1 } else { 2 } = 3 - // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} - // expected-error@-2 {{expected expression}} - // expected-warning@-3 2{{integer literal is unused}}} -} -let m: Void = if .random() { 1 } else { 2 } // expected-error {{cannot convert value of type 'Int' to specified type 'Void'}} -let n: Never = if .random() { 1 } else { 2 } // expected-error {{cannot convert value of type 'Int' to specified type 'Never'}} - -func testConditionalBinding1(_ x: Int?) -> Int { - if let x = if .random() { 0 } else { Int?.none } { // expected-error {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - x - } else { - 0 - } -} - -func testConditionalBinding2(_ x: Int?) -> Int { - if case let x? = if .random() { 0 } else { Int?.none } { // expected-error {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - x - } else { - 0 - } -} - -// MARK: Operators - -let o = !if .random() { true } else { false } // expected-error {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - -// FIXME: Shouldn't be ambiguous -let p = if .random() { 1 } else { 2 } + // expected-error {{ambiguous use of operator '+'}} - if .random() { 3 } else { 4 } + - if .random() { 5 } else { 6 } -// expected-error@-3 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} -// expected-error@-3 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} -// expected-error@-3 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - -let q = .random() ? if .random() { 1 } else { 2 } - : if .random() { 3 } else { 4 } -// expected-error@-2 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} -// expected-error@-2 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - -let r = if .random() { 1 } else { 2 }...if .random() { 1 } else { 2 } -// expected-error@-1 2{{'if' may only be used as expression in return, throw, or as the source of an assignment}} - -let s = if .random() { 1 } else { 2 } ... if .random() { 1 } else { 2 } -// expected-error@-1 2{{'if' may only be used as expression in return, throw, or as the source of an assignment}} - -// MARK: Lookup - -do { - let t = if .random() { t } else { 0 } - // expected-error@-1 {{use of local variable 't' before its declaration}} - // expected-note@-2 {{'t' declared here}} -} - -// MARK: Postfix - -// We don't allow postfix parsing. -do { - let _ = if .random() { [1] } else { [1, 2] }.count - // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} - // expected-error@-2 {{reference to member 'count' cannot be resolved without a contextual type}} - - let _ = (if .random() { [1] } else { [1, 2] }).count - // expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} -} -do { - let _ = if .random() { Int?.none } else { 1 as Int? }?.bitWidth - // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} - // expected-error@-2 {{expected expression}} - - // FIXME: The type error is likely due to not solving the conjunction before attempting default type var bindings. - let _ = (if .random() { Int?.none } else { 1 as Int? })?.bitWidth - // expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - // expected-error@-2 {{type of expression is ambiguous without more context}} -} -do { - let _ = if .random() { Int?.none } else { 1 as Int? }! - // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} - // expected-error@-2 {{expected expression}} - - let _ = (if .random() { Int?.none } else { 1 as Int? })! - // expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} -} -do { - func takesInts(_ x: Int...) {} - let _ = if .random() { takesInts } else { takesInts }(1, 2, 3) - // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} - // expected-warning@-2 {{expression of type '(Int, Int, Int)' is unused}} - - let _ = (if .random() { takesInts } else { takesInts })(1, 2, 3) - // expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} -} -func testSubscriptPostfix(_ x: HasSubscript) { - if .random() { x } else { x }[1, 2, 3] - // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} - // expected-warning@-2 {{expression of type 'HasSubscript' is unused}} - // expected-warning@-3 {{expression of type '[Int]' is unused}} - // expected-warning@-4 {{expression of type 'HasSubscript' is unused}} - - (if .random() { x } else { x })[1, 2, 3] - // expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} -} -do { - func takesClosure(_ fn: () -> Int) {} - - let _ = if .random() { takesClosure } else { takesClosure } { 3 } - // expected-error@-1 {{getter/setter can only be defined for a single variable}} - - let _ = (if .random() { takesClosure } else { takesClosure }) { 3 } - // expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} -} - -// MARK: Statements - -func stmts() { - if if .random() { true } else { false } {} - // expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - - if try if .random() { true } else { false } {} - // expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - // expected-error@-2 {{'try' may not be used on 'if' expression}} - - // expected-error@+1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - guard if .random() { true } else { false } else { - return - } - - switch if .random() { true } else { false } { // expected-error {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - case _ where if .random() { true } else { false }: // expected-error {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - break - default: - break - } - - for b in [true] where if b { true } else { false } {} // expected-error {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - - // Make sure this doesn't parse as an if expr pattern with a label. - let x = 0 - switch 0 { - case x: if .random() { 1 } else { 2 } - // expected-warning@-1 2{{integer literal is unused}} - default: - break - } -} - -// MARK: Non-expression branches - -func noElse() -> Int { - // Not an expression because no else. - if .random() { - 0 // expected-warning {{integer literal is unused}} - } - 1 // expected-warning {{integer literal is unused}} -} - -func returnBranches() -> Int { - // This is not an expression because the branches are not expressions. - if .random() { - return 0 - } else { - return 1 - } -} - -func returnBranches1() -> Int { - return if .random() { // expected-error {{cannot convert return expression of type 'Void' to return type 'Int'}} - return 0 // expected-error {{cannot 'return' in 'if' when used as expression}} - } else { - return 1 // expected-error {{cannot 'return' in 'if' when used as expression}} - } -} - -func returnBranches2() -> Int { - // We don't allow multiple expressions. - if .random() { - print("hello") - 0 // expected-warning {{integer literal is unused}} - } else { - 1 // expected-warning {{integer literal is unused}} - } -} - -func returnBranches3() -> Int { - if .random() { - print("hello") - return 0 - } else { - 1 // expected-warning {{integer literal is unused}} - } -} - -func returnBranches4() -> Int { - if .random() { return 1 } else { 0 } // expected-warning {{integer literal is unused}} -} - -struct Err: Error {} - -func returnBranches5() throws -> Int { - let i = if .random() { - // expected-warning@-1 {{constant 'i' inferred to have type 'Void', which may be unexpected}} - // expected-note@-2 {{add an explicit type annotation to silence this warning}} - return 0 // expected-error {{cannot 'return' in 'if' when used as expression}} - } else { - return 1 // expected-error {{cannot 'return' in 'if' when used as expression}} - } - let j = if .random() { - // expected-warning@-1 {{constant 'j' inferred to have type 'Void', which may be unexpected}} - // expected-note@-2 {{add an explicit type annotation to silence this warning}} - throw Err() - } else { - throw Err() - } - return i // expected-error {{cannot convert return expression of type 'Void' to return type 'Int'}} -} - -func returnBranches6() -> Int { - // We don't allow multiple expressions. - let i = if .random() { - print("hello") - 0 // expected-warning {{integer literal is unused}} - } else { // expected-error {{non-expression branch of 'if' expression may only end with a 'throw'}} - 1 - } - return i -} - -func returnBranches7() -> Int { - let i = if .random() { - print("hello") - return 0 // expected-error {{cannot 'return' in 'if' when used as expression}} - } else { - 1 - } - return i -} - -func returnBranches8() -> Int { - let i = if .random() { return 1 } else { 0 } // expected-error {{cannot 'return' in 'if' when used as expression}} - return i -} - -func returnBranches9() -> Int { - let i = if .random() { - print("hello") - if .random() {} - } else { // expected-error {{non-expression branch of 'if' expression may only end with a 'throw'}} - 1 - } - return i -} - -func returnBranches10() -> Int { - let i = if .random() { - print("hello") - if .random() { - 0 // expected-warning {{integer literal is unused}} - } else { - 2 // expected-warning {{integer literal is unused}} - } - } else { // expected-error {{non-expression branch of 'if' expression may only end with a 'throw'}} - 1 - } - return i -} - -func returnBranches11() -> Int { - let i = if .random() { - print("hello") - if .random() { - "" // expected-warning {{string literal is unused}} - } else { - 0 // expected-warning {{integer literal is unused}} - } - } else { // expected-error {{non-expression branch of 'if' expression may only end with a 'throw'}} - 1 - } - return i -} - -func returnBranches12() -> Int { - if .random() { - print("hello") - if .random() {} - } else { - 1 // expected-warning {{integer literal is unused}} - } -} - -func returnBranches13() -> Int { - if .random() { - print("hello") - if .random() { - 0 // expected-warning {{integer literal is unused}} - } else { - 2 // expected-warning {{integer literal is unused}} - } - } else { - 1 // expected-warning {{integer literal is unused}} - } -} - -func returnBranches14() -> Int { - if .random() { - print("hello") - if .random() { - "" // expected-warning {{string literal is unused}} - } else { - 0 // expected-warning {{integer literal is unused}} - } - } else { - 1 // expected-warning {{integer literal is unused}} - } -} - -func nestedType() -> Int { - if .random() { - struct S { - var x: Int - } - return S(x: 0).x - } else { - 0 // expected-warning {{integer literal is unused}} - } -} - -// MARK: Jumping - -func break1() -> Int { - switch true { - case true: - let j = if .random() { - break // expected-error {{cannot 'break' in 'if' when used as expression}} - } else { - 0 - } - return j - case false: - return 0 - } -} - -func continue1() -> Int { - for _ in 0 ... 5 { - let i = if true { continue } else { 1 } - // expected-error@-1 {{cannot 'continue' in 'if' when used as expression}} - return i - } -} - -func return1() -> Int { - // Make sure we always reject a return. - let i = if .random() { - do { - for _ in [0] { - while true { - switch 0 { - default: - return 0 // expected-error {{cannot 'return' in 'if' when used as expression}} - } - } - } - } - } else { - 0 - } - return i -} - -func return2() throws -> Int { - // In a nested function is okay though. - let i = if .random() { - func foo() { return } - throw Err() - } else { - 0 - } - return i -} - -func return3() throws -> Int { - // A nested type is also okay. - let i = if .random() { - struct Nested { - func foo() { return } - } - throw Err() - } else { - 0 - } - return i -} - -func return4() throws -> Int { - // A nested closure is also okay. - let i = if .random() { - let _ = { return } - throw Err() - } else { - 0 - } - return i -} - -// MARK: Effect specifiers - -func tryIf1() -> Int { - try if .random() { 0 } else { 1 } - // expected-error@-1 {{'try' may not be used on 'if' expression}} -} - -func tryIf2() -> Int { - let x = try if .random() { 0 } else { 1 } - // expected-error@-1 {{'try' may not be used on 'if' expression}} - return x -} - -func tryIf3() -> Int { - return try if .random() { 0 } else { 1 } - // expected-error@-1 {{'try' may not be used on 'if' expression}} -} - -func awaitIf1() async -> Int { - await if .random() { 0 } else { 1 } - // expected-error@-1 {{'await' may not be used on 'if' expression}} -} - -func awaitIf2() async -> Int { - let x = await if .random() { 0 } else { 1 } - // expected-error@-1 {{'await' may not be used on 'if' expression}} - return x -} - -func awaitIf3() async -> Int { - return await if .random() { 0 } else { 1 } - // expected-error@-1 {{'await' may not be used on 'if' expression}} -} - -func tryAwaitIf1() async throws -> Int { - try await if .random() { 0 } else { 1 } - // expected-error@-1 {{'try' may not be used on 'if' expression}} - // expected-error@-2 {{'await' may not be used on 'if' expression}} -} - -func tryAwaitIf2() async throws -> Int { - try await if .random() { 0 } else { 1 } as Int - // expected-error@-1 {{'try' may not be used on 'if' expression}} - // expected-error@-2 {{'await' may not be used on 'if' expression}} -} diff --git a/test/expr/unary/if_expr_global_self_ref.swift b/test/expr/unary/if_expr_global_self_ref.swift deleted file mode 100644 index 2f8fa98d88ebe..0000000000000 --- a/test/expr/unary/if_expr_global_self_ref.swift +++ /dev/null @@ -1,5 +0,0 @@ -// RUN: %target-typecheck-verify-swift - -let x = if .random() { x } else { 0 } -// expected-error@-1 {{cannot reference invalid declaration 'x'}} -// expected-note@-2 {{'x' declared here}} diff --git a/test/expr/unary/single_value_stmt_expr_source_breaks.swift b/test/expr/unary/single_value_stmt_expr_source_breaks.swift deleted file mode 100644 index 61a902f5998f8..0000000000000 --- a/test/expr/unary/single_value_stmt_expr_source_breaks.swift +++ /dev/null @@ -1,22 +0,0 @@ -// RUN: %target-typecheck-verify-swift - -struct Err: Error {} - -func foo() throws -> Int { - // We actually do some constant evaluation before unreachability checking, - // so this used to be legal. - switch true { - case true: - throw Err() - case false: - () // expected-error {{cannot convert value of type '()' to specified type 'Int}} - } -} - -func bar() { - // This used to be an unreachable 'if' after a return. - return - if .random() { 0 } else { 1 } - // expected-error@-1 {{unexpected non-void return value in void function}} - // expected-note@-2 {{did you mean to add a return type?}} -} diff --git a/test/expr/unary/switch_expr.swift b/test/expr/unary/switch_expr.swift deleted file mode 100644 index 9564bbd743830..0000000000000 --- a/test/expr/unary/switch_expr.swift +++ /dev/null @@ -1,1020 +0,0 @@ -// RUN: %target-typecheck-verify-swift -disable-availability-checking - -// MARK: Functions - -func foo() -> Int { - switch Bool.random() { - case true: - 1 - case false: - 2 - } -} - -func foo2() -> Int { - return switch Bool.random() { - case true: - 1 - case false: - 2 - } -} - -func foo3() -> Int { - switch Bool.random() { - case true: - 1 - case false: - 2 - } as Int -} - -func foo4() -> Int { - return switch Bool.random() { - case true: - 1 - case false: - 2 - } as Int -} - -func foo5() -> Int { - // We only allow coercions as a narrow case in the parser, so attempting to - // double them up is invalid. - switch Bool.random() { - case true: - 1 - case false: - 2 - } as Int as Int - // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} - // expected-error@-2 {{expected expression}} -} - - -func foo6() -> Int { - let x = switch Bool.random() { - case true: - 1 - case false: - 2 - } as Int as Int - return x -} - -func foo7() -> String { - switch Bool.random() { - case true: - 1 // expected-error {{cannot convert value of type 'Int' to specified type 'String'}} - case false: - 2 - } as String -} - -func foo8() -> Int { - return (switch Bool.random() { case true: 1 case false: 2 } as Int) - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} -} - -func foo9() -> String? { - switch Bool.random() { // expected-error {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - case true: - 1 as Any - case false: - 2 as Any - } as? String -} - -func foo10() -> String { - switch Bool.random() { // expected-error {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - case true: - 1 as Any - case false: - 2 as Any - } as! String -} - -func foo11() -> Bool { - // We don't parse this. - switch Bool.random() { - case true: - 1 as Any // expected-error {{cannot convert value of type 'Any' to specified type 'Bool'}} - case false: - 2 as Any - } is String - // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} - // expected-error@-2 {{expected expression}} -} - -func foo12() -> Bool { - let x = switch Bool.random() { // expected-error {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - case true: - 1 as Any - case false: - 2 as Any - } is String - return x -} - -func bar() -> Int { - switch Bool.random() { - case true: - fatalError() - case false: - 2 - } -} - -func baz() -> Int { - switch Bool.random() { - case true where switch Bool.random() { case true: false case false: true }: - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - 1 - case false where if .random() { true } else { false }: - // expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}} - 2 - default: - 3 - } -} - -func qux() -> Int { - switch Bool.random() { - case true: - "" // expected-error {{cannot convert value of type 'String' to specified type 'Int'}} - case false: - 0 - } -} - -func takesValue(_ x: T) {} - -// expected-error@+1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} -takesValue(switch Bool.random() { -case true: - 1 -case false: - 2 -}) -takesValue(switch Bool.random() { case true: 1 case false: 2 }) -// expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - -// Cannot parse labeled switch as expression. -do { - takesValue(x: switch Bool.random() { case true: 1 case false: 2 }) - // expected-error@-1 {{extraneous argument label 'x:' in call}} - // expected-error@-2 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - - takesValue(_: x: switch Bool.random() { case true: 1 case false: 2 }) - // expected-error@-1 {{expected expression in list of expressions}} - // expected-error@-2 {{expected ',' separator}} - // expected-error@-3 {{cannot find 'x' in scope}} -} -func takesValueWithLabel(x: T) {} -do { - takesValueWithLabel(x: switch Bool.random() { case true: 1 case false: 2 }) - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - - takesValueWithLabel(x: y: switch Bool.random() { case true: 1 case false: 2 }) - // expected-error@-1 {{expected expression in list of expressions}} - // expected-error@-2 {{expected ',' separator}} - // expected-error@-3 {{cannot find 'y' in scope}} -} -func takesValueAndTrailingClosure(_ x: T, _ fn: () -> Int) {} -takesValueAndTrailingClosure(switch Bool.random() { case true: 0 case false: 1 }) { 2 } -// expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - -func takesInOut(_ x: inout T) {} -takesInOut(&switch Bool.random() { case true: 1 case false: 2 }) -// expected-error@-1 {{cannot pass immutable value of type 'Int' as inout argument}} -// expected-error@-2 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - -struct HasSubscript { - static subscript(x: Int) -> Void { () } - subscript(x: Int...) -> Void { () } -} -HasSubscript[switch Bool.random() { case true: 1 case false: 2 }] -// expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - -func proposalExample1(_ x: Unicode.Scalar) -> Int { - switch x.value { - case 0..<0x80: 1 - case 0x80..<0x0800: 2 - case 0x0800..<0x1_0000: 3 - default: 4 - } -} - -func testNeverBranches1() -> Never { - switch Bool.random() { - case true: - fatalError() - case false: - fatalError() - } -} - -func testNeverBranches2() { - func bar(_ fn: () -> T) {} - bar { - switch Bool.random() { - case true: - fatalError() - case false: - fatalError() - } - } -} - -// MARK: Explicit returns - -func explicitReturn1() -> Int { - print("hello") - return switch Bool.random() { case true: 1 case false: 2 } -} - -func explicitReturn2() -> Int { - return - switch Bool.random() { case true: 1 case false: 2 } - // expected-warning@-1 {{expression following 'return' is treated as an argument of the 'return'}} - // expected-note@-2 {{indent the expression to silence this warning}} -} - -func explicitReturn3() -> Int { - return - switch Bool.random() { case true: 1 case false: 2 } -} - -func explicitReturn4() { - // This used to be legal, but is now treated as a return of the if expression. - return - switch Bool.random() { case true: 1 case false: 2 } - // expected-error@-1 {{unexpected non-void return value in void function}} - // expected-note@-2 {{did you mean to add a return type?}} -} - -func explicitReturn5() { - return; - switch Bool.random() { case true: 1 case false: 2 } // expected-warning 2{{integer literal is unused}} -} - -func explicitReturn6() { - return () - switch Bool.random() { case true: 1 case false: 2 } // expected-warning 2{{integer literal is unused}} -} - -struct AsPropertyInit { - var x: Int = switch Bool.random() { case true: 1 case false: 0 } - var y = switch Bool.random() { case true: 1 case false: 0 } -} - -func testNestedAssignment() { - var x = 0 - x = switch Bool.random() { case true: 0 case false: 1 } // Okay - let fn = { - x = switch Bool.random() { case true: 0 case false: 1 } // Also okay - } - - // We don't allow in a nested assignment. - // TODO: We could improve this error. - print(x = switch Bool.random() { case true: 0 case false: 1 }) - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - - _ = x; _ = fn -} - -struct TestFailableInit { - init?(_ x: Bool) { - let y = switch x { - case true: - 0 - case false: - return nil // expected-error {{cannot 'return' in 'switch' when used as expression}} - } - _ = y - } -} - -struct TestFailableInitFatalError { - init?(_ x: Int) { - // In this case, the switch does not become an expression. - switch x { - case 0: - fatalError() - default: - return nil - } - } -} - -// MARK: Expressions - -let a = switch Bool.random() { -case true: - 0 -case false: - 1 -} - -let b = (switch Bool.random() { case true: 1 case false: 2 }) -// expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - -let c: (Int, k: Int) = (switch Bool.random() { case true: 1 case false: 2 }, - k: switch Bool.random() { case true: 1 case false: 2 }) -// expected-error@-2 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} -// expected-error@-2 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - -let d = switch Bool.random() { -case true: - switch Bool.random() { - case true: - 1 - case false: - 2 - } -case false: - 3 -} - -let e = "\(switch Bool.random() { case true: 1 case false: 2 })" -// expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - -let f = { switch Bool.random() { case true: 1 case false: 2 } } - -func throwsError() throws { - struct E: Error {} - throw switch Bool.random() { case true: E() case false: E() } -} - -// FIXME: If we ever support this, we need to fix the premature inference of '[Any]'/'[AnyHashable: Any]'. -// The issue is that we're attempting to bind defaults to type variables before solving the conjuction. -let g = [switch Bool.random() { case true: "a" case false: "b" }] -// expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} -// expected-error@-2 {{heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}} - -let h = [switch Bool.random() { case true: 1 case false: 2 } : switch Bool.random() { case true: "a" case false: "b" }] -// expected-error@-1 2{{'switch' may only be used as expression in return, throw, or as the source of an assignment}} -// expected-error@-2 {{heterogeneous collection literal could only be inferred to '[AnyHashable : Any]'; add explicit type annotation if this is intentional}} - -let i = [switch Bool.random() { case true: 1 case false: 2 }: switch Bool.random() { case true: "a" case false: "b" }] -// expected-error@-1 2{{'switch' may only be used as expression in return, throw, or as the source of an assignment}} -// expected-error@-2 {{heterogeneous collection literal could only be inferred to '[AnyHashable : Any]'; add explicit type annotation if this is intentional}} - -let j = [switch Bool.random() { case true: 1 case false: 2 }:switch Bool.random() { case true: "a" case false: "b" }] -// expected-error@-1 2{{'switch' may only be used as expression in return, throw, or as the source of an assignment}} -// expected-error@-2 {{heterogeneous collection literal could only be inferred to '[AnyHashable : Any]'; add explicit type annotation if this is intentional}} - -let k = [switch Bool.random() { case true: 1 case false: 2 } :switch Bool.random() { case true: "a" case false: "b" }] -// expected-error@-1 2{{'switch' may only be used as expression in return, throw, or as the source of an assignment}} -// expected-error@-2 {{heterogeneous collection literal could only be inferred to '[AnyHashable : Any]'; add explicit type annotation if this is intentional}} - -let l = switch Bool.random() { case true: 1 case false: 2 } as Any - -let _ = type(of: switch Bool.random() { case true: 1 case false: 2 }) -// expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - -do { - switch Bool.random() { case true: 1 case false: 2 } = 3 - // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} - // expected-error@-2 {{expected expression}} - // expected-warning@-3 2{{integer literal is unused}} -} - -// We currently prefer to parse these as trailing closures. We may want to tell -// the user to just wrap the expression in parens. -do { - _ = (switch fatalError() {}, 1) // expected-error {{expected '{' after 'switch' subject expression}} - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - // expected-error@-2 {{extra trailing closure passed in call}} - - _ = (switch fatalError() { #if FOO - // expected-error@-1 {{extra trailing closure passed in call}} - // expected-error@-2 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - #endif - }, 0) // expected-error {{expected '{' after 'switch' subject expression}} - - _ = (switch Bool.random() { #if FOO - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - // expected-error@-2 {{cannot pass immutable value of type '() -> ()' as inout argument}} - // expected-error@-3 {{type '() -> ()' cannot conform to 'RandomNumberGenerator'}} - // expected-note@-4 {{required by static method 'random(using:)' where 'T' = '() -> ()'}} - // expected-note@-5 {{only concrete types such as structs, enums and classes can conform to protocols}} - case true: // expected-error {{'case' label can only appear inside a 'switch' statement}} - 1 - case false: // expected-error {{'case' label can only appear inside a 'switch' statement}} - 2 - #endif - }, 0) // expected-error {{expected '{' after 'switch' subject expression}} - - _ = (switch Bool.random() { #if FOO - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - // expected-error@-2 {{cannot pass immutable value of type '() -> ()' as inout argument}} - // expected-error@-3 {{type '() -> ()' cannot conform to 'RandomNumberGenerator'}} - // expected-note@-4 {{required by static method 'random(using:)' where 'T' = '() -> ()'}} - // expected-note@-5 {{only concrete types such as structs, enums and classes can conform to protocols}} - case true: // expected-error {{'case' label can only appear inside a 'switch' statement}} - 1 - case false: // expected-error {{'case' label can only appear inside a 'switch' statement}} - 2 - #else - case true: // expected-error {{'case' label can only appear inside a 'switch' statement}} - 1 - case false: // expected-error {{'case' label can only appear inside a 'switch' statement}} - 2 - #endif - }, 0) // expected-error {{expected '{' after 'switch' subject expression}} -} - -// These are syntatically okay because the #if starts on a newline. This seems -// like the common case. -_ = (switch Bool.random() { - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - #if FOO - #else -case true: - 2 -case false: - 3 - #endif -}, 0) - -_ = (switch Bool.random() { - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - // expected-error@-2 {{switch must be exhaustive}} - // expected-note@-3 {{add missing case: 'true'}} - // expected-note@-4 {{add missing case: 'false'}} - #if FOO -case true: - 0 -case false: - 1 - #else - #endif -}, 0) - -_ = (switch Bool.random() { - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - // expected-error@-2 {{switch must be exhaustive}} - // expected-note@-3 {{add missing case: 'true'}} - // expected-note@-4 {{add missing case: 'false'}} - #if FOO -case true: - 0 -case false: - 1 - #endif -}, 0) - -_ = (switch fatalError() { - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - #if FOO - #endif -}, 0) - -func testEmptySwitch() { - let x = switch fatalError() {} - // expected-warning@-1 {{constant 'x' inferred to have type 'Void', which may be unexpected}} - // expected-note@-2 {{add an explicit type annotation to silence this warning}} - - func takesClosure(_ fn: () -> T) -> T { fn() } - let y = takesClosure { switch fatalError() {} } - // expected-warning@-1 {{constant 'y' inferred to have type '()', which may be unexpected}} - // expected-note@-2 {{add an explicit type annotation to silence this warning}} - - _ = x; _ = y -} - -func testConditionalBinding1(_ x: Int?) -> Int { - if let x = switch Bool.random() { case true: 0 case false: Int?.none } { // expected-error {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - x - } else { - 0 - } -} - -func testConditionalBinding2(_ x: Int?) -> Int { - if case let x? = switch Bool.random() { case true: 0 case false: Int?.none } { // expected-error {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - x - } else { - 0 - } -} - -// MARK: Operators - -let m = !switch Bool.random() { case true: true case false: true } -// expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - -// FIXME: Shouldn't be ambiguous -let n = switch Bool.random() { case true: 1 case false: 2 } + // expected-error {{ambiguous use of operator '+'}} - switch Bool.random() { case true: 3 case false: 4 } + - switch Bool.random() { case true: 5 case false: 6 } -// expected-error@-3 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} -// expected-error@-3 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} -// expected-error@-3 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - -let p = .random() ? switch Bool.random() { case true: 1 case false: 2 } - : switch Bool.random() { case true: 3 case false: 4 } -// expected-error@-2 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} -// expected-error@-2 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - -let q = switch Bool.random() { case true: 1 case false: 2 }...switch Bool.random() { case true: 1 case false: 2 } -// expected-error@-1 2{{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - -let r = switch Bool.random() { case true: 1 case false: 2 } ... switch Bool.random() { case true: 1 case false: 2 } -// expected-error@-1 2{{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - -// MARK: Lookup - -do { - let s = switch Bool.random() { case true: s case false: 0 } - // expected-error@-1 {{use of local variable 's' before its declaration}} - // expected-note@-2 {{'s' declared here}} -} - -// MARK: Postfix - -// We don't allow postfix parsing. -do { - let _ = switch Bool.random() { case true: [1] case false: [1, 2] }.count - // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} - // expected-error@-2 {{reference to member 'count' cannot be resolved without a contextual type}} - - let _ = (switch Bool.random() { case true: [1] case false: [1, 2] }).count - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} -} -do { - let _ = switch Bool.random() { case true: Int?.none case false: 1 }?.bitWidth - // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} - // expected-error@-2 {{expected expression}} - - // FIXME: The type error is likely due to not solving the conjunction before attempting default type var bindings. - let _ = (switch Bool.random() { case true: Int?.none case false: 1 })?.bitWidth - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - // expected-error@-2 {{type of expression is ambiguous without more context}} -} -do { - let _ = switch Bool.random() { case true: Int?.none case false: 1 }! - // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} - // expected-error@-2 {{expected expression}} - - let _ = (switch Bool.random() { case true: Int?.none case false: 1 })! - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} -} -do { - func fn(_ x: Int...) {} - - let _ = switch Bool.random() { case true: fn case false: fn }(1, 2, 3) - // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} - // expected-warning@-2 {{expression of type '(Int, Int, Int)' is unused}} - - let _ = (switch Bool.random() { case true: fn case false: fn })(1, 2, 3) - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} -} -func takesSubscript(_ x: HasSubscript) { - let _ = switch Bool.random() { case true: x case false: x }[1, 2, 3] - // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} - // expected-warning@-2 {{expression of type '[Int]' is unused}} - - let _ = (switch Bool.random() { case true: x case false: x })[1, 2, 3] - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} -} -do { - func fn(_ fn: () -> Int) {} - - let _ = switch Bool.random() { case true: fn case false: fn } { 3 } - // expected-error@-1 {{getter/setter can only be defined for a single variable}} - - let _ = (switch Bool.random() { case true: fn case false: fn }) { 3 } - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} -} - -// MARK: Statements - -func stmts() { - if switch Bool.random() { case true: true case false: true } {} - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - - if try switch Bool.random() { case true: true case false: true } {} - // expected-error@-1 {{'try' may not be used on 'switch' expression}} - // expected-error@-2 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - - // expected-error@+1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - guard switch Bool.random() { case true: true case false: true } else { - return - } - - switch switch Bool.random() { case true: true case false: true } { - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - case _ where switch Bool.random() { case true: true case false: true }: - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - break - default: - break - } - - for b in [true] where switch b { case true: true case false: false } {} - // expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}} - - // Make sure this doesn't parse as a switch expr pattern with a label. - let x = 0 - switch 0 { - case x: switch Bool.random() { case true: 1 case false: 2 } - // expected-warning@-1 2{{integer literal is unused}} - default: - break - } -} - -// MARK: Non-expression branches - -func returnBranches() -> Int { - // This is not an expression because the branches are not expressions. - switch Bool.random() { - case true: - return 0 - case false: - return 1 - } -} - -func returnBranches1() -> Int { - return switch Bool.random() { // expected-error {{cannot convert return expression of type 'Void' to return type 'Int'}} - case true: - return 0 // expected-error {{cannot 'return' in 'switch' when used as expression}} - case false: - return 1 // expected-error {{cannot 'return' in 'switch' when used as expression}} - } -} - -func returnBranches2() -> Int { - // We don't allow multiple expressions. - switch Bool.random() { - case true: - print("hello") - 0 // expected-warning {{integer literal is unused}} - case false: - 1 // expected-warning {{integer literal is unused}} - } -} - -func returnBranches3() -> Int { - switch Bool.random() { - case true: - print("hello") - return 0 - case false: - 1 // expected-warning {{integer literal is unused}} - } -} - -func returnBranches4() -> Int { - switch Bool.random() { - case true: - return 1 - case false: - 0 // expected-warning {{integer literal is unused}} - } -} - -func returnBranches5() -> Int { - let i = switch Bool.random() { - // expected-warning@-1 {{constant 'i' inferred to have type 'Void', which may be unexpected}} - // expected-note@-2 {{add an explicit type annotation to silence this warning}} - case true: - return 0 // expected-error {{cannot 'return' in 'switch' when used as expression}} - case false: - return 1 // expected-error {{cannot 'return' in 'switch' when used as expression}} - } - return i // expected-error {{cannot convert return expression of type 'Void' to return type 'Int'}} -} - -func returnBranches6() -> Int { - // We don't allow multiple expressions. - let i = switch Bool.random() { - case true: - print("hello") - 0 // expected-warning {{integer literal is unused}} - // expected-error@-1 {{non-expression branch of 'switch' expression may only end with a 'throw'}} - case false: - 1 - } - return i -} - -func returnBranches7() -> Int { - let i = switch Bool.random() { - case true: - print("hello") - return 0 // expected-error {{cannot 'return' in 'switch' when used as expression}} - case false: - 1 - } - return i -} - -func returnBranches8() -> Int { - let i = switch Bool.random() { - case true: - return 1 // expected-error {{cannot 'return' in 'switch' when used as expression}} - case false: - 0 - } - return i -} - -func returnBranches9() -> Int { - let i = switch Bool.random() { - case true: - print("hello") - if .random() {} // expected-error {{non-expression branch of 'switch' expression may only end with a 'throw'}} - case false: - 1 - } - return i -} - -func returnBranches10() -> Int { - let i = switch Bool.random() { - case true: - print("hello") - switch Bool.random() { - case true: - 0 // expected-warning {{integer literal is unused}} - case false: - 2 // expected-warning {{integer literal is unused}} - } // expected-error {{non-expression branch of 'switch' expression may only end with a 'throw'}} - case false: - 1 - } - return i -} - -func returnBranches11() -> Int { - let i = switch Bool.random() { - case true: - print("hello") - switch Bool.random() { - case true: - "" // expected-warning {{string literal is unused}} - case false: - 2 // expected-warning {{integer literal is unused}} - } // expected-error {{non-expression branch of 'switch' expression may only end with a 'throw'}} - case false: - 1 - } - return i -} - -func returnBranches12() -> Int { - switch Bool.random() { - case true: - print("hello") - if .random() {} - case false: - 1 // expected-warning {{integer literal is unused}} - } -} - -func returnBranches13() -> Int { - switch Bool.random() { - case true: - print("hello") - switch Bool.random() { - case true: - 0 // expected-warning {{integer literal is unused}} - case false: - 2 // expected-warning {{integer literal is unused}} - } - case false: - 1 // expected-warning {{integer literal is unused}} - } -} - -func returnBranches14() -> Int { - switch Bool.random() { - case true: - print("hello") - switch Bool.random() { - case true: - "" // expected-warning {{string literal is unused}} - case false: - 2 // expected-warning {{integer literal is unused}} - } - case false: - 1 // expected-warning {{integer literal is unused}} - } -} - -func doStatementBranch() -> Int { - switch Bool.random() { - case true: - 0 // expected-warning {{integer literal is unused}} - case false: - do {} - } -} - -func genericReturnWhileTrueBranch() { - enum E { - case x(T), y - - func foo() -> E { - switch self { - case .x: - while true {} - case .y: - fatalError() - } - } - } -} - -struct NestedInFailableInit { - init?(_ b: Bool) { - // This is okay, it's treated as a statement. - switch b { - case true: - switch b { - case true: - return nil - case false: - fatalError() - } - case false: - fatalError() - } - } -} - -func nestedType() -> Int { - switch Bool.random() { - case true: - struct S { - var x: Int - } - return S(x: 0).x - case false: - 0 // expected-warning {{integer literal is unused}} - } -} - -// MARK: Jumping - -func break1() -> Int { - switch true { - case true: - break - case false: - 0 // expected-warning {{integer literal is unused}} - } - return 1 -} - -func fallthrough1() -> Int { - switch true { - case true: - if .random() { - fallthrough - } - return 1 - case false: - 0 // expected-warning {{integer literal is unused}} - } -} - -func fallthrough2() -> Int { - let x = switch true { - case true: - if .random() { - fallthrough - } - return 1 // expected-error {{cannot 'return' in 'switch' when used as expression}} - case false: - 0 - } - return x -} - -func breakAfterNeverExpr() -> String { - // We avoid turning this into a switch expression because of the 'break'. - switch Bool.random() { - case true: - if .random() { - fatalError() // or while true {} - break - } - return "" - case false: - fatalError() - } -} - -func nonExhaustive() -> String { - switch Bool.random() { - // expected-error@-1 {{switch must be exhaustive}} - // expected-note@-2 {{add missing case: 'false'}} - case true: - "hello" - } -} - -func nonExhaustiveInClosure() -> String { - let fn = { - switch Bool.random() { - // expected-error@-1 {{switch must be exhaustive}} - // expected-note@-2 {{add missing case: 'false'}} - case true: - "hello" - } - } - return fn() -} - -func breakToInner() -> Int { - switch Bool.random() { - case true: - // These are fine, they're inner breaks. - y: switch Bool.random() { - case true: - break - case false: - switch Bool.random() { - case true: break y - case false: break - } - } - return 0 - case false: - 1 // expected-warning {{integer literal is unused}} - } -} - -func continueToInner() -> Int { - switch Bool.random() { - case true: - // These are fine, they're inner breaks/continues. - y: for x in [0] { - for y in [""] { - if x == 0 { - break y - } else if y == "hello" { - continue y - } else if .random() { - continue - } else { - break - } - } - } - return 0 - case false: - 1 // expected-warning {{integer literal is unused}} - } -} - -// MARK: Effect specifiers - -func trySwitch1() -> Int { - try switch Bool.random() { case true: 0 case false: 1 } - // expected-error@-1 {{'try' may not be used on 'switch' expression}} -} - -func trySwitch2() -> Int { - let x = try switch Bool.random() { case true: 0 case false: 1 } - // expected-error@-1 {{'try' may not be used on 'switch' expression}} - return x -} - -func trySwitch3() -> Int { - return try switch Bool.random() { case true: 0 case false: 1 } - // expected-error@-1 {{'try' may not be used on 'switch' expression}} -} - -func awaitSwitch1() async -> Int { - await switch Bool.random() { case true: 0 case false: 1 } - // expected-error@-1 {{'await' may not be used on 'switch' expression}} -} - -func awaitSwitch2() async -> Int { - let x = await switch Bool.random() { case true: 0 case false: 1 } - // expected-error@-1 {{'await' may not be used on 'switch' expression}} - return x -} - -func awaitSwitch3() async -> Int { - return await switch Bool.random() { case true: 0 case false: 1 } - // expected-error@-1 {{'await' may not be used on 'switch' expression}} -} - -func tryAwaitSwitch1() async throws -> Int { - try await switch Bool.random() { case true: 0 case false: 1 } - // expected-error@-1 {{'try' may not be used on 'switch' expression}} - // expected-error@-2 {{'await' may not be used on 'switch' expression}} -} - -func tryAwaitSwitch2() async throws -> Int { - try await switch Bool.random() { case true: 0 case false: 1 } as Int - // expected-error@-1 {{'try' may not be used on 'switch' expression}} - // expected-error@-2 {{'await' may not be used on 'switch' expression}} -} diff --git a/test/expr/unary/switch_expr_global_self_ref.swift b/test/expr/unary/switch_expr_global_self_ref.swift deleted file mode 100644 index bd3ea629fbc11..0000000000000 --- a/test/expr/unary/switch_expr_global_self_ref.swift +++ /dev/null @@ -1,5 +0,0 @@ -// RUN: %target-typecheck-verify-swift - -let x = switch Bool.random() { case true: x case false: 0 } -// expected-error@-1 {{cannot reference invalid declaration 'x'}} -// expected-note@-2 {{'x' declared here}} diff --git a/validation-test/Sema/SwiftUI/rdar104687668.swift b/validation-test/Sema/SwiftUI/rdar104687668.swift deleted file mode 100644 index 3525d9af344cc..0000000000000 --- a/validation-test/Sema/SwiftUI/rdar104687668.swift +++ /dev/null @@ -1,35 +0,0 @@ -// RUN: %target-typecheck-verify-swift -target %target-cpu-apple-macosx10.15 -// REQUIRES: OS=macosx - -import SwiftUI - -struct A { - func bar() -> Bool { true } -} - -struct B: View { - var body: some View { fatalError() } -} - -struct C: View { - init(@ViewBuilder _: () -> T, _: () -> Void) {} - var body: some View { fatalError() } -} - -func foo(_: C, @ViewBuilder _: (C.Element) -> R) -> R { - fatalError() -} - -let arr: [A] = [] - -@ViewBuilder -func blah() -> some View { - foo(arr) { a in - let b = a.bar() - C { - if b { B() } - } _: { - if b { () } else { () } - } - } -} diff --git a/validation-test/Sema/type_checker_perf/fast/if_expr.swift.gyb b/validation-test/Sema/type_checker_perf/fast/if_expr.swift.gyb deleted file mode 100644 index b96c8df6cdf5b..0000000000000 --- a/validation-test/Sema/type_checker_perf/fast/if_expr.swift.gyb +++ /dev/null @@ -1,35 +0,0 @@ -// RUN: %scale-test --begin 10 --end 100 --step 15 --select NumLeafScopes %s -// REQUIRES: asserts,no_asan - -precedencegroup P { - associativity: right -} - -infix operator ^^^ : P -func ^^^ (lhs: Int, rhs: Int) -> Int { 0 } -func ^^^ (lhs: Int8, rhs: Int8) -> Int8 { 0 } -func ^^^ (lhs: Int16, rhs: Int16) -> Int16 { 0 } -func ^^^ (lhs: Int32, rhs: Int32) -> Int32 { 0 } -func ^^^ (lhs: Int64, rhs: Int64) -> Int64 { 0 } - -func ^^^ (lhs: UInt, rhs: UInt) -> UInt { 0 } -func ^^^ (lhs: UInt8, rhs: UInt8) -> UInt8 { 0 } -func ^^^ (lhs: UInt16, rhs: UInt16) -> UInt16 { 0 } -func ^^^ (lhs: UInt32, rhs: UInt32) -> UInt32 { 0 } -func ^^^ (lhs: UInt64, rhs: UInt64) -> UInt64 { 0 } - -func ^^^ (lhs: Double, rhs: Double) -> Double { 0 } -func ^^^ (lhs: Float, rhs: Float) -> Float { 0 } - -// Make sure solving scales linearly with the number of branches. -let _ = if .random() { - 0 ^^^ 1 ^^^ 2 -} -%for i in range(0, N): -else if .random() { - 3 ^^^ 4 ^^^ 5 -} -%end -else { - 1 -} diff --git a/validation-test/Sema/type_checker_perf/fast/switch_expr.swift.gyb b/validation-test/Sema/type_checker_perf/fast/switch_expr.swift.gyb deleted file mode 100644 index d56626f428419..0000000000000 --- a/validation-test/Sema/type_checker_perf/fast/switch_expr.swift.gyb +++ /dev/null @@ -1,34 +0,0 @@ -// RUN: %scale-test --begin 10 --end 100 --step 15 --select NumLeafScopes %s -// REQUIRES: asserts,no_asan - -precedencegroup P { - associativity: right -} - -infix operator ^^^ : P -func ^^^ (lhs: Int, rhs: Int) -> Int { 0 } -func ^^^ (lhs: Int8, rhs: Int8) -> Int8 { 0 } -func ^^^ (lhs: Int16, rhs: Int16) -> Int16 { 0 } -func ^^^ (lhs: Int32, rhs: Int32) -> Int32 { 0 } -func ^^^ (lhs: Int64, rhs: Int64) -> Int64 { 0 } - -func ^^^ (lhs: UInt, rhs: UInt) -> UInt { 0 } -func ^^^ (lhs: UInt8, rhs: UInt8) -> UInt8 { 0 } -func ^^^ (lhs: UInt16, rhs: UInt16) -> UInt16 { 0 } -func ^^^ (lhs: UInt32, rhs: UInt32) -> UInt32 { 0 } -func ^^^ (lhs: UInt64, rhs: UInt64) -> UInt64 { 0 } - -func ^^^ (lhs: Double, rhs: Double) -> Double { 0 } -func ^^^ (lhs: Float, rhs: Float) -> Float { 0 } - -// Make sure solving scales linearly with the number of branches. -let _ = switch 0 { -case -1: - 0 ^^^ 1 ^^^ 2 -%for i in range(0, N): -case ${i}: - 3 ^^^ 4 ^^^ 5 -%end -default: - 1 -}