diff --git a/include/swift/AST/DiagnosticsDriver.def b/include/swift/AST/DiagnosticsDriver.def index 32e862b3751da..ccce85380c3f7 100644 --- a/include/swift/AST/DiagnosticsDriver.def +++ b/include/swift/AST/DiagnosticsDriver.def @@ -190,7 +190,11 @@ WARNING(warn_drv_darwin_sdk_invalid_settings, none, "SDK settings were ignored because 'SDKSettings.json' could not be parsed", ()) -REMARK(remark_forwarding_to_new_driver, none, "new Swift driver will be used", ()) +REMARK(remark_forwarding_to_new_driver, none, + "new Swift driver at '%0' will be used", (StringRef)) + +REMARK(remark_forwarding_driver_not_there, none, + "new Swift driver at '%0' cannot be found; C++ driver will be used", (StringRef)) #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index d0bb1e0cb990f..d200d3061c9a8 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -177,6 +177,10 @@ def verify_incremental_dependencies : Flags<[FrontendOption, HelpHidden]>, HelpText<"Enable the dependency verifier for each frontend job">; +def disallow_forwarding_driver : + Flag<["-"], "disallow-use-new-driver">, Flags<[]>, + HelpText<"Disable using new swift-driver">; + def driver_emit_fine_grained_dependency_dot_file_after_every_import : Flag<["-"], "driver-emit-fine-grained-dependency-dot-file-after-every-import">, InternalDebugOpt, diff --git a/include/swift/Sema/Constraint.h b/include/swift/Sema/Constraint.h index 5eb4ab14a0f3a..d1f91348f9e60 100644 --- a/include/swift/Sema/Constraint.h +++ b/include/swift/Sema/Constraint.h @@ -672,13 +672,16 @@ class Constraint final : public llvm::ilist_node, return Nested; } - unsigned countActiveNestedConstraints() const { - unsigned count = 0; - for (auto *constraint : Nested) - if (!constraint->isDisabled()) - count++; + unsigned countFavoredNestedConstraints() const { + return llvm::count_if(Nested, [](const Constraint *constraint) { + return constraint->isFavored() && !constraint->isDisabled(); + }); + } - return count; + unsigned countActiveNestedConstraints() const { + return llvm::count_if(Nested, [](const Constraint *constraint) { + return !constraint->isDisabled(); + }); } /// Determine if this constraint represents explicit conversion, diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index d6245bcb6be28..64fa45f2ceed8 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -5328,6 +5328,12 @@ class ConstraintSystem { SmallVectorImpl &Ordering, SmallVectorImpl &PartitionBeginning); + /// The overload sets that have already been resolved along the current path. + const llvm::MapVector & + getResolvedOverloads() const { + return ResolvedOverloads; + } + /// If we aren't certain that we've emitted a diagnostic, emit a fallback /// diagnostic. void maybeProduceFallbackDiagnostic(SolutionApplicationTarget target) const; diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 0e7591cfb493b..f94f0c5532183 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -5813,9 +5813,11 @@ Expr *ExprRewriter::coerceCallArguments( // - new types are propagated to constraint system auto *closureType = param.getPlainType()->castTo(); + auto argLoc = getArgLocator(argIdx, paramIdx, param.getParameterFlags()); + arg = coerceToType( arg, closureType->getResult(), - locator.withPathElement(ConstraintLocator::AutoclosureResult)); + argLoc.withPathElement(ConstraintLocator::AutoclosureResult)); if (shouldInjectWrappedValuePlaceholder) { // If init(wrappedValue:) takes an autoclosure, then we want diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 6d702ebc25fac..f9ae54a452426 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -367,7 +367,7 @@ namespace { } }; - simplifyBinOpExprTyVars(); + simplifyBinOpExprTyVars(); return true; } diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index eb75234cc4925..1f4169b1ae347 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -2013,6 +2013,66 @@ static Constraint *tryOptimizeGenericDisjunction( llvm_unreachable("covered switch"); } +/// Populates the \c found vector with the indices of the given constraints +/// that have a matching type to an existing operator binding elsewhere in +/// the expression. +/// +/// Operator bindings that have a matching type to an existing binding +/// are attempted first by the solver because it's very common to chain +/// operators of the same type together. +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; + + // For concrete operators, consider overloads that have the same type as + // an existing binding, because it's very common to write mixed operator + // expressions where all operands have the same type, e.g. `(x + 10) / 2`. + // For generic operators, only favor an exact overload that has already + // been bound, because mixed operator expressions are far less common, and + // computing generic canonical types is expensive. + SmallSet concreteTypesFound; + SmallSet genericDeclsFound; + for (auto overload : CS.getResolvedOverloads()) { + auto resolved = overload.second; + if (!resolved.choice.isDecl()) + continue; + + auto representativeDecl = resolved.choice.getDecl(); + if (!representativeDecl->isOperator()) + continue; + + auto interfaceType = representativeDecl->getInterfaceType(); + if (interfaceType->is()) { + genericDeclsFound.insert(representativeDecl); + } else { + concreteTypesFound.insert(interfaceType->getCanonicalType()); + } + } + + for (auto index : indices(constraints)) { + auto *constraint = constraints[index]; + if (constraint->isFavored()) + continue; + + auto *decl = constraint->getOverloadChoice().getDecl(); + auto interfaceType = decl->getInterfaceType(); + bool isGeneric = interfaceType->is(); + if ((isGeneric && genericDeclsFound.count(decl)) || + (!isGeneric && concreteTypesFound.count(interfaceType->getCanonicalType()))) + found.push_back(index); + } +} + void ConstraintSystem::partitionDisjunction( ArrayRef Choices, SmallVectorImpl &Ordering, SmallVectorImpl &PartitionBeginning) { @@ -2042,12 +2102,18 @@ void ConstraintSystem::partitionDisjunction( // First collect some things that we'll generally put near the beginning or // end of the partitioning. - SmallVector favored; + SmallVector everythingElse; SmallVector simdOperators; SmallVector disabled; SmallVector unavailable; + // Add existing operator bindings to the main partition first. This often + // helps the solver find a solution fast. + existingOperatorBindingsForDisjunction(*this, Choices, everythingElse); + for (auto index : everythingElse) + taken.insert(Choices[index]); + // First collect disabled and favored constraints. forEachChoice(Choices, [&](unsigned index, Constraint *constraint) -> bool { if (constraint->isDisabled()) { @@ -2107,7 +2173,6 @@ void ConstraintSystem::partitionDisjunction( } }; - SmallVector everythingElse; // Gather the remaining options. forEachChoice(Choices, [&](unsigned index, Constraint *constraint) -> bool { everythingElse.push_back(index); @@ -2134,13 +2199,34 @@ 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 { - return first->countActiveNestedConstraints() < - second->countActiveNestedConstraints(); - }); + // Pick the disjunction with the smallest number of favored, then active choices. + auto cs = this; + auto minDisjunction = std::min_element(disjunctions.begin(), disjunctions.end(), + [&](Constraint *first, Constraint *second) -> bool { + unsigned firstFavored = first->countFavoredNestedConstraints(); + unsigned secondFavored = second->countFavoredNestedConstraints(); + + if (!isOperatorBindOverload(first->getNestedConstraints().front()) || + !isOperatorBindOverload(second->getNestedConstraints().front())) + return first->countActiveNestedConstraints() < second->countActiveNestedConstraints(); + + if (firstFavored == secondFavored) { + // Look for additional choices to favor + SmallVector firstExisting; + SmallVector secondExisting; + + existingOperatorBindingsForDisjunction(*cs, first->getNestedConstraints(), firstExisting); + firstFavored = firstExisting.size() ? firstExisting.size() : first->countActiveNestedConstraints(); + existingOperatorBindingsForDisjunction(*cs, second->getNestedConstraints(), secondExisting); + secondFavored = secondExisting.size() ? secondExisting.size() : second->countActiveNestedConstraints(); + + return firstFavored < secondFavored; + } + + firstFavored = firstFavored ? firstFavored : first->countActiveNestedConstraints(); + secondFavored = secondFavored ? secondFavored : second->countActiveNestedConstraints(); + return firstFavored < secondFavored; + }); if (minDisjunction != disjunctions.end()) return *minDisjunction; diff --git a/lib/Sema/CSStep.cpp b/lib/Sema/CSStep.cpp index 79bc37a25eef3..c3107e8c0d1d1 100644 --- a/lib/Sema/CSStep.cpp +++ b/lib/Sema/CSStep.cpp @@ -616,21 +616,6 @@ bool DisjunctionStep::shortCircuitDisjunctionAt( Constraint *currentChoice, Constraint *lastSuccessfulChoice) const { auto &ctx = CS.getASTContext(); - // If the successfully applied constraint is favored, we'll consider that to - // be the "best". - if (lastSuccessfulChoice->isFavored() && !currentChoice->isFavored()) { -#if !defined(NDEBUG) - if (lastSuccessfulChoice->getKind() == ConstraintKind::BindOverload) { - auto overloadChoice = lastSuccessfulChoice->getOverloadChoice(); - assert((!overloadChoice.isDecl() || - !overloadChoice.getDecl()->getAttrs().isUnavailable(ctx)) && - "Unavailable decl should not be favored!"); - } -#endif - - return true; - } - // Anything without a fix is better than anything with a fix. if (currentChoice->getFix() && !lastSuccessfulChoice->getFix()) return true; @@ -657,15 +642,6 @@ 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. - if (currentChoice->getKind() == ConstraintKind::BindOverload && - isSIMDOperator(currentChoice->getOverloadChoice().getDecl()) && - lastSuccessfulChoice->getKind() == ConstraintKind::BindOverload && - !isSIMDOperator(lastSuccessfulChoice->getOverloadChoice().getDecl())) { - return true; - } - return false; } diff --git a/lib/Sema/CSStep.h b/lib/Sema/CSStep.h index 89b812ee53511..053633dfda07c 100644 --- a/lib/Sema/CSStep.h +++ b/lib/Sema/CSStep.h @@ -213,11 +213,6 @@ class SolverStep { CS.CG.addConstraint(constraint); } - const llvm::MapVector & - getResolvedOverloads() const { - return CS.ResolvedOverloads; - } - void recordDisjunctionChoice(ConstraintLocator *disjunctionLocator, unsigned index) const { CS.recordDisjunctionChoice(disjunctionLocator, index); @@ -716,8 +711,8 @@ class DisjunctionStep final : public BindingStep { if (!repr || repr == typeVar) return; - for (auto elt : getResolvedOverloads()) { - auto resolved = elt.second; + for (auto overload : CS.getResolvedOverloads()) { + auto resolved = overload.second; if (!resolved.boundType->isEqual(repr)) continue; diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index a26577dfbe276..357b78d8630c5 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -317,15 +317,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/test/Constraints/sr10324.swift b/test/Constraints/sr10324.swift new file mode 100644 index 0000000000000..6cacfb2710420 --- /dev/null +++ b/test/Constraints/sr10324.swift @@ -0,0 +1,21 @@ +// RUN: %target-swift-frontend -typecheck -verify %s + +// REQUIRES: rdar65007946 + +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/test/Frontend/crash-in-user-code.swift b/test/Frontend/crash-in-user-code.swift index d36f642c76e46..4a36a0c3b5a4c 100644 --- a/test/Frontend/crash-in-user-code.swift +++ b/test/Frontend/crash-in-user-code.swift @@ -8,8 +8,6 @@ // UNSUPPORTED: OS=tvos // UNSUPPORTED: OS=watchos -// XFAIL: MSVC_VER=15.0 - // CHECK: Stack dump: // CHECK-NEXT: Program arguments: // CHECK-NEXT: Swift version diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp index 0fb6062e12ec0..4b7d23f71309a 100644 --- a/tools/driver/driver.cpp +++ b/tools/driver/driver.cpp @@ -160,33 +160,46 @@ static int run_driver(StringRef ExecName, DiagnosticEngine Diags(SM); Diags.addConsumer(PDC); + std::string newDriverName; + if (auto driverNameOp = llvm::sys::Process::GetEnv("SWIFT_USE_NEW_DRIVER")) { + newDriverName = driverNameOp.getValue(); + } + auto disallowForwarding = llvm::find_if(argv, [](const char* arg) { + return StringRef(arg) == "-disallow-use-new-driver"; + }) != argv.end(); // Forwarding calls to the swift driver if the C++ driver is invoked as `swift` // or `swiftc`, and an environment variable SWIFT_USE_NEW_DRIVER is defined. - if (llvm::sys::Process::GetEnv("SWIFT_USE_NEW_DRIVER") && + if (!newDriverName.empty() && !disallowForwarding && (ExecName == "swift" || ExecName == "swiftc")) { SmallString<256> NewDriverPath(llvm::sys::path::parent_path(Path)); - llvm::sys::path::append(NewDriverPath, "swift-driver"); - SmallVector subCommandArgs; - // Rewrite the program argument. - subCommandArgs.push_back(NewDriverPath.c_str()); - if (ExecName == "swiftc") { - subCommandArgs.push_back("--driver-mode=swiftc"); + llvm::sys::path::append(NewDriverPath, newDriverName); + if (!llvm::sys::fs::exists(NewDriverPath)) { + Diags.diagnose(SourceLoc(), diag::remark_forwarding_driver_not_there, + NewDriverPath); } else { - assert(ExecName == "swift"); - subCommandArgs.push_back("--driver-mode=swift"); + SmallVector subCommandArgs; + // Rewrite the program argument. + subCommandArgs.push_back(NewDriverPath.c_str()); + if (ExecName == "swiftc") { + subCommandArgs.push_back("--driver-mode=swiftc"); + } else { + assert(ExecName == "swift"); + subCommandArgs.push_back("--driver-mode=swift"); + } + subCommandArgs.insert(subCommandArgs.end(), argv.begin() + 1, argv.end()); + + // Execute the subcommand. + subCommandArgs.push_back(nullptr); + Diags.diagnose(SourceLoc(), diag::remark_forwarding_to_new_driver, + NewDriverPath); + ExecuteInPlace(NewDriverPath.c_str(), subCommandArgs.data()); + + // If we reach here then an error occurred (typically a missing path). + std::string ErrorString = llvm::sys::StrError(); + llvm::errs() << "error: unable to invoke subcommand: " << subCommandArgs[0] + << " (" << ErrorString << ")\n"; + return 2; } - subCommandArgs.insert(subCommandArgs.end(), argv.begin() + 1, argv.end()); - - // Execute the subcommand. - subCommandArgs.push_back(nullptr); - Diags.diagnose(SourceLoc(), diag::remark_forwarding_to_new_driver); - ExecuteInPlace(NewDriverPath.c_str(), subCommandArgs.data()); - - // If we reach here then an error occurred (typically a missing path). - std::string ErrorString = llvm::sys::StrError(); - llvm::errs() << "error: unable to invoke subcommand: " << subCommandArgs[0] - << " (" << ErrorString << ")\n"; - return 2; } Driver TheDriver(Path, ExecName, argv, Diags); diff --git a/validation-test/Sema/type_checker_perf/slow/expression_too_complex_4.swift b/validation-test/Sema/type_checker_perf/fast/expression_too_complex_4.swift similarity index 80% rename from validation-test/Sema/type_checker_perf/slow/expression_too_complex_4.swift rename to validation-test/Sema/type_checker_perf/fast/expression_too_complex_4.swift index f404bb6e35d1c..7ce1e003a7a41 100644 --- a/validation-test/Sema/type_checker_perf/slow/expression_too_complex_4.swift +++ b/validation-test/Sema/type_checker_perf/fast/expression_too_complex_4.swift @@ -5,5 +5,4 @@ func test(_ i: Int, _ j: Int) -> Int { return 1 + (((i >> 1) + (i >> 2) + (i >> 3) + (i >> 4) << 1) << 1) & 0x40 + 1 + (((i >> 1) + (i >> 2) + (i >> 3) + (i >> 4) << 1) << 1) & 0x40 + 1 + (((i >> 1) + (i >> 2) + (i >> 3) + (i >> 4) << 1) << 1) & 0x40 - // expected-error@-1 {{the compiler is unable to type-check this expression in reasonable time}} } diff --git a/validation-test/Sema/type_checker_perf/fast/rdar18360240.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar18360240.swift.gyb index d3a80b33700c9..a9e1b674c85ec 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar18360240.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar18360240.swift.gyb @@ -1,4 +1,4 @@ -// RUN: %scale-test --begin 2 --end 10 --step 2 --select NumConstraintScopes --polynomial-threshold 1.5 %s +// RUN: %scale-test --begin 2 --end 10 --step 2 --select NumConstraintScopes %s // REQUIRES: asserts,no_asan let empty: [Int] = [] diff --git a/validation-test/Sema/type_checker_perf/slow/rdar22022980.swift b/validation-test/Sema/type_checker_perf/fast/rdar22022980.swift similarity index 83% rename from validation-test/Sema/type_checker_perf/slow/rdar22022980.swift rename to validation-test/Sema/type_checker_perf/fast/rdar22022980.swift index 74b5698def071..c396254f9b227 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar22022980.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar22022980.swift @@ -2,4 +2,3 @@ // REQUIRES: tools-release,no_asan _ = [1, 3, 5, 7, 11].filter{ $0 == 1 || $0 == 3 || $0 == 11 || $0 == 1 || $0 == 3 || $0 == 11 } == [ 1, 3, 11 ] -// expected-error@-1 {{unable to type-check}} diff --git a/validation-test/Sema/type_checker_perf/slow/rdar23429943.swift b/validation-test/Sema/type_checker_perf/fast/rdar23429943.swift similarity index 69% rename from validation-test/Sema/type_checker_perf/slow/rdar23429943.swift rename to validation-test/Sema/type_checker_perf/fast/rdar23429943.swift index 7f3efc941f47e..e1edbcda1fd8a 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar23429943.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar23429943.swift @@ -1,7 +1,6 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 // REQUIRES: tools-release,no_asan -// expected-error@+1 {{the compiler is unable to type-check this expression in reasonable time}} let _ = [0].reduce([Int]()) { return $0.count == 0 && ($1 == 0 || $1 == 2 || $1 == 3) ? [] : $0 + [$1] } diff --git a/validation-test/Sema/type_checker_perf/slow/rdar23861629.swift b/validation-test/Sema/type_checker_perf/fast/rdar23861629.swift similarity index 86% rename from validation-test/Sema/type_checker_perf/slow/rdar23861629.swift rename to validation-test/Sema/type_checker_perf/fast/rdar23861629.swift index 3f10765e02aa0..3b68b4ac93e11 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar23861629.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar23861629.swift @@ -5,7 +5,6 @@ struct S { var s: String? } func rdar23861629(_ a: [S]) { _ = a.reduce("") { - // expected-error@-1 {{reasonable time}} ($0 == "") ? ($1.s ?? "") : ($0 + "," + ($1.s ?? "")) + ($1.s ?? "test") + ($1.s ?? "okay") } } diff --git a/validation-test/Sema/type_checker_perf/slow/mixed_string_array_addition.swift b/validation-test/Sema/type_checker_perf/slow/mixed_string_array_addition.swift new file mode 100644 index 0000000000000..6b7fa19f0cb2b --- /dev/null +++ b/validation-test/Sema/type_checker_perf/slow/mixed_string_array_addition.swift @@ -0,0 +1,12 @@ +// RUN: %target-typecheck-verify-swift -swift-version 5 -solver-expression-time-threshold=1 + +func method(_ arg: String, body: () -> [String]) {} + +func test(str: String, properties: [String]) { + // expected-error@+1 {{the compiler is unable to type-check this expression in reasonable time}} + method(str + "" + str + "") { + properties.map { param in + "" + param + "" + param + "" + } + [""] + } +}