diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 099d8c4e8a9d7..efe799de8ee08 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -20,6 +20,8 @@ #include "swift/AST/Expr.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PrettyStackTrace.h" +#include "swift/AST/ProtocolConformance.h" +#include "swift/AST/ProtocolConformanceRef.h" #include "swift/AST/SubstitutionMap.h" #include "swift/Sema/IDETypeChecking.h" #include "llvm/ADT/APInt.h" @@ -97,419 +99,6 @@ static bool mergeRepresentativeEquivalenceClasses(ConstraintSystem &CS, namespace { - /// Internal struct for tracking information about types within a series - /// of "linked" expressions. (Such as a chain of binary operator invocations.) - struct LinkedTypeInfo { - unsigned haveIntLiteral : 1; - unsigned haveFloatLiteral : 1; - unsigned haveStringLiteral : 1; - - llvm::SmallSet collectedTypes; - - llvm::SmallVector intLiteralTyvars; - llvm::SmallVector floatLiteralTyvars; - llvm::SmallVector stringLiteralTyvars; - - llvm::SmallVector binaryExprs; - - // TODO: manage as a set of lists, to speed up addition of binding - // constraints. - llvm::SmallVector anonClosureParams; - - LinkedTypeInfo() { - haveIntLiteral = false; - haveFloatLiteral = false; - haveStringLiteral = false; - } - - bool hasLiteral() { - return haveIntLiteral || haveFloatLiteral || haveStringLiteral; - } - }; - - /// Walks an expression sub-tree, and collects information about expressions - /// whose types are mutually dependent upon one another. - class LinkedExprCollector : public ASTWalker { - - llvm::SmallVectorImpl &LinkedExprs; - - public: - - LinkedExprCollector(llvm::SmallVectorImpl &linkedExprs) : - LinkedExprs(linkedExprs) {} - - std::pair walkToExprPre(Expr *expr) override { - - // Store top-level binary exprs for further analysis. - if (isa(expr) || - - // Literal exprs are contextually typed, so store them off as well. - isa(expr) || - - // We'd like to take a look at implicit closure params, so store - // them. - isa(expr) || - - // We'd like to look at the elements of arrays and dictionaries. - isa(expr) || - isa(expr) || - - // assignment expression can involve anonymous closure parameters - // as source and destination, so it's beneficial for diagnostics if - // we look at the assignment. - isa(expr)) { - LinkedExprs.push_back(expr); - return {false, expr}; - } - - return { true, expr }; - } - - Expr *walkToExprPost(Expr *expr) override { - return expr; - } - - /// \brief Ignore statements. - std::pair walkToStmtPre(Stmt *stmt) override { - return { false, stmt }; - } - - /// \brief Ignore declarations. - bool walkToDeclPre(Decl *decl) override { return false; } - }; - - /// Given a collection of "linked" expressions, analyzes them for - /// commonalities regarding their types. This will help us compute a - /// "best common type" from the expression types. - class LinkedExprAnalyzer : public ASTWalker { - - LinkedTypeInfo <I; - ConstraintSystem &CS; - - public: - - LinkedExprAnalyzer(LinkedTypeInfo <i, ConstraintSystem &cs) : - LTI(lti), CS(cs) {} - - std::pair walkToExprPre(Expr *expr) override { - - if (isa(expr)) { - LTI.haveIntLiteral = true; - auto tyvar = CS.getType(expr)->getAs(); - - if (tyvar) { - LTI.intLiteralTyvars.push_back(tyvar); - } - - return { false, expr }; - } - - if (isa(expr)) { - LTI.haveFloatLiteral = true; - auto tyvar = CS.getType(expr)->getAs(); - - if (tyvar) { - LTI.floatLiteralTyvars.push_back(tyvar); - } - - return { false, expr }; - } - - if (isa(expr)) { - LTI.haveStringLiteral = true; - - auto tyvar = CS.getType(expr)->getAs(); - - if (tyvar) { - LTI.stringLiteralTyvars.push_back(tyvar); - } - - return { false, expr }; - } - - if (isa(expr)) { - return { true, expr }; - } - - if (auto UDE = dyn_cast(expr)) { - - if (CS.hasType(UDE)) - LTI.collectedTypes.insert(CS.getType(UDE).getPointer()); - - // Don't recurse into the base expression. - return { false, expr }; - } - - - if (isa(expr)) { - return { true, expr }; - } - - if (auto FVE = dyn_cast(expr)) { - LTI.collectedTypes.insert(CS.getType(FVE).getPointer()); - return { false, expr }; - } - - if (auto DRE = dyn_cast(expr)) { - if (auto varDecl = dyn_cast(DRE->getDecl())) { - if (varDecl->isAnonClosureParam()) { - LTI.anonClosureParams.push_back(DRE); - } else if (CS.hasType(DRE)) { - LTI.collectedTypes.insert(CS.getType(DRE).getPointer()); - } - return { false, expr }; - } - } - - // In the case of a function application, we would have already captured - // the return type during constraint generation, so there's no use in - // looking any further. - if (isa(expr) && - !(isa(expr) || isa(expr) || - isa(expr))) { - return { false, expr }; - } - - if (isa(expr)) { - LTI.binaryExprs.push_back(dyn_cast(expr)); - } - - if (auto favoredType = CS.getFavoredType(expr)) { - LTI.collectedTypes.insert(favoredType); - - return { false, expr }; - } - - // Optimize branches of a conditional expression separately. - if (auto IE = dyn_cast(expr)) { - CS.optimizeConstraints(IE->getCondExpr()); - CS.optimizeConstraints(IE->getThenExpr()); - CS.optimizeConstraints(IE->getElseExpr()); - return { false, expr }; - } - - // TODO: The systems that we need to solve for interpolated string expressions - // require bespoke logic that don't currently work with this approach. - if (isa(expr)) { - return { false, expr }; - } - - // For exprs of a structural type that are not modeling argument lists, - // avoid merging the type variables. (We need to allow for cases like - // (Int, Int32).) - if (isa(expr) && !isa(Parent.getAsExpr())) { - return { false, expr }; - } - - // Coercion exprs have a rigid type, so there's no use in gathering info - // about them. - if (isa(expr)) { - LTI.collectedTypes.insert(CS.getType(expr).getPointer()); - - return { false, expr }; - } - - // Don't walk into subscript expressions - to do so would risk factoring - // the index expression into edge contraction. (We don't want to do this - // if the index expression is a literal type that differs from the return - // type of the subscript operation.) - if (isa(expr) || isa(expr)) { - return { false, expr }; - } - - return { true, expr }; - } - - /// \brief Ignore statements. - std::pair walkToStmtPre(Stmt *stmt) override { - return { false, stmt }; - } - - /// \brief Ignore declarations. - bool walkToDeclPre(Decl *decl) override { return false; } - }; - - /// For a given expression, given information that is global to the - /// expression, attempt to derive a favored type for it. - bool computeFavoredTypeForExpr(Expr *expr, ConstraintSystem &CS) { - LinkedTypeInfo lti; - - expr->walk(LinkedExprAnalyzer(lti, CS)); - - // Link anonymous closure params of the same index. - // TODO: As stated above, we should bucket these whilst collecting the - // exprs to avoid quadratic behavior. - for (auto acp1 : lti.anonClosureParams) { - for (auto acp2 : lti.anonClosureParams) { - if (acp1 == acp2) - continue; - - if (acp1->getDecl()->getBaseName() == acp2->getDecl()->getBaseName()) { - - auto tyvar1 = CS.getType(acp1)->getAs(); - auto tyvar2 = CS.getType(acp2)->getAs(); - - mergeRepresentativeEquivalenceClasses(CS, tyvar1, tyvar2); - } - } - } - - // Link integer literal tyvars. - if (lti.intLiteralTyvars.size() > 1) { - auto rep1 = CS.getRepresentative(lti.intLiteralTyvars[0]); - - for (size_t i = 1; i < lti.intLiteralTyvars.size(); i++) { - auto rep2 = CS.getRepresentative(lti.intLiteralTyvars[i]); - - if (rep1 != rep2) - CS.mergeEquivalenceClasses(rep1, rep2, /*updateWorkList*/ false); - } - } - - // Link float literal tyvars. - if (lti.floatLiteralTyvars.size() > 1) { - auto rep1 = CS.getRepresentative(lti.floatLiteralTyvars[0]); - - for (size_t i = 1; i < lti.floatLiteralTyvars.size(); i++) { - auto rep2 = CS.getRepresentative(lti.floatLiteralTyvars[i]); - - if (rep1 != rep2) - CS.mergeEquivalenceClasses(rep1, rep2, /*updateWorkList*/ false); - } - } - - // Link string literal tyvars. - if (lti.stringLiteralTyvars.size() > 1) { - auto rep1 = CS.getRepresentative(lti.stringLiteralTyvars[0]); - - for (size_t i = 1; i < lti.stringLiteralTyvars.size(); i++) { - auto rep2 = CS.getRepresentative(lti.stringLiteralTyvars[i]); - - if (rep1 != rep2) - CS.mergeEquivalenceClasses(rep1, rep2, /*updateWorkList*/ false); - } - } - - if (lti.collectedTypes.size() == 1) { - // TODO: Compute the BCT. - - // It's only useful to favor the type instead of - // binding it directly to arguments/result types, - // which means in case it has been miscalculated - // solver can still make progress. - auto favoredTy = (*lti.collectedTypes.begin())->getWithoutSpecifierType(); - CS.setFavoredType(expr, favoredTy.getPointer()); - - // If we have a chain of identical binop expressions with homogeneous - // argument types, we can directly simplify the associated constraint - // graph. - auto simplifyBinOpExprTyVars = [&]() { - // Don't attempt to do linking if there are - // literals intermingled with other inferred types. - if (lti.hasLiteral()) - return; - - for (auto binExp1 : lti.binaryExprs) { - for (auto binExp2 : lti.binaryExprs) { - if (binExp1 == binExp2) - continue; - - auto fnTy1 = CS.getType(binExp1)->getAs(); - auto fnTy2 = CS.getType(binExp2)->getAs(); - - if (!(fnTy1 && fnTy2)) - return; - - auto ODR1 = dyn_cast(binExp1->getFn()); - auto ODR2 = dyn_cast(binExp2->getFn()); - - if (!(ODR1 && ODR2)) - return; - - // TODO: We currently limit this optimization to known arithmetic - // operators, but we should be able to broaden this out to - // logical operators as well. - if (!isArithmeticOperatorDecl(ODR1->getDecls()[0])) - return; - - if (ODR1->getDecls()[0]->getBaseName() != - ODR2->getDecls()[0]->getBaseName()) - return; - - // All things equal, we can merge the tyvars for the function - // types. - auto rep1 = CS.getRepresentative(fnTy1); - auto rep2 = CS.getRepresentative(fnTy2); - - if (rep1 != rep2) { - CS.mergeEquivalenceClasses(rep1, rep2, - /*updateWorkList*/ false); - } - - auto odTy1 = CS.getType(ODR1)->getAs(); - auto odTy2 = CS.getType(ODR2)->getAs(); - - if (odTy1 && odTy2) { - auto odRep1 = CS.getRepresentative(odTy1); - auto odRep2 = CS.getRepresentative(odTy2); - - // Since we'll be choosing the same overload, we can merge - // the overload tyvar as well. - if (odRep1 != odRep2) - CS.mergeEquivalenceClasses(odRep1, odRep2, - /*updateWorkList*/ false); - } - } - } - }; - - simplifyBinOpExprTyVars(); - - return true; - } - - if (lti.haveFloatLiteral) { - if (auto floatProto = - CS.TC.Context.getProtocol( - KnownProtocolKind::ExpressibleByFloatLiteral)) { - if (auto defaultType = CS.TC.getDefaultType(floatProto, CS.DC)) { - if (!CS.getFavoredType(expr)) { - CS.setFavoredType(expr, defaultType.getPointer()); - } - return true; - } - } - } - - if (lti.haveIntLiteral) { - if (auto intProto = - CS.TC.Context.getProtocol( - KnownProtocolKind::ExpressibleByIntegerLiteral)) { - if (auto defaultType = CS.TC.getDefaultType(intProto, CS.DC)) { - if (!CS.getFavoredType(expr)) { - CS.setFavoredType(expr, defaultType.getPointer()); - } - return true; - } - } - } - - if (lti.haveStringLiteral) { - if (auto stringProto = - CS.TC.Context.getProtocol( - KnownProtocolKind::ExpressibleByStringLiteral)) { - if (auto defaultType = CS.TC.getDefaultType(stringProto, CS.DC)) { - if (!CS.getFavoredType(expr)) { - CS.setFavoredType(expr, defaultType.getPointer()); - } - return true; - } - } - } - - return false; - } - /// Determine whether the given parameter type and argument should be /// "favored" because they match exactly. bool isFavoredParamAndArg(ConstraintSystem &CS, @@ -3316,6 +2905,84 @@ namespace { } // end anonymous namespace +static void simplifyDisjunction(ConstraintSystem &CS, Constraint *disjunction) { + auto &TC = CS.TC; + + // Collects all of the protocol requirement choices we have + // in this overload set, this would allow us to hide their + // witnesses until the requirement itself matches. + llvm::SmallVector, 8> requirements; + + auto isValidChoice = [](Constraint *choice) -> bool { + return choice->getKind() == ConstraintKind::BindOverload && + choice->getOverloadChoice().isDecl(); + }; + + for (auto *choice : disjunction->getNestedConstraints()) { + if (!isValidChoice(choice)) + continue; + + auto *decl = choice->getOverloadChoice().getDecl(); + if (isa(decl->getDeclContext())) { + if (decl->isProtocolRequirement()) + requirements.push_back({choice, decl}); + } + } + + // Try to hide witness choices under the main requirement + // choice, that helps to significately reduce the number + // of choices we consider blindly. + for (auto *choice : disjunction->getNestedConstraints()) { + if (!isValidChoice(choice)) + continue; + + auto *decl = choice->getOverloadChoice().getDecl(); + auto *nominal = + decl->getDeclContext()->getAsNominalTypeOrNominalTypeExtensionContext(); + if (!nominal) + continue; + + auto type = nominal->getDeclaredType(); + for (auto &req : requirements) { + auto *requirementChoice = req.first; + const auto &requirement = req.second; + auto *protocol = dyn_cast(requirement->getDeclContext()); + assert(protocol && "requirement has to have protocol context!"); + + // If the declaration context of this overload choice + // is the same as requirement we identified, it might + // mean that this is a default witness for this requirement, + // if so, but we have no way to reliably prove that because + // default witnesses are calculated later. + if (nominal == protocol) + continue; + + ConformanceCheckOptions options; + options |= ConformanceCheckFlags::InExpression; + options |= ConformanceCheckFlags::SuppressDependencyTracking; + options |= ConformanceCheckFlags::SkipConditionalRequirements; + + auto result = TC.conformsToProtocol(type, protocol, + decl->getDeclContext(), options); + + if (!result || !result->isConcrete()) + continue; + + auto conformance = result->getConcrete(); + auto wintess = conformance->getWitnessDecl(req.second, &TC); + if (wintess == decl) { + // Looks like this choice satisfies protocol requirement + // already present in the overload set, let's record and + // hide the witness. + if (choice->isFavored()) + requirementChoice->setFavored(); + + choice->setDisabled(); + } + } + } +} + Expr *ConstraintSystem::generateConstraints(Expr *expr) { // Remove implicit conversions from the expression. expr = expr->walk(SanitizeExpr(getTypeChecker())); @@ -3332,6 +2999,11 @@ Expr *ConstraintSystem::generateConstraints(Expr *expr) { if (result) this->optimizeConstraints(result); + for (auto &constraint : getConstraints()) { + if (constraint.getKind() == ConstraintKind::Disjunction) + simplifyDisjunction(*this, &constraint); + } + return result; } @@ -3362,18 +3034,6 @@ Type ConstraintSystem::generateConstraints(Pattern *pattern, } void ConstraintSystem::optimizeConstraints(Expr *e) { - - SmallVector linkedExprs; - - // Collect any linked expressions. - LinkedExprCollector collector(linkedExprs); - e->walk(collector); - - // Favor types, as appropriate. - for (auto linkedExpr : linkedExprs) { - computeFavoredTypeForExpr(linkedExpr, *this); - } - // Optimize the constraints. ConstraintOptimizer optimizer(*this); e->walk(optimizer); diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 188bb4b0fce13..e88bd3dc35014 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -538,6 +538,9 @@ ConstraintSystem::SolverScope::~SolverScope() { // Remove any conformances checked along the current path. truncate(cs.CheckedConformances, numCheckedConformances); + for (auto *favored : FavoredChoices) + favored->setFavored(false); + // Reset the previous score. cs.CurrentScore = PreviousScore; @@ -1345,7 +1348,8 @@ ConstraintSystem::solve(Expr *&expr, // Try to shrink the system by reducing disjunction domains. This // goes through every sub-expression and generate its own sub-system, to // try to reduce the domains of those subexpressions. - shrink(expr); + + //shrink(expr); // Generate constraints for the main system. if (auto generatedExpr = generateConstraints(expr)) @@ -1759,46 +1763,6 @@ void ConstraintSystem::collectDisjunctions( } } -/// \brief Check if the given disjunction choice should be attempted by solver. -static bool shouldSkipDisjunctionChoice(ConstraintSystem &cs, - DisjunctionChoice &choice, - Optional &bestNonGenericScore) { - if (choice->isDisabled()) { - auto &TC = cs.TC; - if (TC.getLangOpts().DebugConstraintSolver) { - auto &log = cs.getASTContext().TypeCheckerDebug->getStream(); - log.indent(cs.solverState->depth) - << "(skipping "; - choice->print(log, &TC.Context.SourceMgr); - log << '\n'; - } - - return true; - } - - // Don't attempt to solve for generic operators if we already have - // a non-generic solution. - - // FIXME: Less-horrible but still horrible hack to attempt to - // speed things up. Skip the generic operators if we - // already have a solution involving non-generic operators, - // but continue looking for a better non-generic operator - // solution. - if (bestNonGenericScore && choice.isGenericOperatorOrUnavailable()) { - auto &score = bestNonGenericScore->Data; - // Let's skip generic overload choices only in case if - // non-generic score indicates that there were no forced - // unwrappings of optional(s), no unavailable overload - // choices present in the solution, no fixes required, - // and there are no non-trivial function conversions. - if (score[SK_ForceUnchecked] == 0 && score[SK_Unavailable] == 0 && - score[SK_Fix] == 0 && score[SK_FunctionConversion] == 0) - return true; - } - - return false; -} - Constraint *ConstraintSystem::selectDisjunction( SmallVectorImpl &disjunctions) { if (disjunctions.empty()) @@ -1830,6 +1794,27 @@ Constraint *ConstraintSystem::selectDisjunction( return disjunction; } +static void +favorLinkedChoices(ConstraintSystem &cs, const ValueDecl *op, + llvm::function_ref callback) { + for (const auto &constraint : cs.getConstraints()) { + if (constraint.getKind() != ConstraintKind::Disjunction) + continue; + + auto *choice = constraint.getNestedConstraints()[0]; + if (choice->getKind() != ConstraintKind::BindOverload) + continue; + + auto overload = choice->getOverloadChoice(); + if (!overload.isDecl()) + continue; + + auto *decl = overload.getDecl(); + if (decl->isOperator() && decl->getBaseName() == op->getBaseName()) + callback(&constraint); + } +} + bool ConstraintSystem::solveSimplified( Constraint *disjunction, SmallVectorImpl &solutions, FreeTypeVariableBinding allowFreeTypeVariables) { @@ -1884,66 +1869,39 @@ bool ConstraintSystem::solveSimplified( auto afterDisjunction = InactiveConstraints.erase(disjunction); CG.removeConstraint(disjunction); - // Check if selected disjunction has a representative - // this might happen when there are multiple binary operators - // chained together. If so, disable choices which differ - // from currently selected representative. - auto pruneOverloadSet = [&](Constraint *disjunction) -> bool { - auto *choice = disjunction->getNestedConstraints().front(); - auto *typeVar = choice->getFirstType()->getAs(); - if (!typeVar) - return false; - - auto *repr = typeVar->getImpl().getRepresentative(nullptr); - if (!repr || repr == typeVar) - return false; - - bool isPruned = false; - for (auto resolved = resolvedOverloadSets; resolved; - resolved = resolved->Previous) { - if (!resolved->BoundType->isEqual(repr)) - continue; - - auto &representative = resolved->Choice; - if (!representative.isDecl()) - return false; + Optional> lastSolvedChoice; - // Disable all of the overload choices which are different from - // the one which is currently picked for representative. - for (auto *constraint : disjunction->getNestedConstraints()) { - auto choice = constraint->getOverloadChoice(); - if (!choice.isDecl()) - continue; + ++solverState->NumDisjunctions; + llvm::SmallVector choices; + choices.reserve(disjunction->countActiveNestedConstraints()); + for (auto *constraint : disjunction->getNestedConstraints()) { + DisjunctionChoice choice(this, disjunction, constraint); - if (choice.getDecl() != representative.getDecl()) { - constraint->setDisabled(); - isPruned = true; - } - } - break; - } + if (choice.isDisabled()) + continue; - return isPruned; - }; + // Attempt unavailable choices only if fixes are allowed + // because even if such choice forms a solution it would + // result in error regardless. + if (!shouldAttemptFixes() && choice.isUnavailable()) + continue; - bool hasDisabledChoices = pruneOverloadSet(disjunction); + choices.push_back(std::move(choice)); + } - Optional> lastSolvedChoice; - Optional bestNonGenericScore; + std::sort(choices.begin(), choices.end(), + [](const DisjunctionChoice &a, const DisjunctionChoice &b) { + return a->isFavored(); + }); - ++solverState->NumDisjunctions; - auto constraints = disjunction->getNestedConstraints(); // Try each of the constraints within the disjunction. - for (auto index : indices(constraints)) { - auto currentChoice = - DisjunctionChoice(this, disjunction, constraints[index]); - if (shouldSkipDisjunctionChoice(*this, currentChoice, bestNonGenericScore)) - continue; + for (auto index : indices(choices)) { + auto currentChoice = choices[index]; // We already have a solution; check whether we should // short-circuit the disjunction. if (lastSolvedChoice) { - auto *lastChoice = lastSolvedChoice->first.getConstraint(); + Constraint *lastChoice = lastSolvedChoice->first; auto &score = lastSolvedChoice->second; bool hasUnavailableOverloads = score.Data[SK_Unavailable] > 0; bool hasFixes = score.Data[SK_Fix] > 0; @@ -1952,8 +1910,7 @@ bool ConstraintSystem::solveSimplified( // that there are no unavailable overload choices present in the // solution, and the solution does not involve fixes. if (!hasUnavailableOverloads && !hasFixes && - shortCircuitDisjunctionAt(¤tChoice, lastChoice, - getASTContext())) + shortCircuitDisjunctionAt(currentChoice, lastChoice, getASTContext())) break; } @@ -1980,23 +1937,17 @@ bool ConstraintSystem::solveSimplified( DisjunctionChoices.push_back({locator, index}); } - if (auto score = currentChoice.solve(solutions, allowFreeTypeVariables)) { - if (!currentChoice.isGenericOperatorOrUnavailable() && - currentChoice.isSymmetricOperator()) { - if (!bestNonGenericScore || score < bestNonGenericScore) - bestNonGenericScore = score; - } + if (auto *op = currentChoice.getOperatorDecl()) { + favorLinkedChoices(*this, op, [&](const Constraint *relatedDisjunction) { + auto favoredChoice = relatedDisjunction->getNestedConstraints()[index]; + favoredChoice->setFavored(); + scope.FavoredChoices.push_back(favoredChoice); + }); + } + if (auto score = currentChoice.solve(solutions, allowFreeTypeVariables)) lastSolvedChoice = {currentChoice, *score}; - // If we see a tuple-to-tuple conversion that succeeded, we're done. - // FIXME: This should be more general. - if (auto restriction = currentChoice->getRestriction()) { - if (*restriction == ConversionRestrictionKind::TupleToTuple) - break; - } - } - if (TC.getLangOpts().DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); log.indent(solverState->depth) << ")\n"; @@ -2007,14 +1958,6 @@ bool ConstraintSystem::solveSimplified( InactiveConstraints.insert(afterDisjunction, disjunction); CG.addConstraint(disjunction); - if (hasDisabledChoices) { - // Re-enable previously disabled overload choices. - for (auto *choice : disjunction->getNestedConstraints()) { - if (choice->isDisabled()) - choice->setEnabled(); - } - } - // If we are exiting due to an expression that is too complex, do // not allow our caller to continue as if we have been successful. auto tooComplex = getExpressionTooComplex(solutions); @@ -2046,37 +1989,6 @@ DisjunctionChoice::solve(SmallVectorImpl &solutions, return bestScore; } -bool DisjunctionChoice::isGenericOperatorOrUnavailable() const { - auto *decl = getOperatorDecl(Choice); - if (!decl) - return false; - - auto &ctx = decl->getASTContext(); - if (decl->getAttrs().isUnavailable(ctx)) - return true; - - auto interfaceType = decl->getInterfaceType(); - return interfaceType->is(); -} - -bool DisjunctionChoice::isSymmetricOperator() const { - auto *decl = getOperatorDecl(Choice); - if (!decl) - return false; - - auto func = dyn_cast(decl); - auto paramList = - func->getParameterList(func->getDeclContext()->isTypeContext()); - if (paramList->size() != 2) - return true; - - auto firstType = - paramList->get(0)->getInterfaceType()->getWithoutSpecifierType(); - auto secondType = - paramList->get(1)->getInterfaceType()->getWithoutSpecifierType(); - return firstType->isEqual(secondType); -} - void DisjunctionChoice::propagateConversionInfo() const { if (!CS->isExplicitConversionConstraint(Disjunction)) return; diff --git a/lib/Sema/Constraint.h b/lib/Sema/Constraint.h index 0ea734f415569..051d0cc16f88f 100644 --- a/lib/Sema/Constraint.h +++ b/lib/Sema/Constraint.h @@ -510,7 +510,7 @@ class Constraint final : public llvm::ilist_node, } /// Mark or retrieve whether this constraint should be favored in the system. - void setFavored() { IsFavored = true; } + void setFavored(bool flag = true) { IsFavored = flag; } bool isFavored() const { return IsFavored; } /// Whether the solver should remember which choice was taken for diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 155a3a5ecaa88..8b2c2f6dd87eb 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1451,6 +1451,8 @@ class ConstraintSystem { /// Constraint graph scope associated with this solver scope. ConstraintGraphScope CGScope; + llvm::SmallVector FavoredChoices; + SolverScope(const SolverScope &) = delete; SolverScope &operator=(const SolverScope &) = delete; @@ -3246,30 +3248,37 @@ class DisjunctionChoice { Constraint *choice) : CS(cs), Disjunction(disjunction), Choice(choice) {} - Constraint *operator&() const { return Choice; } - - Constraint *getConstraint() const { return Choice; } - Constraint *operator->() const { return Choice; } bool isDisabled() const { return Choice->isDisabled(); } - // FIXME: Both of the accessors below are required to support - // performance optimization hacks in constraint solver. + bool isUnavailable() const { + if (auto *decl = getDecl(Choice)) + return decl->getAttrs().isUnavailable(decl->getASTContext()); + + return false; + } + + ValueDecl *getOperatorDecl() const { + auto *decl = getDecl(Choice); + if (!decl) + return nullptr; - bool isGenericOperatorOrUnavailable() const; - bool isSymmetricOperator() const; + return decl->isOperator() ? decl : nullptr; + } /// \brief Apply given choice to the system and try to solve it. Optional solve(SmallVectorImpl &solutions, FreeTypeVariableBinding allowFreeTypeVariables); + operator Constraint *() { return Choice; } + private: /// \brief If associated disjunction is an explicit conversion, /// let's try to propagate its type early to prune search space. void propagateConversionInfo() const; - static ValueDecl *getOperatorDecl(Constraint *constraint) { + static ValueDecl *getDecl(Constraint *constraint) { if (constraint->getKind() != ConstraintKind::BindOverload) return nullptr; @@ -3277,8 +3286,7 @@ class DisjunctionChoice { if (choice.getKind() != OverloadChoiceKind::Decl) return nullptr; - auto *decl = choice.getDecl(); - return decl->isOperator() ? decl : nullptr; + return choice.getDecl(); } }; diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index fe32aa5f7804a..51a8821838d63 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -787,6 +787,10 @@ namespace { /// Simplify a key path expression into a canonical form. void resolveKeyPathExpr(KeyPathExpr *KPE); + /// Simplify constructs like `UInt32(1)` into `1 as UInt32` if + /// the type conforms to the expected literal protocol. + Expr *simplifyInitWithLiteral(Expr *E); + public: PreCheckExpression(TypeChecker &tc, DeclContext *dc) : TC(tc), DC(dc) { } @@ -989,6 +993,9 @@ namespace { return KPE; } + if (auto *simplified = simplifyInitWithLiteral(expr)) + return simplified; + return expr; } @@ -1547,6 +1554,53 @@ void PreCheckExpression::resolveKeyPathExpr(KeyPathExpr *KPE) { KPE->resolveComponents(TC.Context, components); } +Expr *PreCheckExpression::simplifyInitWithLiteral(Expr *E) { + auto *call = dyn_cast(E); + if (!call || call->getNumArguments() != 1) + return nullptr; + + auto *typeExpr = dyn_cast(call->getFn()); + if (!typeExpr) + return nullptr; + + auto *argExpr = call->getArg()->getSemanticsProvidingExpr(); + auto *number = dyn_cast(argExpr); + if (!number) + return nullptr; + + auto *protocol = TC.getLiteralProtocol(number); + if (!protocol) + return nullptr; + + Type type; + if (auto *rep = typeExpr->getTypeRepr()) { + TypeResolutionOptions options; + options |= TypeResolutionFlags::AllowUnboundGenerics; + options |= TypeResolutionFlags::InExpression; + type = TC.resolveType(rep, DC, options); + } else { + type = typeExpr->getTypeLoc().getType(); + } + + if (!type) + return nullptr; + + ConformanceCheckOptions options; + options |= ConformanceCheckFlags::InExpression; + options |= ConformanceCheckFlags::SuppressDependencyTracking; + options |= ConformanceCheckFlags::SkipConditionalRequirements; + + auto result = TC.conformsToProtocol(type, protocol, DC, options); + if (result) { + auto *expr = + new (TC.Context) CoerceExpr(argExpr, {}, typeExpr->getTypeRepr()); + expr->setImplicit(); + return expr; + } + + return nullptr; +} + /// \brief Clean up the given ill-formed expression, removing any references /// to type variables and setting error types on erroneous expression nodes. void CleanupIllFormedExpressionRAII::doIt(Expr *expr, ASTContext &Context) { diff --git a/stdlib/public/core/FloatingPoint.swift.gyb b/stdlib/public/core/FloatingPoint.swift.gyb index 3906c63e33024..f5743c045470b 100644 --- a/stdlib/public/core/FloatingPoint.swift.gyb +++ b/stdlib/public/core/FloatingPoint.swift.gyb @@ -486,46 +486,6 @@ public protocol FloatingPoint: SignedNumeric, Strideable, Hashable { /// [spec]: http://ieeexplore.ieee.org/servlet/opac?punumber=4610933 var significand: Self { get } - /// Adds two values and produces their sum, rounded to a - /// representable value. - /// - /// The addition operator (`+`) calculates the sum of its two arguments. For - /// example: - /// - /// let x = 1.5 - /// let y = x + 2.25 - /// // y == 3.75 - /// - /// The `+` operator implements the addition operation defined by the - /// [IEEE 754 specification][spec]. - /// - /// [spec]: http://ieeexplore.ieee.org/servlet/opac?punumber=4610933 - /// - /// - Parameters: - /// - lhs: The first value to add. - /// - rhs: The second value to add. - static func +(_ lhs: Self, _ rhs: Self) -> Self - - /// Adds two values and stores the result in the left-hand-side variable, - /// rounded to a representable value. - /// - /// - Parameters: - /// - lhs: The first value to add. - /// - rhs: The second value to add. - static func +=(_ lhs: inout Self, _ rhs: Self) - - /// Calculates the additive inverse of a value. - /// - /// The unary minus operator (prefix `-`) calculates the negation of its - /// operand. The result is always exact. - /// - /// let x = 21.5 - /// let y = -x - /// // y == -21.5 - /// - /// - Parameter operand: The value to negate. - static prefix func - (_ operand: Self) -> Self - /// Replaces this value with its additive inverse. /// /// The result is always exact. This example uses the `negate()` method to @@ -536,62 +496,6 @@ public protocol FloatingPoint: SignedNumeric, Strideable, Hashable { /// // x == -21.5 mutating func negate() - /// Subtracts one value from another and produces their difference, rounded - /// to a representable value. - /// - /// The subtraction operator (`-`) calculates the difference of its two - /// arguments. For example: - /// - /// let x = 7.5 - /// let y = x - 2.25 - /// // y == 5.25 - /// - /// The `-` operator implements the subtraction operation defined by the - /// [IEEE 754 specification][spec]. - /// - /// [spec]: http://ieeexplore.ieee.org/servlet/opac?punumber=4610933 - /// - /// - Parameters: - /// - lhs: A numeric value. - /// - rhs: The value to subtract from `lhs`. - static func -(_ lhs: Self, _ rhs: Self) -> Self - - /// Subtracts the second value from the first and stores the difference in - /// the left-hand-side variable, rounding to a representable value. - /// - /// - Parameters: - /// - lhs: A numeric value. - /// - rhs: The value to subtract from `lhs`. - static func -=(_ lhs: inout Self, _ rhs: Self) - - /// Multiplies two values and produces their product, rounding to a - /// representable value. - /// - /// The multiplication operator (`*`) calculates the product of its two - /// arguments. For example: - /// - /// let x = 7.5 - /// let y = x * 2.25 - /// // y == 16.875 - /// - /// The `*` operator implements the multiplication operation defined by the - /// [IEEE 754 specification][spec]. - /// - /// [spec]: http://ieeexplore.ieee.org/servlet/opac?punumber=4610933 - /// - /// - Parameters: - /// - lhs: The first value to multiply. - /// - rhs: The second value to multiply. - static func *(_ lhs: Self, _ rhs: Self) -> Self - - /// Multiplies two values and stores the result in the left-hand-side - /// variable, rounding to a representable value. - /// - /// - Parameters: - /// - lhs: The first value to multiply. - /// - rhs: The second value to multiply. - static func *=(_ lhs: inout Self, _ rhs: Self) - /// Returns the quotient of dividing the first value by the second, rounded /// to a representable value. /// diff --git a/stdlib/public/core/Integers.swift.gyb b/stdlib/public/core/Integers.swift.gyb index 53404cb49fabc..cce0a5ac4841c 100644 --- a/stdlib/public/core/Integers.swift.gyb +++ b/stdlib/public/core/Integers.swift.gyb @@ -1570,7 +1570,7 @@ public protocol BinaryInteger : /// // x.trailingZeroBitCount == 3 var trailingZeroBitCount: Int { get } -% for x in chain(*binaryArithmetic.values()): +% for x in binaryArithmetic['BinaryInteger']: // defaulted using an in-place counterpart, but can be used as an // optimization hook ${operatorComment(x.operator, False)}