Solver: Prevent infinite recursion in analyzeUnsolvableRule

In complex scenarios reasons for learned rules can themselves be learned
rules caused by other learned rules which had the some of the same
reasons. In this situation iterating over all problem rules requires
keeping track of which rules have previously been analyzed to avoid and
endless loop.

Side effect is that the sorting of problems including learned rules
changes slightly.
main
Nils Adermann 4 years ago
parent 6e8e5f6ba6
commit 7b4cb9c370

@ -518,15 +518,19 @@ class Solver
* @param Problem $problem
* @param Rule $conflictRule
*/
private function analyzeUnsolvableRule(Problem $problem, Rule $conflictRule)
private function analyzeUnsolvableRule(Problem $problem, Rule $conflictRule, &$ruleSeen)
{
$ruleSeen[spl_object_hash($conflictRule)] = true;
if ($conflictRule->getType() == RuleSet::TYPE_LEARNED) {
$why = spl_object_hash($conflictRule);
$learnedWhy = $this->learnedWhy[$why];
$problemRules = $this->learnedPool[$learnedWhy];
foreach ($problemRules as $problemRule) {
$this->analyzeUnsolvableRule($problem, $problemRule);
if (!isset($ruleSeen[spl_object_hash($problemRule)])) {
$this->analyzeUnsolvableRule($problem, $problemRule, $ruleSeen);
}
}
return;
@ -550,7 +554,9 @@ class Solver
$problem = new Problem();
$problem->addRule($conflictRule);
$this->analyzeUnsolvableRule($problem, $conflictRule);
$ruleSeen = array();
$this->analyzeUnsolvableRule($problem, $conflictRule, $ruleSeen);
$this->problems[] = $problem;
@ -576,7 +582,7 @@ class Solver
$why = $decision[Decisions::DECISION_REASON];
$problem->addRule($why);
$this->analyzeUnsolvableRule($problem, $why);
$this->analyzeUnsolvableRule($problem, $why, $ruleSeen);
$literals = $why->getLiterals();

@ -116,13 +116,13 @@ Your requirements could not be resolved to an installable set of packages.
Problem 1
- Conclusion: don't install friendsofphp/php-cs-fixer v2.10.5 (conflict analysis result)
- Conclusion: don't install symfony/console v3.4.29 (conflict analysis result)
- Conclusion: don't install symfony/console v2.8.8 (conflict analysis result)
- Conclusion: don't install symfony/console v3.4.28 (conflict analysis result)
- Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5].
- illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26].
- illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8].
- Conclusion: don't install symfony/console v2.8.7 (conflict analysis result)
- Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5].
- Conclusion: don't install symfony/console v3.4.28 (conflict analysis result)
- Conclusion: don't install symfony/console v2.8.8 (conflict analysis result)
- Conclusion: don't install symfony/console v3.4.29 (conflict analysis result)
- Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0].
- friendsofphp/php-cs-fixer v2.10.4 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29].
- You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29].

@ -0,0 +1,325 @@
--TEST--
See Github issue #9012 ( https://github.com/composer/composer/issues/9012 and https://gist.github.com/Seldaek/4e2dbc2cea4b4fd7a8207bb310ec8e34).
Recursive analysis of the same learnt rules can lead to infinite recursion in solver.
--COMPOSER--
{
"require": {
"php": ">=7.1.7",
"laravel/framework": "^6.0",
"nunomaduro/collision": "^4.0"
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"process-timeout": 0
},
"repositories": {
"laravel/framework": {
"type": "package",
"package": [
{
"name": "laravel/framework",
"version": "v6.0.0",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "6.x-dev"
}
},
"require": {
"symfony/console": "^4.3.4"
}
}
]
},
"nunomaduro/collision": {
"type": "package",
"package": [
{
"name": "nunomaduro/collision",
"version": "v4.2.0",
"type": "library",
"extra": {
"laravel": {
"providers": [
"NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider"
]
}
},
"require": {
"symfony/console": "^5.0"
}
}
]
},
"symfony/console": {
"type": "package",
"package": [
{
"name": "symfony/console",
"version": "v5.1.7",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.6",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.5",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.4",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.3",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.2",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.1",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.0",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.0-RC2",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.0-RC1",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.0-BETA1",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.11",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.10",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.9",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.8",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.7",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.6",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.5",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.4",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.3",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.2",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.1",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.0",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.15",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.14",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.13",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.12",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.11",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.10",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.9",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.8",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.7",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.6",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.5",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.4",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.3",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.2",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.1",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.0",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.0-RC1",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.0-BETA2",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.0-BETA1",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.3.11",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.3.10",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.3.9",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.3.8",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.3.7",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.3.6",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.3.5",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.3.4",
"type": "library"
}
]
}
}
}
--RUN--
update
--EXPECT--
--EXPECT-OUTPUT--
--EXPECT-EXIT-CODE--
2

@ -37,9 +37,9 @@ Updating dependencies
Your requirements could not be resolved to an installable set of packages.
Problem 1
- Conclusion: don't install regular/pkg 1.0.3 (conflict analysis result)
- Conclusion: don't install regular/pkg 1.0.2 (conflict analysis result)
- Conclusion: don't install regular/pkg 1.0.1 (conflict analysis result)
- Conclusion: don't install regular/pkg 1.0.2 (conflict analysis result)
- Conclusion: don't install regular/pkg 1.0.3 (conflict analysis result)
- Only one of these can be installed: regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3], replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. replacer/pkg replaces regular/pkg and thus cannot coexist with it.
- Root composer.json requires regular/pkg 1.* -> satisfiable by regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3].
- Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3].

Loading…
Cancel
Save