diff --git a/lib/spack/spack/multimethod.py b/lib/spack/spack/multimethod.py
index dc948d2861255a1de8ab0c7f32c77f3f59a0a564..efc5f5a9e1ddfc591e310fa1fd074599b1362019 100644
--- a/lib/spack/spack/multimethod.py
+++ b/lib/spack/spack/multimethod.py
@@ -26,7 +26,7 @@
 import spack.architecture
 import spack.error
 from spack.util.lang import *
-from spack.spec import parse_local_spec, Spec
+from spack.spec import parse_anonymous_spec, Spec
 
 
 class SpecMultiMethod(object):
@@ -179,7 +179,7 @@ def install(self, prefix):
 class when(object):
     def __init__(self, spec):
         pkg = get_calling_package_name()
-        self.spec = parse_local_spec(spec, pkg)
+        self.spec = parse_anonymous_spec(spec, pkg)
 
     def __call__(self, method):
         # Get the first definition of the method in the calling scope
diff --git a/lib/spack/spack/packages/__init__.py b/lib/spack/spack/packages/__init__.py
index d0e6b74f0aeeb13375e0f622420837e41c2ea1c2..a6dd4d1b5c75b3f9b1d0eb291af4da131238aef9 100644
--- a/lib/spack/spack/packages/__init__.py
+++ b/lib/spack/spack/packages/__init__.py
@@ -21,7 +21,7 @@
 instances = {}
 
 
-def autospec(function):
+def _autospec(function):
     """Decorator that automatically converts the argument of a single-arg
        function to a Spec."""
     def converter(arg):
@@ -147,7 +147,7 @@ def satisfies(self, other):
 
 
 
-@autospec
+@_autospec
 def get(spec):
     if spec.virtual:
         raise UnknownPackageError(spec.name)
@@ -159,12 +159,12 @@ def get(spec):
     return instances[spec.name]
 
 
-@autospec
+@_autospec
 def get_installed(spec):
     return [s for s in installed_package_specs() if s.satisfies(spec)]
 
 
-@autospec
+@_autospec
 def providers_for(vpkg_spec):
     if not hasattr(providers_for, 'index'):
         providers_for.index = ProviderIndex(all_package_names())
diff --git a/lib/spack/spack/relations.py b/lib/spack/spack/relations.py
index 398f25429f8e8a66e017208827d2fb5050b98d3c..aaa1495c910fec39a4fdaecbcb9c916288543eb4 100644
--- a/lib/spack/spack/relations.py
+++ b/lib/spack/spack/relations.py
@@ -51,7 +51,7 @@ class Mpileaks(Package):
 import spack
 import spack.spec
 import spack.error
-from spack.spec import Spec, parse_local_spec
+from spack.spec import Spec, parse_anonymous_spec
 from spack.packages import packages_module
 from spack.util.lang import *
 
@@ -76,7 +76,7 @@ def provides(*specs, **kwargs):
     """
     pkg = get_calling_package_name()
     spec_string = kwargs.get('when', pkg)
-    provider_spec = parse_local_spec(spec_string, pkg)
+    provider_spec = parse_anonymous_spec(spec_string, pkg)
 
     provided = caller_locals().setdefault("provided", {})
     for string in specs:
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index f2c3d70f230e6f33221079cfdf105f384b250fc1..1657e2128b45efffa585aae0be9d271acddd18f3 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -739,6 +739,8 @@ def validate_names(self):
 
 
     def constrain(self, other, **kwargs):
+        other = self._autospec(other)
+
         if not self.name == other.name:
             raise UnsatisfiableSpecNameError(self.name, other.name)
 
@@ -766,10 +768,10 @@ def constrain(self, other, **kwargs):
         self.architecture = self.architecture or other.architecture
 
         if kwargs.get('deps', True):
-            self.constrain_dependencies(other)
+            self._constrain_dependencies(other)
 
 
-    def constrain_dependencies(self, other):
+    def _constrain_dependencies(self, other):
         """Apply constraints of other spec's dependencies to this spec."""
         if not self.dependencies or not other.dependencies:
             return
@@ -806,9 +808,22 @@ def dep_difference(self, other):
         return mine
 
 
+    def _autospec(self, spec_like):
+        """Used to convert arguments to specs.  If spec_like is a spec, returns it.
+           If it's a string, tries to parse a string.  If that fails, tries to parse
+           a local spec from it (i.e. name is assumed to be self's name).
+        """
+        if isinstance(spec_like, spack.spec.Spec):
+            return spec_like
+
+        try:
+            return spack.spec.Spec(spec_like)
+        except SpecError:
+            return parse_anonymous_spec(spec_like, self.name)
+
+
     def satisfies(self, other, **kwargs):
-        if not isinstance(other, Spec):
-            other = Spec(other)
+        other = self._autospec(other)
 
         # First thing we care about is whether the name matches
         if self.name != other.name:
@@ -934,9 +949,7 @@ def __getitem__(self, name):
     def __contains__(self, spec):
         """True if this spec has any dependency that satisfies the supplied
            spec."""
-        if not isinstance(spec, Spec):
-            spec = Spec(spec)
-
+        spec = self._autospec(spec)
         for s in self.preorder_traversal():
             if s.satisfies(spec):
                 return True
@@ -1104,18 +1117,22 @@ def __init__(self):
 
     def do_parse(self):
         specs = []
-        while self.next:
-            if self.accept(ID):
-                specs.append(self.spec())
 
-            elif self.accept(DEP):
-                if not specs:
-                    self.last_token_error("Dependency has no package")
-                self.expect(ID)
-                specs[-1]._add_dependency(self.spec())
+        try:
+            while self.next:
+                if self.accept(ID):
+                    specs.append(self.spec())
 
-            else:
-                self.unexpected_token()
+                elif self.accept(DEP):
+                    if not specs:
+                        self.last_token_error("Dependency has no package")
+                    self.expect(ID)
+                    specs[-1]._add_dependency(self.spec())
+
+                else:
+                    self.unexpected_token()
+        except spack.parse.ParseError, e:
+            raise SpecParseError(e)
 
         return specs
 
@@ -1238,7 +1255,7 @@ def parse(string):
     return SpecParser().parse(string)
 
 
-def parse_local_spec(spec_like, pkg_name):
+def parse_anonymous_spec(spec_like, pkg_name):
     """Allow the user to omit the package name part of a spec if they
        know what it has to be already.
 
@@ -1251,19 +1268,19 @@ def parse_local_spec(spec_like, pkg_name):
 
     if isinstance(spec_like, str):
         try:
-            local_spec = Spec(spec_like)
-        except spack.parse.ParseError:
-            local_spec = Spec(pkg_name + spec_like)
-            if local_spec.name != pkg_name: raise ValueError(
+            anon_spec = Spec(spec_like)
+        except SpecParseError:
+            anon_spec = Spec(pkg_name + spec_like)
+            if anon_spec.name != pkg_name: raise ValueError(
                     "Invalid spec for package %s: %s" % (pkg_name, spec_like))
     else:
-        local_spec = spec_like
+        anon_spec = spec_like.copy()
 
-    if local_spec.name != pkg_name:
+    if anon_spec.name != pkg_name:
         raise ValueError("Spec name '%s' must match package name '%s'"
-                         % (local_spec.name, pkg_name))
+                         % (anon_spec.name, pkg_name))
 
-    return local_spec
+    return anon_spec
 
 
 class SpecError(spack.error.SpackError):
@@ -1272,6 +1289,14 @@ def __init__(self, message):
         super(SpecError, self).__init__(message)
 
 
+class SpecParseError(SpecError):
+    """Wrapper for ParseError for when we're parsing specs."""
+    def __init__(self, parse_error):
+        super(SpecParseError, self).__init__(parse_error.message)
+        self.string = parse_error.string
+        self.pos = parse_error.pos
+
+
 class DuplicateDependencyError(SpecError):
     """Raised when the same dependency occurs in a spec twice."""
     def __init__(self, message):
diff --git a/lib/spack/spack/test/spec_semantics.py b/lib/spack/spack/test/spec_semantics.py
index d1cd5ca1c5b8f7f10e9f00040a7480ab84cba414..a0ded3b30c706ba2ee99de168375fff1096cd7fe 100644
--- a/lib/spack/spack/test/spec_semantics.py
+++ b/lib/spack/spack/test/spec_semantics.py
@@ -9,25 +9,34 @@ class SpecSematicsTest(MockPackagesTest):
     # ================================================================================
     # Utility functions to set everything up.
     # ================================================================================
-    def check_satisfies(self, lspec, rspec):
-        l, r = Spec(lspec), Spec(rspec)
-        self.assertTrue(l.satisfies(r))
-        self.assertTrue(r.satisfies(l))
+    def check_satisfies(self, spec, anon_spec):
+        left = Spec(spec)
+        right = parse_anonymous_spec(anon_spec, left.name)
+
+        self.assertTrue(left.satisfies(right))
+        self.assertTrue(left.satisfies(anon_spec))
+        self.assertTrue(right.satisfies(left))
 
         try:
-            l.constrain(r)
-            r.constrain(l)
+            left.copy().constrain(right)
+            left.copy().constrain(anon_spec)
+            right.copy().constrain(left)
         except SpecError, e:
             self.fail("Got a SpecError in constrain!  " + e.message)
 
 
-    def check_unsatisfiable(self, lspec, rspec):
-        l, r = Spec(lspec), Spec(rspec)
-        self.assertFalse(l.satisfies(r))
-        self.assertFalse(r.satisfies(l))
+    def check_unsatisfiable(self, spec, anon_spec):
+        left = Spec(spec)
+        right = parse_anonymous_spec(anon_spec, left.name)
+
+        self.assertFalse(left.satisfies(right))
+        self.assertFalse(left.satisfies(anon_spec))
+
+        self.assertFalse(right.satisfies(left))
 
-        self.assertRaises(UnsatisfiableSpecError, l.constrain, r)
-        self.assertRaises(UnsatisfiableSpecError, r.constrain, l)
+        self.assertRaises(UnsatisfiableSpecError, left.constrain, right)
+        self.assertRaises(UnsatisfiableSpecError, left.constrain, anon_spec)
+        self.assertRaises(UnsatisfiableSpecError, right.constrain, left)
 
 
     def check_constrain(self, expected, constrained, constraint):
@@ -48,77 +57,77 @@ def check_invalid_constraint(self, constrained, constraint):
     # Satisfiability and constraints
     # ================================================================================
     def test_satisfies(self):
-        self.check_satisfies('libelf@0.8.13', 'libelf@0:1')
-        self.check_satisfies('libdwarf^libelf@0.8.13', 'libdwarf^libelf@0:1')
+        self.check_satisfies('libelf@0.8.13', '@0:1')
+        self.check_satisfies('libdwarf^libelf@0.8.13', '^libelf@0:1')
 
 
     def test_satisfies_compiler(self):
-        self.check_satisfies('foo%gcc', 'foo%gcc')
-        self.check_satisfies('foo%intel', 'foo%intel')
-        self.check_unsatisfiable('foo%intel', 'foo%gcc')
-        self.check_unsatisfiable('foo%intel', 'foo%pgi')
+        self.check_satisfies('foo%gcc', '%gcc')
+        self.check_satisfies('foo%intel', '%intel')
+        self.check_unsatisfiable('foo%intel', '%gcc')
+        self.check_unsatisfiable('foo%intel', '%pgi')
 
 
     def test_satisfies_compiler_version(self):
-        self.check_satisfies('foo%gcc', 'foo%gcc@4.7.2')
-        self.check_satisfies('foo%intel', 'foo%intel@4.7.2')
+        self.check_satisfies('foo%gcc', '%gcc@4.7.2')
+        self.check_satisfies('foo%intel', '%intel@4.7.2')
 
-        self.check_satisfies('foo%pgi@4.5', 'foo%pgi@4.4:4.6')
-        self.check_satisfies('foo@2.0%pgi@4.5', 'foo@1:3%pgi@4.4:4.6')
+        self.check_satisfies('foo%pgi@4.5', '%pgi@4.4:4.6')
+        self.check_satisfies('foo@2.0%pgi@4.5', '@1:3%pgi@4.4:4.6')
 
-        self.check_unsatisfiable('foo%pgi@4.3', 'foo%pgi@4.4:4.6')
-        self.check_unsatisfiable('foo@4.0%pgi', 'foo@1:3%pgi')
-        self.check_unsatisfiable('foo@4.0%pgi@4.5', 'foo@1:3%pgi@4.4:4.6')
+        self.check_unsatisfiable('foo%pgi@4.3', '%pgi@4.4:4.6')
+        self.check_unsatisfiable('foo@4.0%pgi', '@1:3%pgi')
+        self.check_unsatisfiable('foo@4.0%pgi@4.5', '@1:3%pgi@4.4:4.6')
 
 
     def test_satisfies_architecture(self):
-        self.check_satisfies('foo=chaos_5_x86_64_ib', 'foo=chaos_5_x86_64_ib')
-        self.check_satisfies('foo=bgqos_0', 'foo=bgqos_0')
+        self.check_satisfies('foo=chaos_5_x86_64_ib', '=chaos_5_x86_64_ib')
+        self.check_satisfies('foo=bgqos_0', '=bgqos_0')
 
-        self.check_unsatisfiable('foo=bgqos_0', 'foo=chaos_5_x86_64_ib')
-        self.check_unsatisfiable('foo=chaos_5_x86_64_ib', 'foo=bgqos_0')
+        self.check_unsatisfiable('foo=bgqos_0', '=chaos_5_x86_64_ib')
+        self.check_unsatisfiable('foo=chaos_5_x86_64_ib', '=bgqos_0')
 
 
     def test_satisfies_dependencies(self):
-        self.check_satisfies('mpileaks^mpich', 'mpileaks^mpich')
-        self.check_satisfies('mpileaks^zmpi', 'mpileaks^zmpi')
+        self.check_satisfies('mpileaks^mpich', '^mpich')
+        self.check_satisfies('mpileaks^zmpi', '^zmpi')
 
-        self.check_unsatisfiable('mpileaks^mpich', 'mpileaks^zmpi')
-        self.check_unsatisfiable('mpileaks^zmpi', 'mpileaks^mpich')
+        self.check_unsatisfiable('mpileaks^mpich', '^zmpi')
+        self.check_unsatisfiable('mpileaks^zmpi', '^mpich')
 
 
     def test_satisfies_dependency_versions(self):
-        self.check_satisfies('mpileaks^mpich@2.0', 'mpileaks^mpich@1:3')
-        self.check_unsatisfiable('mpileaks^mpich@1.2', 'mpileaks^mpich@2.0')
+        self.check_satisfies('mpileaks^mpich@2.0', '^mpich@1:3')
+        self.check_unsatisfiable('mpileaks^mpich@1.2', '^mpich@2.0')
 
-        self.check_satisfies('mpileaks^mpich@2.0^callpath@1.5', 'mpileaks^mpich@1:3^callpath@1.4:1.6')
-        self.check_unsatisfiable('mpileaks^mpich@4.0^callpath@1.5', 'mpileaks^mpich@1:3^callpath@1.4:1.6')
-        self.check_unsatisfiable('mpileaks^mpich@2.0^callpath@1.7', 'mpileaks^mpich@1:3^callpath@1.4:1.6')
-        self.check_unsatisfiable('mpileaks^mpich@4.0^callpath@1.7', 'mpileaks^mpich@1:3^callpath@1.4:1.6')
+        self.check_satisfies('mpileaks^mpich@2.0^callpath@1.5', '^mpich@1:3^callpath@1.4:1.6')
+        self.check_unsatisfiable('mpileaks^mpich@4.0^callpath@1.5', '^mpich@1:3^callpath@1.4:1.6')
+        self.check_unsatisfiable('mpileaks^mpich@2.0^callpath@1.7', '^mpich@1:3^callpath@1.4:1.6')
+        self.check_unsatisfiable('mpileaks^mpich@4.0^callpath@1.7', '^mpich@1:3^callpath@1.4:1.6')
 
 
     def test_satisfies_virtual_dependencies(self):
-        self.check_satisfies('mpileaks^mpi', 'mpileaks^mpi')
-        self.check_satisfies('mpileaks^mpi', 'mpileaks^mpich')
+        self.check_satisfies('mpileaks^mpi', '^mpi')
+        self.check_satisfies('mpileaks^mpi', '^mpich')
 
-        self.check_satisfies('mpileaks^mpi', 'mpileaks^zmpi')
-        self.check_unsatisfiable('mpileaks^mpich', 'mpileaks^zmpi')
+        self.check_satisfies('mpileaks^mpi', '^zmpi')
+        self.check_unsatisfiable('mpileaks^mpich', '^zmpi')
 
 
     def test_satisfies_virtual_dependency_versions(self):
-        self.check_satisfies('mpileaks^mpi@1.5', 'mpileaks^mpi@1.2:1.6')
-        self.check_unsatisfiable('mpileaks^mpi@3', 'mpileaks^mpi@1.2:1.6')
+        self.check_satisfies('mpileaks^mpi@1.5', '^mpi@1.2:1.6')
+        self.check_unsatisfiable('mpileaks^mpi@3', '^mpi@1.2:1.6')
 
-        self.check_satisfies('mpileaks^mpi@2:', 'mpileaks^mpich')
-        self.check_satisfies('mpileaks^mpi@2:', 'mpileaks^mpich@3.0.4')
-        self.check_satisfies('mpileaks^mpi@2:', 'mpileaks^mpich2@1.4')
+        self.check_satisfies('mpileaks^mpi@2:', '^mpich')
+        self.check_satisfies('mpileaks^mpi@2:', '^mpich@3.0.4')
+        self.check_satisfies('mpileaks^mpi@2:', '^mpich2@1.4')
 
-        self.check_satisfies('mpileaks^mpi@1:', 'mpileaks^mpich2')
-        self.check_satisfies('mpileaks^mpi@2:', 'mpileaks^mpich2')
+        self.check_satisfies('mpileaks^mpi@1:', '^mpich2')
+        self.check_satisfies('mpileaks^mpi@2:', '^mpich2')
 
-        self.check_unsatisfiable('mpileaks^mpi@3:', 'mpileaks^mpich2@1.4')
-        self.check_unsatisfiable('mpileaks^mpi@3:', 'mpileaks^mpich2')
-        self.check_unsatisfiable('mpileaks^mpi@3:', 'mpileaks^mpich@1.0')
+        self.check_unsatisfiable('mpileaks^mpi@3:', '^mpich2@1.4')
+        self.check_unsatisfiable('mpileaks^mpi@3:', '^mpich2')
+        self.check_unsatisfiable('mpileaks^mpi@3:', '^mpich@1.0')
 
 
     def test_constrain(self):
diff --git a/lib/spack/spack/test/spec_syntax.py b/lib/spack/spack/test/spec_syntax.py
index 40796502462f594d3e435e5131a10ef189e9f037..ec6761015ea30908ab367c25984aeec52699a1b1 100644
--- a/lib/spack/spack/test/spec_syntax.py
+++ b/lib/spack/spack/test/spec_syntax.py
@@ -1,7 +1,7 @@
 import unittest
 import spack.spec
 from spack.spec import *
-from spack.parse import Token, ParseError
+from spack.parse import Token
 
 # Sample output for a complex lexing.
 complex_lex = [Token(ID, 'mvapich_foo'),
@@ -96,10 +96,10 @@ def test_canonicalize(self):
         self.check_parse("x^y", "x@: ^y@:")
 
     def test_parse_errors(self):
-        self.assertRaises(ParseError, self.check_parse, "x@@1.2")
-        self.assertRaises(ParseError, self.check_parse, "x ^y@@1.2")
-        self.assertRaises(ParseError, self.check_parse, "x@1.2::")
-        self.assertRaises(ParseError, self.check_parse, "x::")
+        self.assertRaises(SpecParseError, self.check_parse, "x@@1.2")
+        self.assertRaises(SpecParseError, self.check_parse, "x ^y@@1.2")
+        self.assertRaises(SpecParseError, self.check_parse, "x@1.2::")
+        self.assertRaises(SpecParseError, self.check_parse, "x::")
 
     def test_duplicate_variant(self):
         self.assertRaises(DuplicateVariantError, self.check_parse, "x@1.2+debug+debug")