diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 1511965adda5b..d2f21354ab52e 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -1041,7 +1041,6 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( ConstraintKind resultConstraintKind = ConstraintKind::Conversion; if (auto opaque = resultContextType->getAs()) { if (opaque->getDecl()->isOpaqueReturnTypeOfFunction(func)) { - options |= ConstraintSystemFlags::UnderlyingTypeForOpaqueReturnType; resultConstraintKind = ConstraintKind::OpaqueUnderlyingType; } } diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 3e5f899be1c3d..770b79c72a9c2 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7507,6 +7507,11 @@ ArrayRef SolutionResult::getAmbiguousSolutions() const { return makeArrayRef(solutions, numSolutions); } +MutableArrayRef SolutionResult::takeAmbiguousSolutions() && { + assert(getKind() == Ambiguous); + return MutableArrayRef(solutions, numSolutions); +} + llvm::PointerUnion SolutionApplicationTarget::walk( ASTWalker &walker) { switch (kind) { diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 071767fe08891..2cb35341bde68 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3831,7 +3831,8 @@ bool ConstraintSystem::generateConstraints(StmtCondition condition, case StmtConditionElement::CK_Boolean: { Expr *condExpr = condElement.getBoolean(); - setContextualType(condExpr, TypeLoc::withoutLoc(boolTy), CTP_Condition); + setContextualType(condExpr, TypeLoc::withoutLoc(boolTy), CTP_Condition, + /*isOpaqueReturnType=*/false); condExpr = generateConstraints(condExpr, dc); if (!condExpr) { diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 4568823340ff5..0b3346c403819 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -9064,6 +9064,59 @@ void ConstraintSystem::addConstraint(ConstraintKind kind, Type first, } } +void ConstraintSystem::addContextualConversionConstraint( + Expr *expr, ContextualTypeInfo contextualType) { + Type convertType = contextualType.getType(); + if (convertType.isNull()) + return; + + // Determine the type of the constraint. + auto constraintKind = ConstraintKind::Conversion; + switch (contextualType.purpose) { + case CTP_ReturnStmt: + case CTP_ReturnSingleExpr: + case CTP_Initialization: + if (contextualType.isOpaqueReturnType) + constraintKind = ConstraintKind::OpaqueUnderlyingType; + break; + + case CTP_CallArgument: + constraintKind = ConstraintKind::ArgumentConversion; + break; + + case CTP_YieldByReference: + // In a by-reference yield, we expect the contextual type to be an + // l-value type, so the result must be bound to that. + constraintKind = ConstraintKind::Bind; + break; + + case CTP_ArrayElement: + case CTP_AssignSource: + case CTP_CalleeResult: + case CTP_CannotFail: + case CTP_Condition: + case CTP_Unused: + case CTP_YieldByValue: + case CTP_ThrowStmt: + case CTP_EnumCaseRawValue: + case CTP_DefaultParameter: + case CTP_ClosureResult: + case CTP_DictionaryKey: + case CTP_DictionaryValue: + case CTP_CoerceOperand: + case CTP_SubscriptAssignSource: + case CTP_ForEachStmt: + break; + } + + // Add the constraint. + bool isForSingleExprFunction = (contextualType.purpose == CTP_ReturnSingleExpr); + auto *convertTypeLocator = getConstraintLocator( + expr, LocatorPathElt::ContextualType(isForSingleExprFunction)); + addConstraint(constraintKind, getType(expr), convertType, + convertTypeLocator, /*isFavored*/ true); +} + Type ConstraintSystem::addJoinConstraint( ConstraintLocator *locator, ArrayRef> inputs) { diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 94b00f9e3a435..fbae75e344bc2 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -240,7 +240,8 @@ void ConstraintSystem::applySolution(const Solution &solution) { for (const auto &contextualType : solution.contextualTypes) { if (!getContextualTypeInfo(contextualType.first)) { setContextualType(contextualType.first, contextualType.second.typeLoc, - contextualType.second.purpose); + contextualType.second.purpose, + contextualType.second.isOpaqueReturnType); } } @@ -591,7 +592,7 @@ bool ConstraintSystem::Candidate::solve( cs.Timer.emplace(E, cs); // Generate constraints for the new system. - if (auto generatedExpr = cs.generateConstraints(E)) { + if (auto generatedExpr = cs.generateConstraints(E, DC)) { E = generatedExpr; } else { // Failure to generate constraint system for sub-expression @@ -1115,80 +1116,93 @@ bool ConstraintSystem::solve(Expr *&expr, getASTContext().TypeCheckerOpts.DebugConstraintSolver, debugConstraintSolverForExpr(getASTContext(), expr)); - // Attempt to solve the constraint system. - auto solution = solveImpl(expr, - convertType, - listener, - solutions, - allowFreeTypeVariables); - - // The constraint system has failed - if (solution == SolutionKind::Error) - return true; - - // If the system is unsolved or there are multiple solutions present but - // type checker options do not allow unresolved types, let's try to salvage - if (solution == SolutionKind::Unsolved || - (solutions.size() != 1 && - !Options.contains( - ConstraintSystemFlags::AllowUnresolvedTypeVariables))) { - if (shouldSuppressDiagnostics()) - return true; + /// Dump solutions for debugging purposes. + auto dumpSolutions = [&] { + // Debug-print the set of solutions. + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { + auto &log = getASTContext().TypeCheckerDebug->getStream(); + if (solutions.size() == 1) { + log << "---Solution---\n"; + solutions[0].dump(log); + } else { + for (unsigned i = 0, e = solutions.size(); i != e; ++i) { + log << "--- Solution #" << i << " ---\n"; + solutions[i].dump(log); + } + } + } + }; - // Try to fix the system or provide a decent diagnostic. - auto salvagedResult = salvage(); - switch (salvagedResult.getKind()) { - case SolutionResult::Kind::Success: + // 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. + for (unsigned stage = 0; stage != 2; ++stage) { + auto solution = (stage == 0) + ? solveImpl(expr, convertType, listener, allowFreeTypeVariables) + : salvage(); + + switch (solution.getKind()) { + case SolutionResult::Success: + // Return the successful solution. solutions.clear(); - solutions.push_back(std::move(salvagedResult).takeSolution()); - break; - - case SolutionResult::Kind::Error: - case SolutionResult::Kind::Ambiguous: - return true; + solutions.push_back(std::move(solution).takeSolution()); + dumpSolutions(); + return false; - case SolutionResult::Kind::UndiagnosedError: - diagnoseFailureFor(expr); - salvagedResult.markAsDiagnosed(); + case SolutionResult::Error: return true; - case SolutionResult::Kind::TooComplex: + case SolutionResult::TooComplex: getASTContext().Diags.diagnose(expr->getLoc(), diag::expression_too_complex) - .highlight(expr->getSourceRange()); - salvagedResult.markAsDiagnosed(); + .highlight(expr->getSourceRange()); + solution.markAsDiagnosed(); return true; - } - // The system was salvaged; continue on as if nothing happened. - } + case SolutionResult::Ambiguous: + // If salvaging produced an ambiguous result, it has already been + // diagnosed. + if (stage == 1) { + solution.markAsDiagnosed(); + return true; + } - if (getExpressionTooComplex(solutions)) { - getASTContext().Diags.diagnose(expr->getLoc(), diag::expression_too_complex) - .highlight(expr->getSourceRange()); - return true; - } + if (Options.contains( + ConstraintSystemFlags::AllowUnresolvedTypeVariables)) { + 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; + } - if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { - auto &log = getASTContext().TypeCheckerDebug->getStream(); - if (solutions.size() == 1) { - log << "---Solution---\n"; - solutions[0].dump(log); - } else { - for (unsigned i = 0, e = solutions.size(); i != e; ++i) { - log << "--- Solution #" << i << " ---\n"; - solutions[i].dump(log); + LLVM_FALLTHROUGH; + + case SolutionResult::UndiagnosedError: + if (shouldSuppressDiagnostics()) { + solution.markAsDiagnosed(); + return true; + } + + if (stage == 1) { + diagnoseFailureFor(expr); + solution.markAsDiagnosed(); + return true; } + + // Loop again to try to salvage. + solution.markAsDiagnosed(); + continue; } } - return false; + llvm_unreachable("Loop always returns"); } -ConstraintSystem::SolutionKind +SolutionResult ConstraintSystem::solveImpl(Expr *&expr, Type convertType, ExprTypeCheckListener *listener, - SmallVectorImpl &solutions, FreeTypeVariableBinding allowFreeTypeVariables) { if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); @@ -1215,39 +1229,31 @@ ConstraintSystem::solveImpl(Expr *&expr, shrink(expr); // Generate constraints for the main system. - if (auto generatedExpr = generateConstraints(expr)) + if (auto generatedExpr = generateConstraints(expr, DC)) expr = generatedExpr; else { if (listener) listener->constraintGenerationFailed(expr); - return SolutionKind::Error; + return SolutionResult::forError(); } // If there is a type that we're expected to convert to, add the conversion // constraint. if (convertType) { - auto constraintKind = ConstraintKind::Conversion; - - auto ctp = getContextualTypePurpose(origExpr); - if ((ctp == CTP_ReturnStmt || - ctp == CTP_ReturnSingleExpr || - ctp == CTP_Initialization) - && Options.contains(ConstraintSystemFlags::UnderlyingTypeForOpaqueReturnType)) - constraintKind = ConstraintKind::OpaqueUnderlyingType; - - if (ctp == CTP_CallArgument) - constraintKind = ConstraintKind::ArgumentConversion; - - // In a by-reference yield, we expect the contextual type to be an - // l-value type, so the result must be bound to that. - if (ctp == CTP_YieldByReference) - constraintKind = ConstraintKind::Bind; - - bool isForSingleExprFunction = ctp == CTP_ReturnSingleExpr; - auto *convertTypeLocator = getConstraintLocator( - expr, LocatorPathElt::ContextualType(isForSingleExprFunction)); + // Determine whether we know more about the contextual type. + ContextualTypePurpose ctp = CTP_Unused; + bool isOpaqueReturnType = false; + if (auto contextualInfo = getContextualTypeInfo(origExpr)) { + ctp = contextualInfo->purpose; + isOpaqueReturnType = contextualInfo->isOpaqueReturnType; + } + // Substitute type variables in for unresolved types. if (allowFreeTypeVariables == FreeTypeVariableBinding::UnresolvedType) { + bool isForSingleExprFunction = (ctp == CTP_ReturnSingleExpr); + auto *convertTypeLocator = getConstraintLocator( + expr, LocatorPathElt::ContextualType(isForSingleExprFunction)); + convertType = convertType.transform([&](Type type) -> Type { if (type->is()) return createTypeVariable(convertTypeLocator, TVO_CanBindToNoEscape); @@ -1255,13 +1261,14 @@ ConstraintSystem::solveImpl(Expr *&expr, }); } - addConstraint(constraintKind, getType(expr), convertType, - convertTypeLocator, /*isFavored*/ true); + ContextualTypeInfo info{ + TypeLoc::withoutLoc(convertType), ctp, isOpaqueReturnType}; + addContextualConversionConstraint(expr, info); } // Notify the listener that we've built the constraint system. if (listener && listener->builtConstraints(*this, expr)) { - return SolutionKind::Error; + return SolutionResult::forError(); } if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { @@ -1273,11 +1280,22 @@ ConstraintSystem::solveImpl(Expr *&expr, } // Try to solve the constraint system using computed suggestions. + SmallVector solutions; solve(solutions, allowFreeTypeVariables); - // If there are no solutions let's mark system as unsolved, - // and solved otherwise even if there are multiple solutions still present. - return solutions.empty() ? SolutionKind::Unsolved : SolutionKind::Solved; + if (getExpressionTooComplex(solutions)) + return SolutionResult::forTooComplex(); + + switch (solutions.size()) { + case 0: + return SolutionResult::forUndiagnosedError(); + + case 1: + return SolutionResult::forSolved(std::move(solutions.front())); + + default: + return SolutionResult::forAmbiguous(solutions); + } } bool ConstraintSystem::solve(SmallVectorImpl &solutions, diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 1cd0c4724e7a4..a639bd9d1e72b 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -792,6 +792,7 @@ using OpenedTypeMap = struct ContextualTypeInfo { TypeLoc typeLoc; ContextualTypePurpose purpose; + bool isOpaqueReturnType = false; Type getType() const { return typeLoc.getType(); } }; @@ -1012,10 +1013,6 @@ enum class ConstraintSystemFlags { /// expression, and doesn't dig into its subexpressions. ReusePrecheckedType = 0x20, - /// If set, the top-level expression may be able to provide an underlying - /// type for the contextual opaque archetype. - UnderlyingTypeForOpaqueReturnType = 0x40, - /// FIXME(diagnostics): Once diagnostics are completely switched to new /// framework, this flag could be removed as obsolete. /// @@ -2180,11 +2177,12 @@ class ConstraintSystem { } void setContextualType( - const Expr *expr, TypeLoc T, ContextualTypePurpose purpose) { + const Expr *expr, TypeLoc T, ContextualTypePurpose purpose, + bool isOpaqueReturnType) { assert(expr != nullptr && "Expected non-null expression!"); assert(contextualTypes.count(expr) == 0 && "Already set this contextual type"); - contextualTypes[expr] = { T, purpose }; + contextualTypes[expr] = { T, purpose, isOpaqueReturnType }; } Optional getContextualTypeInfo(const Expr *expr) const { @@ -2413,6 +2411,10 @@ class ConstraintSystem { void addConstraint(Requirement req, ConstraintLocatorBuilder locator, bool isFavored = false); + /// Add the appropriate constraint for a contextual conversion. + void addContextualConversionConstraint( + Expr *expr, ContextualTypeInfo contextualType); + /// Add a "join" constraint between a set of types, producing the common /// supertype. /// @@ -3078,7 +3080,7 @@ class ConstraintSystem { /// Generate constraints for the given (unchecked) expression. /// /// \returns a possibly-sanitized expression, or null if an error occurred. - Expr *generateConstraints(Expr *E, DeclContext *dc = nullptr); + Expr *generateConstraints(Expr *E, DeclContext *dc); /// Generate constraints for binding the given pattern to the /// value of the given expression. @@ -4036,18 +4038,13 @@ class ConstraintSystem { /// \param expr The expression to generate constraints from. /// \param convertType The expected type of the expression. /// \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 Error is an error occurred, Solved is system is consistent - /// and solutions were found, Unsolved otherwise. - SolutionKind solveImpl(Expr *&expr, - Type convertType, - ExprTypeCheckListener *listener, - SmallVectorImpl &solutions, - FreeTypeVariableBinding allowFreeTypeVariables - = FreeTypeVariableBinding::Disallow); + SolutionResult solveImpl(Expr *&expr, + Type convertType, + ExprTypeCheckListener *listener, + FreeTypeVariableBinding allowFreeTypeVariables + = FreeTypeVariableBinding::Disallow); public: /// Pre-check the expression, validating any types that occur in the diff --git a/lib/Sema/SolutionResult.h b/lib/Sema/SolutionResult.h index 8e3085a3a69c1..e0e73382303f8 100644 --- a/lib/Sema/SolutionResult.h +++ b/lib/Sema/SolutionResult.h @@ -120,6 +120,9 @@ class SolutionResult { /// Retrieve the set of solutions when there is an ambiguity. ArrayRef getAmbiguousSolutions() const; + /// Take the set of solutions when there is an ambiguity. + MutableArrayRef takeAmbiguousSolutions() &&; + /// Whether this solution requires the client to produce a diagnostic. bool requiresDiagnostic() const { switch (kind) { diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index d176a8e79c2f4..de6d54af3adce 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2189,9 +2189,6 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables)) csOptions |= ConstraintSystemFlags::AllowUnresolvedTypeVariables; - if (options.contains(TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType)) - csOptions |= ConstraintSystemFlags::UnderlyingTypeForOpaqueReturnType; - if (options.contains(TypeCheckExprFlags::SubExpressionDiagnostics)) csOptions |= ConstraintSystemFlags::SubExpressionDiagnostics; @@ -2222,7 +2219,9 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, Expr *contextualTypeExpr = expr; if (auto loadExpr = dyn_cast_or_null(contextualTypeExpr)) contextualTypeExpr = loadExpr->getSubExpr(); - cs.setContextualType(contextualTypeExpr, convertType, convertTypePurpose); + cs.setContextualType( + contextualTypeExpr, convertType, convertTypePurpose, + options.contains(TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType)); // If the convertType is *only* provided for that hint, then null it out so // that we don't later treat it as an actual conversion constraint. @@ -2466,7 +2465,7 @@ getTypeOfCompletionOperatorImpl(DeclContext *DC, Expr *expr, // Construct a constraint system from this expression. ConstraintSystem CS(DC, options); - expr = CS.generateConstraints(expr); + expr = CS.generateConstraints(expr, DC); if (!expr) return nullptr; @@ -2666,8 +2665,7 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, initType = patternType; // Add a conversion constraint between the types. - if (!cs.Options.contains( - ConstraintSystemFlags::UnderlyingTypeForOpaqueReturnType)) { + if (!initType->is()) { cs.addConstraint(ConstraintKind::Conversion, cs.getType(expr), patternType, Locator, /*isFavored*/true); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 66b2a57f3c79e..c4df6c61bb254 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -47,7 +47,6 @@ enum class TypeResolutionStage : uint8_t; namespace constraints { enum class ConstraintKind : char; - enum class SolutionKind : char; class ConstraintSystem; class Solution; class SolutionResult; @@ -154,7 +153,7 @@ enum ContextualTypePurpose { -/// Flags that can be used to control name lookup. +/// Flags that can be used to control type checking. enum class TypeCheckExprFlags { /// Whether we know that the result of the expression is discarded. This /// disables constraints forcing an lvalue result to be loadable.