diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index d2f21354ab52e..0dff01b97cc29 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -1107,8 +1107,14 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( } // Apply the solution to the function body. - return cast_or_null( - cs.applySolutionToBody(solutions.front(), func)); + if (auto result = cs.applySolution( + solutions.front(), + SolutionApplicationTarget(func), + /*performingDiagnostics=*/false)) { + return result->getFunctionBody(); + } + + return nullptr; } ConstraintSystem::TypeMatchResult ConstraintSystem::matchFunctionBuilder( diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 770b79c72a9c2..443f67d35fd29 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7231,14 +7231,14 @@ bool ConstraintSystem::applySolutionFixes(const Solution &solution) { /// Apply a given solution to the expression, producing a fully /// type-checked expression. -llvm::PointerUnion ConstraintSystem::applySolutionImpl( - Solution &solution, SolutionApplicationTarget target, Type convertType, - bool discardedExpr, bool performingDiagnostics) { +Optional ConstraintSystem::applySolution( + Solution &solution, SolutionApplicationTarget target, + bool performingDiagnostics) { // If any fixes needed to be applied to arrive at this solution, resolve // them to specific expressions. if (!solution.Fixes.empty()) { if (shouldSuppressDiagnostics()) - return nullptr; + return None; bool diagnosedErrorsViaFixes = applySolutionFixes(solution); // If all of the available fixes would result in a warning, @@ -7248,12 +7248,12 @@ llvm::PointerUnion ConstraintSystem::applySolutionImpl( })) { // If we already diagnosed any errors via fixes, that's it. if (diagnosedErrorsViaFixes) - return nullptr; + return None; // If we didn't manage to diagnose anything well, so fall back to // diagnosing mining the system to construct a reasonable error message. diagnoseFailureFor(target); - return nullptr; + return None; } } @@ -7261,9 +7261,13 @@ llvm::PointerUnion ConstraintSystem::applySolutionImpl( ExprWalker walker(rewriter); // Apply the solution to the target. - llvm::PointerUnion result; + SolutionApplicationTarget result = target; if (auto expr = target.getAsExpr()) { - result = expr->walk(walker); + Expr *rewrittenExpr = expr->walk(walker); + if (!rewrittenExpr) + return None; + + result.setExpr(rewrittenExpr); } else { auto fn = *target.getAsFunction(); @@ -7284,14 +7288,11 @@ llvm::PointerUnion ConstraintSystem::applySolutionImpl( }); if (!newBody) - return result; + return None; - result = newBody; + result.setFunctionBody(newBody); } - if (result.isNull()) - return result; - // If we're re-typechecking an expression for diagnostics, don't // visit closures that have non-single expression bodies. if (!performingDiagnostics) { @@ -7309,15 +7310,17 @@ llvm::PointerUnion ConstraintSystem::applySolutionImpl( // If any of them failed to type check, bail. if (hadError) - return nullptr; + return None; } - if (auto resultExpr = result.dyn_cast()) { + if (auto resultExpr = result.getAsExpr()) { Expr *expr = target.getAsExpr(); assert(expr && "Can't have expression result without expression target"); + // We are supposed to use contextual type only if it is present and // this expression doesn't represent the implicit return of the single // expression function which got deduced to be `Never`. + Type convertType = target.getExprConversionType(); auto shouldCoerceToContextualType = [&]() { return convertType && !(getType(resultExpr)->isUninhabited() && @@ -7328,19 +7331,22 @@ llvm::PointerUnion ConstraintSystem::applySolutionImpl( // If we're supposed to convert the expression to some particular type, // do so now. if (shouldCoerceToContextualType()) { - result = rewriter.coerceToType(resultExpr, convertType, - getConstraintLocator(expr)); - if (!result) - return nullptr; - } else if (getType(resultExpr)->hasLValueType() && !discardedExpr) { + resultExpr = rewriter.coerceToType(resultExpr, + simplifyType(convertType), + getConstraintLocator(expr)); + } else if (getType(resultExpr)->hasLValueType() && + !target.isDiscardedExpr()) { // We referenced an lvalue. Load it. - result = rewriter.coerceToType(resultExpr, - getType(resultExpr)->getRValueType(), - getConstraintLocator(expr)); + resultExpr = rewriter.coerceToType(resultExpr, + getType(resultExpr)->getRValueType(), + getConstraintLocator(expr)); } - if (resultExpr) - solution.setExprTypes(resultExpr); + if (!resultExpr) + return None; + + solution.setExprTypes(resultExpr); + result.setExpr(resultExpr); } rewriter.finalize(); @@ -7509,16 +7515,21 @@ ArrayRef SolutionResult::getAmbiguousSolutions() const { MutableArrayRef SolutionResult::takeAmbiguousSolutions() && { assert(getKind() == Ambiguous); + markAsDiagnosed(); return MutableArrayRef(solutions, numSolutions); } -llvm::PointerUnion SolutionApplicationTarget::walk( - ASTWalker &walker) { +SolutionApplicationTarget SolutionApplicationTarget::walk(ASTWalker &walker) { switch (kind) { - case Kind::expression: - return getAsExpr()->walk(walker); + case Kind::expression: { + SolutionApplicationTarget result = *this; + result.setExpr(getAsExpr()->walk(walker)); + return result; + } case Kind::function: - return getAsFunction()->getBody()->walk(walker); + return SolutionApplicationTarget( + *getAsFunction(), + cast_or_null(getFunctionBody()->walk(walker))); } } diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index fbae75e344bc2..9e8c3678a7f2f 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1074,7 +1074,8 @@ void ConstraintSystem::shrink(Expr *expr) { } } -static bool debugConstraintSolverForExpr(ASTContext &C, Expr *expr) { +static bool debugConstraintSolverForTarget( + ASTContext &C, SolutionApplicationTarget target) { if (C.TypeCheckerOpts.DebugConstraintSolver) return true; @@ -1082,14 +1083,13 @@ static bool debugConstraintSolverForExpr(ASTContext &C, Expr *expr) { // No need to compute the line number to find out it's not present. return false; - // Get the lines on which the expression starts and ends. + // Get the lines on which the target starts and ends. unsigned startLine = 0, endLine = 0; - if (expr->getSourceRange().isValid()) { - auto range = - Lexer::getCharSourceRangeFromSourceRange(C.SourceMgr, - expr->getSourceRange()); - startLine = C.SourceMgr.getLineNumber(range.getStart()); - endLine = C.SourceMgr.getLineNumber(range.getEnd()); + SourceRange range = target.getSourceRange(); + if (range.isValid()) { + auto charRange = Lexer::getCharSourceRangeFromSourceRange(C.SourceMgr, range); + startLine = C.SourceMgr.getLineNumber(charRange.getStart()); + endLine = C.SourceMgr.getLineNumber(charRange.getEnd()); } assert(startLine <= endLine && "expr ends before it starts?"); @@ -1107,25 +1107,44 @@ static bool debugConstraintSolverForExpr(ASTContext &C, Expr *expr) { return startBound != endBound; } -bool ConstraintSystem::solve(Expr *&expr, - Type convertType, - ExprTypeCheckListener *listener, - SmallVectorImpl &solutions, - FreeTypeVariableBinding allowFreeTypeVariables) { +/// If we aren't certain that we've emitted a diagnostic, emit a fallback +/// diagnostic. +static void maybeProduceFallbackDiagnostic( + ConstraintSystem &cs, SolutionApplicationTarget target) { + if (cs.Options.contains(ConstraintSystemFlags::SubExpressionDiagnostics) || + cs.Options.contains(ConstraintSystemFlags::SuppressDiagnostics)) + return; + + // Before producing fatal error here, let's check if there are any "error" + // diagnostics already emitted or waiting to be emitted. Because they are + // a better indication of the problem. + ASTContext &ctx = cs.getASTContext(); + if (ctx.Diags.hadAnyError() || ctx.hasDelayedConformanceErrors()) + return; + + ctx.Diags.diagnose(target.getLoc(), diag::failed_to_produce_diagnostic); +} + +Optional> ConstraintSystem::solve( + SolutionApplicationTarget &target, + ExprTypeCheckListener *listener, + FreeTypeVariableBinding allowFreeTypeVariables +) { llvm::SaveAndRestore debugForExpr( getASTContext().TypeCheckerOpts.DebugConstraintSolver, - debugConstraintSolverForExpr(getASTContext(), expr)); + debugConstraintSolverForTarget(getASTContext(), target)); /// Dump solutions for debugging purposes. - auto dumpSolutions = [&] { + auto dumpSolutions = [&](const SolutionResult &result) { // Debug-print the set of solutions. if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); - if (solutions.size() == 1) { + if (result.getKind() == SolutionResult::Success) { log << "---Solution---\n"; - solutions[0].dump(log); - } else { - for (unsigned i = 0, e = solutions.size(); i != e; ++i) { + result.getSolution().dump(log); + } else if (result.getKind() == SolutionResult::Ambiguous) { + auto solutions = result.getAmbiguousSolutions(); + for (unsigned i : indices(solutions)) { log << "--- Solution #" << i << " ---\n"; solutions[i].dump(log); } @@ -1135,45 +1154,48 @@ bool ConstraintSystem::solve(Expr *&expr, // Take up to two attempts at solving the system. The first attempts to // solve a system that is expected to be well-formed, the second kicks in - // when there is an error and attempts to salvage an ill-formed expression. + // when there is an error and attempts to salvage an ill-formed program. for (unsigned stage = 0; stage != 2; ++stage) { auto solution = (stage == 0) - ? solveImpl(expr, convertType, listener, allowFreeTypeVariables) + ? solveImpl(target, listener, allowFreeTypeVariables) : salvage(); switch (solution.getKind()) { - case SolutionResult::Success: + case SolutionResult::Success: { // Return the successful solution. - solutions.clear(); - solutions.push_back(std::move(solution).takeSolution()); - dumpSolutions(); - return false; + dumpSolutions(solution); + std::vector result; + result.push_back(std::move(solution).takeSolution()); + return std::move(result); + } case SolutionResult::Error: - return true; + maybeProduceFallbackDiagnostic(*this, target); + return None; case SolutionResult::TooComplex: - getASTContext().Diags.diagnose(expr->getLoc(), diag::expression_too_complex) - .highlight(expr->getSourceRange()); + getASTContext().Diags.diagnose( + target.getLoc(), diag::expression_too_complex) + .highlight(target.getSourceRange()); solution.markAsDiagnosed(); - return true; + return None; case SolutionResult::Ambiguous: // If salvaging produced an ambiguous result, it has already been // diagnosed. if (stage == 1) { solution.markAsDiagnosed(); - return true; + return None; } if (Options.contains( ConstraintSystemFlags::AllowUnresolvedTypeVariables)) { + dumpSolutions(solution); auto ambiguousSolutions = std::move(solution).takeAmbiguousSolutions(); - solutions.assign(std::make_move_iterator(ambiguousSolutions.begin()), - std::make_move_iterator(ambiguousSolutions.end())); - dumpSolutions(); - solution.markAsDiagnosed(); - return false; + std::vector result( + std::make_move_iterator(ambiguousSolutions.begin()), + std::make_move_iterator(ambiguousSolutions.end())); + return std::move(result); } LLVM_FALLTHROUGH; @@ -1181,13 +1203,13 @@ bool ConstraintSystem::solve(Expr *&expr, case SolutionResult::UndiagnosedError: if (shouldSuppressDiagnostics()) { solution.markAsDiagnosed(); - return true; + return None; } if (stage == 1) { - diagnoseFailureFor(expr); + diagnoseFailureFor(target); solution.markAsDiagnosed(); - return true; + return None; } // Loop again to try to salvage. @@ -1200,14 +1222,13 @@ bool ConstraintSystem::solve(Expr *&expr, } SolutionResult -ConstraintSystem::solveImpl(Expr *&expr, - Type convertType, +ConstraintSystem::solveImpl(SolutionApplicationTarget &target, ExprTypeCheckListener *listener, FreeTypeVariableBinding allowFreeTypeVariables) { if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); - log << "---Constraint solving for the expression at "; - auto R = expr->getSourceRange(); + log << "---Constraint solving at "; + auto R = target.getSourceRange(); if (R.isValid()) { R.print(log, getASTContext().SourceMgr, /*PrintText=*/ false); } else { @@ -1219,6 +1240,7 @@ ConstraintSystem::solveImpl(Expr *&expr, assert(!solverState && "cannot be used directly"); // Set up the expression type checker timer. + Expr *expr = target.getAsExpr(); Timer.emplace(expr, *this); Expr *origExpr = expr; @@ -1232,14 +1254,12 @@ ConstraintSystem::solveImpl(Expr *&expr, if (auto generatedExpr = generateConstraints(expr, DC)) expr = generatedExpr; else { - if (listener) - listener->constraintGenerationFailed(expr); return SolutionResult::forError(); } // If there is a type that we're expected to convert to, add the conversion // constraint. - if (convertType) { + if (Type convertType = target.getExprConversionType()) { // Determine whether we know more about the contextual type. ContextualTypePurpose ctp = CTP_Unused; bool isOpaqueReturnType = false; @@ -1286,6 +1306,8 @@ ConstraintSystem::solveImpl(Expr *&expr, if (getExpressionTooComplex(solutions)) return SolutionResult::forTooComplex(); + target.setExpr(expr); + switch (solutions.size()) { case 0: return SolutionResult::forUndiagnosedError(); diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 472174e6de469..e5383f0a7e518 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1146,43 +1146,113 @@ class SolutionApplicationTarget { } kind; union { - Expr *expression; - AnyFunctionRef function; + struct { + Expr *expression; + + /// The type to which the expression should be converted. + Type convertType; + + /// Whether the expression result will be discarded at the end. + bool isDiscarded; + } expression; + + struct { + AnyFunctionRef function; + BraceStmt *body; + } function; }; public: - SolutionApplicationTarget(Expr *expr) { + SolutionApplicationTarget(Expr *expr, Type convertType, bool isDiscarded) { kind = Kind::expression; - expression = expr; + expression.expression = expr; + expression.convertType = convertType; + expression.isDiscarded = isDiscarded; } - SolutionApplicationTarget(AnyFunctionRef fn) { + SolutionApplicationTarget(AnyFunctionRef fn) + : SolutionApplicationTarget(fn, fn.getBody()) { } + + SolutionApplicationTarget(AnyFunctionRef fn, BraceStmt *body) { kind = Kind::function; - function = fn; + function.function = fn; + function.body = body; } Expr *getAsExpr() const { switch (kind) { case Kind::expression: - return expression; + return expression.expression; case Kind::function: return nullptr; } } + Type getExprConversionType() const { + assert(kind == Kind::expression); + return expression.convertType; + } + + void setExprConversionType(Type type) { + assert(kind == Kind::expression); + expression.convertType = type; + } + + bool isDiscardedExpr() const { + assert(kind == Kind::expression); + return expression.isDiscarded; + } + + void setExpr(Expr *expr) { + assert(kind == Kind::expression); + expression.expression = expr; + } + Optional getAsFunction() const { switch (kind) { case Kind::expression: return None; case Kind::function: - return function; + return function.function; + } + } + + BraceStmt *getFunctionBody() const { + assert(kind == Kind::function); + return function.body; + } + + void setFunctionBody(BraceStmt *stmt) { + assert(kind == Kind::function); + function.body = stmt; + } + + /// Retrieve the source range of the target. + SourceRange getSourceRange() const { + switch (kind) { + case Kind::expression: + return expression.expression->getSourceRange(); + + case Kind::function: + return function.body->getSourceRange(); + } + } + + /// Retrieve the source location for the target. + SourceLoc getLoc() const { + switch (kind) { + case Kind::expression: + return expression.expression->getLoc(); + + case Kind::function: + return function.function.getLoc(); } } /// Walk the contents of the application target. - llvm::PointerUnion walk(ASTWalker &walker); + SolutionApplicationTarget walk(ASTWalker &walker); }; enum class ConstraintSystemPhase { @@ -4036,13 +4106,11 @@ class ConstraintSystem { /// Solve the system of constraints generated from provided expression. /// - /// \param expr The expression to generate constraints from. - /// \param convertType The expected type of the expression. + /// \param target The target to generate constraints from. /// \param listener The callback to check solving progress. /// \param allowFreeTypeVariables How to bind free type variables in /// the solution. - SolutionResult solveImpl(Expr *&expr, - Type convertType, + SolutionResult solveImpl(SolutionApplicationTarget &target, ExprTypeCheckListener *listener, FreeTypeVariableBinding allowFreeTypeVariables = FreeTypeVariableBinding::Disallow); @@ -4053,26 +4121,21 @@ class ConstraintSystem { static bool preCheckExpression(Expr *&expr, DeclContext *dc, ConstraintSystem *baseCS = nullptr); - /// Solve the system of constraints generated from provided expression. - /// - /// The expression should have already been pre-checked with - /// preCheckExpression(). + /// Solve the system of constraints generated from provided target. /// - /// \param expr The expression to generate constraints from. - /// \param convertType The expected type of the expression. + /// \param target The target that we'll generate constraints from, which + /// may be updated by the solving process. /// \param listener The callback to check solving progress. - /// \param solutions The set of solutions to the system of constraints. /// \param allowFreeTypeVariables How to bind free type variables in /// the solution. /// - /// \returns true is an error occurred, false is system is consistent - /// and solutions were found. - bool solve(Expr *&expr, - Type convertType, - ExprTypeCheckListener *listener, - SmallVectorImpl &solutions, - FreeTypeVariableBinding allowFreeTypeVariables - = FreeTypeVariableBinding::Disallow); + /// \returns the set of solutions, if any were found, or \c None if an + /// error occurred. When \c None, an error has been emitted. + Optional> solve( + SolutionApplicationTarget &target, + ExprTypeCheckListener *listener, + FreeTypeVariableBinding allowFreeTypeVariables + = FreeTypeVariableBinding::Disallow); /// Solve the system of constraints. /// @@ -4146,35 +4209,16 @@ class ConstraintSystem { findBestSolution(SmallVectorImpl &solutions, bool minimize); -private: - llvm::PointerUnion applySolutionImpl( - Solution &solution, SolutionApplicationTarget target, - Type convertType, bool discardedExpr, bool performingDiagnostics); - public: - /// Apply a given solution to the expression, producing a fully - /// type-checked expression. + /// Apply a given solution to the target, producing a fully + /// type-checked target or \c None if an error occurred. /// - /// \param convertType the contextual type to which the - /// expression should be converted, if any. - /// \param discardedExpr if true, the result of the expression - /// is contextually ignored. + /// \param target the target to which the solution will be applied. /// \param performingDiagnostics if true, don't descend into bodies of /// non-single expression closures, or build curry thunks. - Expr *applySolution(Solution &solution, Expr *expr, - Type convertType, - bool discardedExpr, - bool performingDiagnostics) { - return applySolutionImpl(solution, expr, convertType, discardedExpr, - performingDiagnostics).get(); - } - - /// Apply a given solution to the body of the given function. - BraceStmt *applySolutionToBody(Solution &solution, AnyFunctionRef fn) { - return cast_or_null( - applySolutionImpl(solution, fn, Type(), false, false) - .dyn_cast()); - } + Optional applySolution( + Solution &solution, SolutionApplicationTarget target, + bool performingDiagnostics); /// Reorder the disjunctive clauses for a given expression to /// increase the likelihood that a favored constraint will be successfully diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index de6d54af3adce..0a9f90509187a 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2027,19 +2027,10 @@ bool ExprTypeCheckListener::builtConstraints(ConstraintSystem &cs, Expr *expr) { return false; } -Expr *ExprTypeCheckListener::foundSolution(Solution &solution, Expr *expr) { - return expr; -} - Expr *ExprTypeCheckListener::appliedSolution(Solution &solution, Expr *expr) { return expr; } -void ExprTypeCheckListener::preCheckFailed(Expr *expr) {} -void ExprTypeCheckListener::constraintGenerationFailed(Expr *expr) {} -void ExprTypeCheckListener::applySolutionFailed(Solution &solution, - Expr *expr) {} - void ParentConditionalConformance::diagnoseConformanceStack( DiagnosticEngine &diags, SourceLoc loc, ArrayRef conformances) { @@ -2067,89 +2058,6 @@ bool GenericRequirementsCheckListener::diagnoseUnsatisfiedRequirement( return false; } -/// Sometimes constraint solver fails without producing any diagnostics, -/// that leads to crashes down the line in AST Verifier or SILGen -/// which, as a result, are much harder to figure out. -/// -/// This class is intended to guard against situations like that by -/// keeping track of failures of different type-check phases, and -/// emitting fallback fatal error if any of them fail without producing -/// error diagnostic, and there were no errors emitted or scheduled to be -/// emitted previously. -class FallbackDiagnosticListener : public ExprTypeCheckListener { - ASTContext &Context; - TypeCheckExprOptions Options; - ExprTypeCheckListener *BaseListener; - -public: - FallbackDiagnosticListener(ASTContext &ctx, TypeCheckExprOptions options, - ExprTypeCheckListener *base) - : Context(ctx), Options(options), BaseListener(base) {} - - bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { - return BaseListener ? BaseListener->builtConstraints(cs, expr) : false; - } - - Expr *foundSolution(Solution &solution, Expr *expr) override { - return BaseListener ? BaseListener->foundSolution(solution, expr) : expr; - } - - Expr *appliedSolution(Solution &solution, Expr *expr) override { - return BaseListener ? BaseListener->appliedSolution(solution, expr) : expr; - } - - void preCheckFailed(Expr *expr) override { - if (BaseListener) - BaseListener->preCheckFailed(expr); - maybeProduceFallbackDiagnostic(expr); - } - - void constraintGenerationFailed(Expr *expr) override { - if (BaseListener) - BaseListener->constraintGenerationFailed(expr); - maybeProduceFallbackDiagnostic(expr); - } - - void applySolutionFailed(Solution &solution, Expr *expr) override { - if (BaseListener) - BaseListener->applySolutionFailed(solution, expr); - - if (hadAnyErrors()) - return; - - // If solution involves invalid or incomplete conformances that's - // a probable cause of failure to apply it without producing an error, - // which is going to be diagnosed later, so let's not produce - // fallback diagnostic in this case. - if (llvm::any_of( - solution.Conformances, - [](const std::pair - &conformance) -> bool { - auto &ref = conformance.second; - return ref.isConcrete() && ref.getConcrete()->isInvalid(); - })) - return; - - maybeProduceFallbackDiagnostic(expr); - } - -private: - bool hadAnyErrors() const { return Context.Diags.hadAnyError(); } - - void maybeProduceFallbackDiagnostic(Expr *expr) const { - if (Options.contains(TypeCheckExprFlags::SubExpressionDiagnostics) || - DiagnosticSuppression::isEnabled(Context.Diags)) - return; - - // Before producing fatal error here, let's check if there are any "error" - // diagnostics already emitted or waiting to be emitted. Because they are - // a better indication of the problem. - if (!(hadAnyErrors() || Context.hasDelayedConformanceErrors())) - Context.Diags.diagnose(expr->getLoc(), - diag::failed_to_produce_diagnostic); - } -}; - #pragma mark High-level entry points Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, TypeLoc convertType, @@ -2158,25 +2066,12 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, ExprTypeCheckListener *listener, ConstraintSystem *baseCS) { auto &Context = dc->getASTContext(); - FallbackDiagnosticListener diagListener(Context, options, listener); - return typeCheckExpressionImpl(expr, dc, convertType, convertTypePurpose, - options, diagListener, baseCS); -} - -Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, - TypeLoc convertType, - ContextualTypePurpose convertTypePurpose, - TypeCheckExprOptions options, - ExprTypeCheckListener &listener, - ConstraintSystem *baseCS) { - auto &Context = dc->getASTContext(); FrontendStatsTracer StatsTracer(Context.Stats, "typecheck-expr", expr); PrettyStackTraceExpr stackTrace(Context, "type-checking", expr); // First, pre-check the expression, validating any types that occur in the // expression and folding sequence expressions. if (ConstraintSystem::preCheckExpression(expr, dc, baseCS)) { - listener.preCheckFailed(expr); return Type(); } @@ -2243,23 +2138,25 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, convertTo = getOptionalType(expr->getLoc(), var); } - SmallVector viable; // Attempt to solve the constraint system. - if (cs.solve(expr, convertTo, &listener, viable, allowFreeTypeVariables)) + SolutionApplicationTarget target( + expr, convertTo, + options.contains(TypeCheckExprFlags::IsDiscarded)); + auto viable = cs.solve(target, listener, allowFreeTypeVariables); + if (!viable) return Type(); // If the client allows the solution to have unresolved type expressions, // check for them now. We cannot apply the solution with unresolved TypeVars, // because they will leak out into arbitrary places in the resultant AST. if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables) && - (viable.size() != 1 || + (viable->size() != 1 || (convertType.getType() && convertType.getType()->hasUnresolvedType()))) { return ErrorType::get(Context); } - auto result = expr; - auto &solution = viable[0]; - result = listener.foundSolution(solution, result); + auto result = target.getAsExpr(); + auto &solution = (*viable)[0]; if (!result) return Type(); @@ -2267,19 +2164,20 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, cs.applySolution(solution); // Apply the solution to the expression. - result = cs.applySolution( - solution, result, convertType.getType(), - options.contains(TypeCheckExprFlags::IsDiscarded), - options.contains(TypeCheckExprFlags::SubExpressionDiagnostics)); - - if (!result) { - listener.applySolutionFailed(solution, expr); + bool performingDiagnostics = + options.contains(TypeCheckExprFlags::SubExpressionDiagnostics); + // FIXME: HACK! + target.setExprConversionType(convertType.getType()); + auto resultTarget = cs.applySolution(solution, target, performingDiagnostics); + if (!resultTarget) { // Failure already diagnosed, above, as part of applying the solution. return Type(); } + result = resultTarget->getAsExpr(); // Notify listener that we've applied the solution. - result = listener.appliedSolution(solution, result); + if (listener) + result = listener->appliedSolution(solution, result); if (!result) return Type(); @@ -2348,7 +2246,6 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc, ConstraintSystem cs(dc, ConstraintSystemFlags::SuppressDiagnostics); // Attempt to solve the constraint system. - SmallVector viable; const Type originalType = expr->getType(); const bool needClearType = originalType && originalType->hasError(); const auto recoverOriginalType = [&] () { @@ -2360,14 +2257,16 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc, // re-check. if (needClearType) expr->setType(Type()); - if (cs.solve(expr, /*convertType*/Type(), listener, viable, - allowFreeTypeVariables)) { + SolutionApplicationTarget target(expr, Type(), /*isDiscarded=*/false); + auto viable = cs.solve(target, listener, allowFreeTypeVariables); + if (!viable) { recoverOriginalType(); return Type(); } // Get the expression's simplified type. - auto &solution = viable[0]; + expr = target.getAsExpr(); + auto &solution = (*viable)[0]; auto &solutionCS = solution.getConstraintSystem(); Type exprType = solution.simplifyType(solutionCS.getType(expr)); @@ -2434,19 +2333,18 @@ void TypeChecker::getPossibleTypesOfExpressionWithoutApplying( ConstraintSystem cs(dc, options); // Attempt to solve the constraint system. - SmallVector viable; - const Type originalType = expr->getType(); if (originalType && originalType->hasError()) expr->setType(Type()); - cs.solve(expr, /*convertType*/ Type(), listener, viable, - allowFreeTypeVariables); - - for (auto &solution : viable) { - auto exprType = solution.simplifyType(cs.getType(expr)); - assert(exprType && !exprType->hasTypeVariable()); - types.insert(exprType.getPointer()); + SolutionApplicationTarget target(expr, Type(), /*isDiscarded=*/false); + if (auto viable = cs.solve(target, listener, allowFreeTypeVariables)) { + expr = target.getAsExpr(); + for (auto &solution : *viable) { + auto exprType = solution.simplifyType(cs.getType(expr)); + assert(exprType && !exprType->hasTypeVariable()); + types.insert(exprType.getPointer()); + } } } @@ -2676,16 +2574,13 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, return false; } - Expr *foundSolution(Solution &solution, Expr *expr) override { - // Figure out what type the constraints decided on. - auto ty = solution.simplifyType(initType); - initType = ty->getRValueType()->reconstituteSugar(/*recursive =*/false); - - // Just keep going. - return expr; - } - Expr *appliedSolution(Solution &solution, Expr *expr) override { + { + // Figure out what type the constraints decided on. + auto ty = solution.simplifyType(initType); + initType = ty->getRValueType()->reconstituteSugar(/*recursive =*/false); + } + // Convert the initializer to the type of the pattern. expr = solution.coerceToType(expr, initType, Locator); if (!expr) diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index c4df6c61bb254..3c643d706e25e 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -305,13 +305,6 @@ class ExprTypeCheckListener { /// constraint system, or false otherwise. virtual bool builtConstraints(constraints::ConstraintSystem &cs, Expr *expr); - /// Callback invoked once a solution has been found. - /// - /// The callback may further alter the expression, returning either a - /// new expression (to replace the result) or a null pointer to indicate - /// failure. - virtual Expr *foundSolution(constraints::Solution &solution, Expr *expr); - /// Callback invokes once the chosen solution has been applied to the /// expression. /// @@ -320,18 +313,6 @@ class ExprTypeCheckListener { /// failure. virtual Expr *appliedSolution(constraints::Solution &solution, Expr *expr); - - /// Callback invoked if expression is structurally unsound and can't - /// be correctly processed by the constraint solver. - virtual void preCheckFailed(Expr *expr); - - /// Callback invoked if constraint system failed to generate - /// constraints for a given expression. - virtual void constraintGenerationFailed(Expr *expr); - - /// Callback invoked if application of chosen solution to - /// expression has failed. - virtual void applySolutionFailed(constraints::Solution &solution, Expr *expr); }; /// A conditional conformance that implied some other requirements. That is, \c @@ -868,15 +849,6 @@ class TypeChecker final { TypeCheckExprOptions(), listener); } -private: - static Type typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, - TypeLoc convertType, - ContextualTypePurpose convertTypePurpose, - TypeCheckExprOptions options, - ExprTypeCheckListener &listener, - constraints::ConstraintSystem *baseCS); - -public: /// Type check the given expression and return its type without /// applying the solution. ///