diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index fb6ed509888..a0fadc0bfac 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -991,6 +991,10 @@ namespace swift { /// debugging unsigned ShuffleDisjunctionChoicesSeed = 0; + /// If true, we will crash if the constraint solver found a valid solution + /// in diagnostic mode. + bool CrashOnValidSalvage = false; + /// Triggers llvm fatal error if the typechecker tries to typecheck a decl /// or an identifier reference with any of the provided prefix names. This /// is for testing purposes. diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index aa164c56156..9736a698928 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -438,8 +438,11 @@ def solver_shuffle_disjunctions_EQ : Joined<["-"], "solver-shuffle-disjunctions= def solver_shuffle_choices_EQ : Joined<["-"], "solver-shuffle-choices=">, HelpText<"Random seed for shuffling disjunction choices. For debugging solver performance">; -def disable_named_lazy_member_loading : Flag<["-"], "disable-named-lazy-member-loading">, - HelpText<"Disable per-name lazy member loading (obsolete)">; +def solver_enable_crash_on_valid_salvage : Flag<["-"], "solver-enable-crash-on-valid-salvage">, + HelpText<"Reject valid solutions in diagnostic mode">; + +def solver_disable_crash_on_valid_salvage : Flag<["-"], "solver-disable-crash-on-valid-salvage">, + HelpText<"Accept valid solutions in diagnostic mode">; def disable_named_lazy_import_as_member_loading : Flag<["-"], "disable-named-lazy-import-as-member-loading">, diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 080f0f49d3d..7bc515279e8 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -2127,21 +2127,30 @@ static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args, if (Args.getLastArg(OPT_solver_disable_splitter)) Opts.SolverDisableSplitter = true; - if (Args.hasArg(OPT_solver_enable_prepared_overloads) || - Args.hasArg(OPT_solver_disable_prepared_overloads)) - Opts.SolverEnablePreparedOverloads = Args.hasArg(OPT_solver_enable_prepared_overloads); + Opts.CrashOnValidSalvage = + Args.hasFlag(OPT_solver_enable_crash_on_valid_salvage, + OPT_solver_disable_crash_on_valid_salvage, + Opts.CrashOnValidSalvage); - if (Args.hasArg(OPT_solver_enable_prune_disjunctions) || - Args.hasArg(OPT_solver_disable_prune_disjunctions)) - Opts.SolverPruneDisjunctions = Args.hasArg(OPT_solver_enable_prune_disjunctions); + Opts.SolverEnablePreparedOverloads = + Args.hasFlag(OPT_solver_enable_prepared_overloads, + OPT_solver_disable_prepared_overloads, + Opts.SolverEnablePreparedOverloads); - if (Args.hasArg(OPT_solver_enable_optimize_operator_defaults) || - Args.hasArg(OPT_solver_disable_optimize_operator_defaults)) - Opts.SolverOptimizeOperatorDefaults = Args.hasArg(OPT_solver_enable_optimize_operator_defaults); + Opts.SolverPruneDisjunctions = + Args.hasFlag(OPT_solver_enable_prune_disjunctions, + OPT_solver_disable_prune_disjunctions, + Opts.SolverPruneDisjunctions); - if (Args.hasArg(OPT_solver_enable_performance_hacks) || - Args.hasArg(OPT_solver_disable_performance_hacks)) - Opts.SolverEnablePerformanceHacks = Args.hasArg(OPT_solver_enable_performance_hacks); + Opts.SolverOptimizeOperatorDefaults = + Args.hasFlag(OPT_solver_enable_optimize_operator_defaults, + OPT_solver_disable_optimize_operator_defaults, + Opts.SolverOptimizeOperatorDefaults); + + Opts.SolverEnablePerformanceHacks = + Args.hasFlag(OPT_solver_enable_performance_hacks, + OPT_solver_disable_performance_hacks, + Opts.SolverEnablePerformanceHacks); if (FrontendOpts.RequestedAction == FrontendOptions::ActionType::Immediate) Opts.DeferToRuntime = true; diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index c1307acb696..424cc6e5bdd 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -2028,6 +2028,22 @@ SolutionResult ConstraintSystem::salvage() { if (*best != 0) viable[0] = std::move(viable[*best]); viable.erase(viable.begin() + 1, viable.end()); + + if (getASTContext().TypeCheckerOpts.CrashOnValidSalvage) { + auto &solution = viable[0]; + if (solution.Fixes.empty() && + diagnosticTransaction == nullptr && + !getASTContext().LangOpts.DisableAvailabilityChecking && + solution.getFixedScore().Data[SK_Unavailable] == 0 && + solution.getFixedScore().Data[SK_Hole] == 0 && + solution.getFixedScore().Data[SK_Fix] == 0) { + ABORT([&](auto &out) { + out << "Found valid solution in salvage()\n\n"; + solution.dump(out, 0); + }); + } + } + return SolutionResult::forSolved(std::move(viable[0])); } diff --git a/test/Constraints/solution_with_holes.swift b/test/Constraints/solution_with_holes.swift new file mode 100644 index 00000000000..4c9bd009c37 --- /dev/null +++ b/test/Constraints/solution_with_holes.swift @@ -0,0 +1,14 @@ +// RUN: %target-typecheck-verify-swift + +protocol P { + associatedtype X // expected-note {{protocol requires nested type 'X'}} +} + +struct S: P {} // expected-error {{type 'S' does not conform to protocol 'P'}} +// expected-note@-1 {{add stubs for conformance}} + +func foo(_ x: T) -> T.X {} + +func bar(_ x: S) { + foo(x) // result of foo() is a hole +}