Repository: spack/spack

Issue:
clingo error with versions of virtual packages
### Steps to reproduce

This is a bug with clingo and versions of virtual packages.

For hpcviewer, old versions require java exactly version 8, new
versions use java >= 11, so we have:

```
depends_on('java@11:', type=('build', 'run'), when='@2021.0:')
depends_on('java@8', type=('build', 'run'), when='@:2020')
```

Note that the two when regions are disjoint, so for a given version,
only one can be correct.  But with clingo, an old spec
`hpcviewer@2019.12` concretizes as:

```
hpcviewer@2019.12%gcc@8.4.1 arch=linux-rhel8-x86_64
    ^openjdk@11.0.12_7%gcc@8.4.1 arch=linux-rhel8-x86_64
```

This is incorrect.  Version 2019.12 satisfies @:2020 and
should require java 8, so openjdk 1.8.

The problem is with the combination of clingo and java as a virtual
package.  The original concretizer correctly picks `openjdk@1.8.0_265-b01`

Or, if I change `depends_on('java')` to a concrete package openjdk
(version 1.8), then clingo correctly picks 1.8.0_265-b01.

ping @tgamblin @alalazo @becker33 
anyone else ?

### Error message

$ spack -d spec hpcviewer@2019.12

[spack-spec.txt](https://github.com/spack/spack/files/7340312/spack-spec.txt)


### Information on your system

$ spack debug report
* **Spack:** 0.11.2-14795-81c272fcb7
* **Python:** 3.6.8
* **Platform:** linux-rhel8-zen2
* **Concretizer:** clingo

### General information

- [X] I have run `spack debug report` and reported the version of Spack/Python/Platform
- [X] I have searched the issues of this repo and believe this is not a duplicate
- [X] I have run the failing commands in debug mode and reported the output


Discussion:
Bug confirmed. This is due to the fact that we don't have a "version uniqueness" rule for virtual packages. This diff:
```diff
diff --git a/lib/spack/spack/solver/concretize.lp b/lib/spack/spack/solver/concretize.lp
index e444faffec..a05c062071 100644
--- a/lib/spack/spack/solver/concretize.lp
+++ b/lib/spack/spack/solver/concretize.lp
@@ -28,6 +28,8 @@ version_declared(Package, Version) :- version_declared(Package, Version, _).
 1 { version(Package, Version) : version_declared(Package, Version) } 1
  :- node(Package).
 
+:- virtual_node(Package), 2 { version(Package, _) }.
+
 % If we select a deprecated version, mark the package as deprecated
 deprecated(Package, Version) :- version(Package, Version), deprecated_version(Package, Version).
```
solves the bug for me. Currently checking if any test is failing and trying to add a regression test.
For reference, the current answer set contains 2 versions of `java`:
```
version_satisfies("hpcviewer","2019.12") version_satisfies("hpcviewer",":2020") version("hpcviewer","2019.12") virtual_node("java") version_satisfies("java","8") version_satisfies("java","11:") version_satisfies("java","11") version("java","11") version("java","8") version_satisfies("openjdk","11.0:11") version("openjdk","11.0.12_7")
```

Resolving patch:
diff --git a/var/spack/repos/builtin.mock/packages/hpcviewer/package.py b/var/spack/repos/builtin.mock/packages/hpcviewer/package.py
new file mode 100644
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/hpcviewer/package.py
@@ -0,0 +1,13 @@
+# Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+class Hpcviewer(AutotoolsPackage):
+    """Uses version-test-pkg, as a build dependency"""
+    homepage = "http://www.spack.org"
+    url = "http://www.spack.org/downloads/aml-1.0.tar.gz"
+
+    version('2019.02', '0123456789abcdef0123456789abcdef')
+
+    depends_on('java@11:', type=('build', 'run'), when='@2021.0:')
+    depends_on('java@8', type=('build', 'run'), when='@:2020')
diff --git a/var/spack/repos/builtin.mock/packages/virtual-with-versions/package.py b/var/spack/repos/builtin.mock/packages/virtual-with-versions/package.py
new file mode 100644
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/virtual-with-versions/package.py
@@ -0,0 +1,20 @@
+# Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+class VirtualWithVersions(AutotoolsPackage):
+    """Uses version-test-pkg, as a build dependency"""
+    homepage = "http://www.spack.org"
+    url = "http://www.spack.org/downloads/aml-1.0.tar.gz"
+
+    version('17.0.1', '0123456789abcdef0123456789abcdef')
+    version('16.0.1', '0123456789abcdef0123456789abcdef')
+    version('11.0.1', '0123456789abcdef0123456789abcdef')
+    version('1.8.0', '0123456789abcdef0123456789abcdef')
+
+    provides('java@17', when='@17.0:17.9')
+    provides('java@16', when='@16.0:16.9')
+    provides('java@11', when='@11.0:11.9')
+    provides('java@10', when='@10.0:10.9')
+    provides('java@9', when='@9.0:9.9')
+    provides('java@8', when='@1.8.0:1.8.9')


Test patch:
diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py
--- a/lib/spack/spack/test/concretize.py
+++ b/lib/spack/spack/test/concretize.py
@@ -1267,3 +1267,10 @@ def test_newer_dependency_adds_a_transitive_virtual(self):
         s = spack.spec.Spec('root-adds-virtual').concretized()
         assert s['leaf-adds-virtual'].satisfies('@2.0')
         assert 'blas' in s
+
+    @pytest.mark.regression('26718')
+    def test_versions_in_virtual_dependencies(self):
+        # Ensure that a package that needs a given version of a virtual
+        # package doesn't end up using a later implementation
+        s = spack.spec.Spec('hpcviewer@2019.02').concretized()
+        assert s['java'].satisfies('virtual-with-versions@1.8.0')