diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index e94fbb3b8ee21..eae6981241f13 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -92,432 +92,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; - ConstraintSystem &CS; - - public: - LinkedExprCollector(llvm::SmallVectorImpl &linkedExprs, - ConstraintSystem &cs) - : LinkedExprs(linkedExprs), CS(cs) {} - - std::pair walkToExprPre(Expr *expr) override { - - if (CS.shouldReusePrecheckedType() && - !CS.getType(expr)->hasTypeVariable()) { - return { false, expr }; - } - - // 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; - } - - /// Ignore statements. - std::pair walkToStmtPre(Stmt *stmt) override { - return { false, stmt }; - } - - /// Ignore declarations. - bool walkToDeclPre(Decl *decl) override { return false; } - - /// Ignore patterns. - std::pair walkToPatternPre(Pattern *pat) override { - return { false, pat }; - } - - /// Ignore types. - bool walkToTypeLocPre(TypeLoc &TL) 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 (CS.shouldReusePrecheckedType() && - !CS.getType(expr)->hasTypeVariable()) { - return { false, expr }; - } - - 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 (isa(varDecl) && - cast(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 }; - } - - // 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 (auto *coercion = dyn_cast(expr)) { - // Let's not collect information about types initialized by - // coercions just like we don't for regular initializer calls, - // because that might lead to overly eager type variable merging. - if (!coercion->isLiteralInit()) - 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 }; - } - - // Don't walk into unresolved member expressions - we avoid merging type - // variables inside UnresolvedMemberExpr and those outside, since they - // should be allowed to behave independently in CS. - if (isa(expr)) { - return {false, expr }; - } - - return { true, expr }; - } - - /// Ignore statements. - std::pair walkToStmtPre(Stmt *stmt) override { - return { false, stmt }; - } - - /// Ignore declarations. - bool walkToDeclPre(Decl *decl) override { return false; } - - /// Ignore patterns. - std::pair walkToPatternPre(Pattern *pat) override { - return { false, pat }; - } - - /// Ignore types. - bool walkToTypeLocPre(TypeLoc &TL) 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); - } - } - } - - auto mergeTypeVariables = [&](ArrayRef typeVars) { - if (typeVars.size() < 2) - return; - - auto rep1 = CS.getRepresentative(typeVars.front()); - for (unsigned i = 1, n = typeVars.size(); i != n; ++i) { - auto rep2 = CS.getRepresentative(typeVars[i]); - if (rep1 != rep2) - CS.mergeEquivalenceClasses(rep1, rep2, /*updateWorkList*/ false); - } - }; - - mergeTypeVariables(lti.intLiteralTyvars); - mergeTypeVariables(lti.floatLiteralTyvars); - mergeTypeVariables(lti.stringLiteralTyvars); - - 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, @@ -3724,17 +3298,6 @@ void ConstraintSystem::optimizeConstraints(Expr *e) { if (TC.getLangOpts().DisableConstraintSolverPerformanceHacks) return; - SmallVector linkedExprs; - - // Collect any linked expressions. - LinkedExprCollector collector(linkedExprs, *this); - 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 abfd321776ccc..5e97b7fd889a8 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -2086,9 +2086,72 @@ static Constraint *tryOptimizeGenericDisjunction( llvm_unreachable("covered switch"); } +// Performance hack: favor operator overloads with decl or type we're already +// binding elsewhere in this expression. +static void existingOperatorBindingsForDisjunction(ConstraintSystem &CS, ArrayRef constraints, SmallVectorImpl &found) { + auto *choice = constraints.front(); + if (choice->getKind() != ConstraintKind::BindOverload) + return; + + auto overload = choice->getOverloadChoice(); + if (!overload.isDecl()) + return; + auto decl = overload.getDecl(); + if (!decl->isOperator()) + return; + auto baseName = decl->getBaseName(); + + SmallSet typesFound; + + for (auto *resolved = CS.getResolvedOverloadSets(); resolved; + resolved = resolved->Previous) { + if (!resolved->Choice.isDecl()) + continue; + auto representativeDecl = resolved->Choice.getDecl(); + + if (!representativeDecl->isOperator()) + continue; + + if (representativeDecl->getBaseName() == baseName) { + // Favor exactly the same decl, if we have a binding to the same name. + for (auto *constraint : constraints) { + if (constraint->isFavored()) + continue; + auto choice = constraint->getOverloadChoice(); + if (choice.getDecl() == representativeDecl) { + found.push_back(constraint); + break; + } + } + } else { + // Favor the same type, if we have a binding to an operator of that type. + auto representativeType = representativeDecl->getInterfaceType(); + if (typesFound.count(representativeType.getPointer())) + continue; + typesFound.insert(representativeType.getPointer()); + + for (auto *constraint : constraints) { + if (constraint->isFavored()) + continue; + auto choice = constraint->getOverloadChoice(); + if (choice.getDecl()->getInterfaceType()->isEqual(representativeType)) { + found.push_back(constraint); + break; + } + } + } + } +} + void ConstraintSystem::partitionDisjunction( ArrayRef Choices, SmallVectorImpl &Ordering, SmallVectorImpl &PartitionBeginning) { + + SmallVector existing; + existingOperatorBindingsForDisjunction(*this, Choices, existing); + for (auto constraint : existing) + favorConstraint(constraint); + // Apply a special-case rule for favoring one generic function over // another. if (auto favored = tryOptimizeGenericDisjunction(TC, DC, Choices)) { @@ -2203,6 +2266,45 @@ void ConstraintSystem::partitionDisjunction( assert(Ordering.size() == Choices.size()); } +static Constraint *selectLeafApplyDisjunction(ConstraintSystem &cs, SmallVectorImpl &disjunctions) { + // Only do this work for relatively deep search trees. + if (disjunctions.size() < 6) + return nullptr; + + SmallDenseMap disjunctionForFnExpr; + SmallDenseMap sourceRangeForDisjunction; + + for (auto *disjunction : disjunctions) + disjunctionForFnExpr[disjunction->getLocator()->getAnchor()] = disjunction; + + for (auto &constraint : cs.getConstraints()) { + if (constraint.getKind() != ConstraintKind::ApplicableFunction) + continue; + + auto applyExpr = dyn_cast(constraint.getLocator()->getAnchor()); + if (!applyExpr) + continue; + + if (auto disjunction = disjunctionForFnExpr[applyExpr->getFn()]) + sourceRangeForDisjunction[disjunction] = applyExpr->getSourceRange(); + } + + Constraint *result = nullptr; + SourceRange range; + for (auto test : sourceRangeForDisjunction) { + if (range.isValid()) { + auto testRange = range; + testRange.widen(test.getSecond()); + if (testRange != range) + continue; + } + + result = test.getFirst(); + range = test.getSecond(); + } + return result; +} + Constraint *ConstraintSystem::selectDisjunction() { SmallVector disjunctions; @@ -2224,7 +2326,6 @@ Constraint *ConstraintSystem::selectDisjunction() { if (auto *disjunction = selectBestBindingDisjunction(*this, disjunctions)) return disjunction; - // Pick the disjunction with the smallest number of active choices. auto minDisjunction = std::min_element(disjunctions.begin(), disjunctions.end(), [&](Constraint *first, Constraint *second) -> bool { @@ -2232,10 +2333,15 @@ Constraint *ConstraintSystem::selectDisjunction() { second->countActiveNestedConstraints(); }); - if (minDisjunction != disjunctions.end()) - return *minDisjunction; + if (minDisjunction == disjunctions.end()) + return nullptr; - return nullptr; + // If the minimum sized disjunction is an overload disjunction, then try to choose a leaf apply expr from the set of disjunctions. + // Choosing an overload disjunction for an apply that depends on other disjunctions as arguments will likely not allow short-circuiting. + if ((*minDisjunction)->getNestedConstraints().front()->getKind() == ConstraintKind::BindOverload) + if (auto apply = selectLeafApplyDisjunction(*this, disjunctions)) + return apply; + return *minDisjunction; } bool DisjunctionChoice::attempt(ConstraintSystem &cs) const { diff --git a/lib/Sema/CSStep.cpp b/lib/Sema/CSStep.cpp index a3ec63db14e54..3b7a94e66f816 100644 --- a/lib/Sema/CSStep.cpp +++ b/lib/Sema/CSStep.cpp @@ -334,7 +334,45 @@ StepResult ComponentStep::take(bool prevFailed) { // Produce a type variable step. return suspend( llvm::make_unique(CS, *bestBindings, Solutions)); - } else if (disjunction) { + } + + // Before stepping into a new disjunction, see if we can short-circuit this part of the solution tree by attempting to show that the last opened typevar is impossible to bind. This prunes exponential behavior when there are multiple generic function/operator overloads in the expression. + if (disjunction && CS.OpenedTypes.size()) { + auto lastOpenedType = CS.OpenedTypes.back(); + auto lastOpenedTyvar = CS.getRepresentative(lastOpenedType.second.back().second); + if (CS.TypeVariables.count(lastOpenedTyvar) && !CS.getFixedType(lastOpenedTyvar)) { + auto bindings = CS.getPotentialBindings(lastOpenedTyvar); + if (!bindings.FullyBound && !bindings.PotentiallyIncomplete && bindings.Bindings.size()) { + size_t failures = 0; + for (auto binding : bindings.Bindings) { + // We can't definitively rule out some subtype being possible here. + if (binding.Kind == ConstraintSystem::AllowedBindingKind::Subtypes) + break; + + ConstraintSystem::SolverScope scope(CS); + if (CS.TC.getLangOpts().DebugConstraintSolver) { + auto &log = CS.getASTContext().TypeCheckerDebug->getStream(); + log.indent(CS.solverState ? CS.solverState->depth * 2 : 2) + << "(examining for short-circuit)\n"; + } + CS.addConstraint(ConstraintKind::Bind, lastOpenedTyvar, binding.BindingType, lastOpenedType.first); + if (CS.simplify()) + failures++; + } + if (failures == bindings.Bindings.size()) { + if (CS.TC.getLangOpts().DebugConstraintSolver) { + auto &log = CS.getASTContext().TypeCheckerDebug->getStream(); + log.indent(CS.solverState ? CS.solverState->depth * 2 : 2) + << "(short-circuit due to last opened type binding failure " + << lastOpenedTyvar->getString() << ")\n"; + } + return done(/*isSuccess=*/false); + } + } + } + } + + if (disjunction) { // Produce a disjunction step. return suspend( llvm::make_unique(CS, disjunction, Solutions)); @@ -636,8 +674,12 @@ bool DisjunctionStep::shortCircuitDisjunctionAt( auto &ctx = CS.getASTContext(); // If the successfully applied constraint is favored, we'll consider that to - // be the "best". - if (lastSuccessfulChoice->isFavored() && !currentChoice->isFavored()) { + // be the "best". If it was only temporarily favored because it matched other + // operator bindings, we can even short-circuit other favored constraints. + if (lastSuccessfulChoice->isFavored() && + (!currentChoice->isFavored() || + (CS.solverState->isTemporarilyFavored(lastSuccessfulChoice) && + !CS.solverState->isTemporarilyFavored(currentChoice)))) { #if !defined(NDEBUG) if (lastSuccessfulChoice->getKind() == ConstraintKind::BindOverload) { auto overloadChoice = lastSuccessfulChoice->getOverloadChoice(); @@ -676,16 +718,47 @@ bool DisjunctionStep::shortCircuitDisjunctionAt( if (currentChoice->getKind() == ConstraintKind::CheckedCast) return true; - // If we have a SIMD operator, and the prior choice was not a SIMD - // Operator, we're done. + // Extra checks for binding of operators if (currentChoice->getKind() == ConstraintKind::BindOverload && - isSIMDOperator(currentChoice->getOverloadChoice().getDecl()) && + currentChoice->getOverloadChoice().getDecl()->isOperator() && lastSuccessfulChoice->getKind() == ConstraintKind::BindOverload && - !isSIMDOperator(lastSuccessfulChoice->getOverloadChoice().getDecl()) && + lastSuccessfulChoice->getOverloadChoice().getDecl()->isOperator() && !ctx.LangOpts.SolverEnableOperatorDesignatedTypes) { - return true; - } + // If we have a SIMD operator, and the prior choice was not a SIMD + // Operator, we're done. + if (isSIMDOperator(currentChoice->getOverloadChoice().getDecl()) && + !isSIMDOperator(lastSuccessfulChoice->getOverloadChoice().getDecl())) + return true; + + // Otherwise, bind tyvars bound to the same + // decl in the solution to the choice tyvar. We can continue finding more + // solutions, but all the instances of the operator that chose the same + // overload as this successful choice will be bound togeter. + auto lastTyvar = + lastSuccessfulChoice->getFirstType()->getAs(); + auto lastRep = CS.getRepresentative(lastTyvar); + + for (auto overload : Solutions.back().overloadChoices) { + auto overloadChoice = overload.getSecond().choice; + if (!overloadChoice.isDecl() || + overloadChoice.getDecl() != + lastSuccessfulChoice->getOverloadChoice().getDecl()) + continue; + + auto choiceTyvar = + CS.getType(simplifyLocatorToAnchor(overload.getFirst())) + ->getAs(); + if (!choiceTyvar) + continue; + + auto rep = CS.getRepresentative(choiceTyvar); + if (lastRep != rep) { + CS.mergeEquivalenceClasses(rep, lastRep); + lastRep = CS.getRepresentative(lastRep); + } + } + } return false; } diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index a4a8098dc978f..84529d2eea997 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -274,15 +274,17 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const { Locator->dump(sm, Out); Out << "]]"; } - Out << ":"; + Out << ":\n"; interleave(getNestedConstraints(), [&](Constraint *constraint) { if (constraint->isDisabled()) - Out << "[disabled] "; + Out << "> [disabled] "; + else + Out << "> "; constraint->print(Out, sm); }, - [&] { Out << " or "; }); + [&] { Out << "\n"; }); return; } diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 2c14b9f3ccb8b..86aefe4749660 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1436,6 +1436,16 @@ class ConstraintSystem { favoredConstraints.push_back(constraint); } + /// Whether or not the given constraint is only favored during this scope. + bool isTemporarilyFavored(Constraint *constraint) { + assert(constraint->isFavored()); + + for (auto test : favoredConstraints) + if (test == constraint) + return true; + return false; + } + private: /// The list of constraints that have been retired along the /// current path, this list is used in LIFO fashion when constraints diff --git a/test/Constraints/sr10324.swift b/test/Constraints/sr10324.swift new file mode 100644 index 0000000000000..5e38121153008 --- /dev/null +++ b/test/Constraints/sr10324.swift @@ -0,0 +1,19 @@ +// RUN: %target-swift-frontend -typecheck -verify %s + +struct A { + static func * (lhs: A, rhs: A) -> B { return B() } + static func * (lhs: B, rhs: A) -> B { return B() } + static func * (lhs: A, rhs: B) -> B { return B() } +} +struct B {} + +let (x, y, z) = (A(), A(), A()) + +let w = A() * A() * A() // works + +// Should all work +let a = x * y * z +let b = x * (y * z) +let c = (x * y) * z +let d = x * (y * z as B) +let e = (x * y as B) * z diff --git a/validation-test/Sema/type_checker_perf/fast/rdar18360240.swift b/validation-test/Sema/type_checker_perf/fast/rdar18360240.swift new file mode 100644 index 0000000000000..a157c352a48f2 --- /dev/null +++ b/validation-test/Sema/type_checker_perf/fast/rdar18360240.swift @@ -0,0 +1,6 @@ +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 +// REQUIRES: OS=macosx +// REQUIRES: asserts + +let empty: [Int] = [] +let _ = empty + empty + empty + empty + empty + empty + empty + empty + empty + empty + empty + empty diff --git a/validation-test/Sema/type_checker_perf/fast/rdar18360240.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar18360240.swift.gyb deleted file mode 100644 index 421324d06cb46..0000000000000 --- a/validation-test/Sema/type_checker_perf/fast/rdar18360240.swift.gyb +++ /dev/null @@ -1,10 +0,0 @@ -// RUN: %scale-test --begin 2 --end 10 --step 2 --select NumConstraintScopes --polynomial-threshold 1.5 %s -// REQUIRES: OS=macosx -// REQUIRES: asserts - -let empty: [Int] = [] -let _ = empty + -%for i in range(0, N): - empty + -%end - empty diff --git a/validation-test/Sema/type_checker_perf/fast/rdar25866240.swift b/validation-test/Sema/type_checker_perf/fast/rdar25866240.swift new file mode 100644 index 0000000000000..28381b18c5963 --- /dev/null +++ b/validation-test/Sema/type_checker_perf/fast/rdar25866240.swift @@ -0,0 +1,7 @@ +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 +// REQUIRES: OS=macosx +// REQUIRES: asserts + +func f(c1: [String], c2: [String], c3: [String], c4: [String], c5: [String], c6: [String], c7: [String], c8: [String], c9: [String], c10: [String]) { + _ = c1 + c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c10 +} diff --git a/validation-test/Sema/type_checker_perf/fast/rdar25866240.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar25866240.swift.gyb deleted file mode 100644 index 116a4e5c14c3e..0000000000000 --- a/validation-test/Sema/type_checker_perf/fast/rdar25866240.swift.gyb +++ /dev/null @@ -1,12 +0,0 @@ -// RUN: %scale-test --begin 2 --end 10 --step 1 --select NumConstraintScopes %s -Xfrontend=-solver-disable-shrink -Xfrontend=-disable-constraint-solver-performance-hacks -Xfrontend=-solver-enable-operator-designated-types -// REQUIRES: OS=macosx -// REQUIRES: asserts - -func f( -%for i in range(N): - collection${i}: [String], -%end - collection${N+1}: [String] -) { - _ = ${' + '.join("collection%s" % i for i in range(N))} -} diff --git a/validation-test/Sema/type_checker_perf/fast/simd_add.gyb b/validation-test/Sema/type_checker_perf/fast/simd_add.gyb index 8bd1cf83e43ff..917be248be2f7 100644 --- a/validation-test/Sema/type_checker_perf/fast/simd_add.gyb +++ b/validation-test/Sema/type_checker_perf/fast/simd_add.gyb @@ -1,4 +1,4 @@ -// RUN: %scale-test --begin 3 --end 7 --step 1 --select NumLeafScopes %s +// RUN: %scale-test --begin 3 --end 7 --step 1 --select NumLeafScopes %s --exponential-threshold 11.0 // REQUIRES: OS=macosx // REQUIRES: asserts