Repository: spack/spack

Issue:
Environment errors when reconcretizing
### Steps to reproduce

Activate the following environment:
```yaml
spack:
  specs:
  - zlib-ng
  - hdf5@1.14.2 ~mpi
  - zlib-ng generator=make
  view: false
  concretizer:
    unify: true
```
and concretize it:
```console
$ spack concretize
[ ... ]
```
Then deconcretize `hdf5` and reconcretize:
```console
$ spack deconcretize hdf5
$ spack concretize
```

### Error message

During the last command an assertion error is enmitted:
```console
$ spack concretize 
==> Error: literal specs have different requirements. clear cache before computing literals
```

The trigger is, in general:
- Have 2 or more user specs that map to the same concrete spec on a first concretization
- Deconcretize something else, and try to reconcretize the environment

### Information on your system

* **Spack:** 0.22.0.dev0 (5299b84319d34072fae5be6abdf8e57f06be94e9)
* **Python:** 3.11.5
* **Platform:** linux-ubuntu20.04-icelake
* **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:
Looking at the code, the assertion seems unnecessary[^1] and a fix might just be:
```diff
diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py
index b41bcba228..4661d82e96 100644
--- a/lib/spack/spack/solver/asp.py
+++ b/lib/spack/spack/solver/asp.py
@@ -2709,10 +2709,6 @@ def literal_specs(self, specs):
             # Effect imposes the spec
             imposed_spec_key = str(spec), None
             cache = self._effect_cache[spec.name]
-            msg = (
-                "literal specs have different requirements. clear cache before computing literals"
-            )
-            assert imposed_spec_key not in cache, msg
             effect_id = next(self._effect_id_counter)
             requirements = self.spec_clauses(spec)
             root_name = spec.name
```
Also, the message is misleading, since:
1. The error was stemming from a concrete spec, not from a literal spec
2. Requirements are literally the same (we are enforcing the same concrete spec)

[^1]: I'll double check this and eventually submit a PR with a regression test

Resolving patch:
diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py
--- a/lib/spack/spack/solver/asp.py
+++ b/lib/spack/spack/solver/asp.py
@@ -2699,15 +2699,13 @@ def literal_specs(self, specs):
             self.gen.fact(fn.pkg_fact(spec.name, fn.condition_trigger(condition_id, trigger_id)))
             self.gen.fact(fn.condition_reason(condition_id, f"{spec} requested from CLI"))
 
-            # Effect imposes the spec
             imposed_spec_key = str(spec), None
             cache = self._effect_cache[spec.name]
-            msg = (
-                "literal specs have different requirements. clear cache before computing literals"
-            )
-            assert imposed_spec_key not in cache, msg
-            effect_id = next(self._id_counter)
-            requirements = self.spec_clauses(spec)
+            if imposed_spec_key in cache:
+                effect_id, requirements = cache[imposed_spec_key]
+            else:
+                effect_id = next(self._id_counter)
+                requirements = self.spec_clauses(spec)
             root_name = spec.name
             for clause in requirements:
                 clause_name = clause.args[0]


Test patch:
diff --git a/lib/spack/spack/test/env.py b/lib/spack/spack/test/env.py
--- a/lib/spack/spack/test/env.py
+++ b/lib/spack/spack/test/env.py
@@ -778,3 +778,32 @@ def test_env_with_include_def_missing(mutable_mock_env_path, mock_packages):
     with e:
         with pytest.raises(UndefinedReferenceError, match=r"which does not appear"):
             e.concretize()
+
+
+@pytest.mark.regression("41292")
+def test_deconcretize_then_concretize_does_not_error(mutable_mock_env_path, mock_packages):
+    """Tests that, after having deconcretized a spec, we can reconcretize an environment which
+    has 2 or more user specs mapping to the same concrete spec.
+    """
+    mutable_mock_env_path.mkdir()
+    spack_yaml = mutable_mock_env_path / ev.manifest_name
+    spack_yaml.write_text(
+        """spack:
+      specs:
+      # These two specs concretize to the same hash
+      - c
+      - c@1.0
+      # Spec used to trigger the bug
+      - a
+      concretizer:
+        unify: true
+    """
+    )
+    e = ev.Environment(mutable_mock_env_path)
+    with e:
+        e.concretize()
+        e.deconcretize(spack.spec.Spec("a"), concrete=False)
+        e.concretize()
+    assert len(e.concrete_roots()) == 3
+    all_root_hashes = set(x.dag_hash() for x in e.concrete_roots())
+    assert len(all_root_hashes) == 2