]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: better error messages for empty type sets
authorRobert Findley <rfindley@google.com>
Mon, 1 Nov 2021 18:46:35 +0000 (14:46 -0400)
committerRobert Findley <rfindley@google.com>
Tue, 2 Nov 2021 16:57:03 +0000 (16:57 +0000)
This is a clean port of CL 358175 to go/types.

Change-Id: If1b4e51d1579fd168e651d79d031335ff09ca128
Reviewed-on: https://go-review.googlesource.com/c/go/+/360474
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/go/types/instantiate.go
src/go/types/testdata/check/typeinst2.go2
src/go/types/testdata/fixedbugs/issue47411.go2
src/go/types/typeset.go

index 3720cb725acb11a073240ca94f87dd98ea9cf664..8d8d2818429ecf322b4b441f208d049227445ffb 100644 (file)
@@ -134,8 +134,16 @@ func (check *Checker) verify(pos token.Pos, tparams []*TypeParam, targs []Type)
 // TODO(gri) This should be a method of interfaces or type sets.
 func (check *Checker) satisfies(pos token.Pos, targ Type, tpar *TypeParam, smap substMap) error {
        iface := tpar.iface()
+
+       // Every type argument satisfies interface{}.
        if iface.Empty() {
-               return nil // no type bound
+               return nil
+       }
+
+       // A type argument that is a type parameter with an empty type set satisfies any constraint.
+       // (The empty set is a subset of any set.)
+       if targ := asTypeParam(targ); targ != nil && targ.iface().typeSet().IsEmpty() {
+               return nil
        }
 
        // TODO(rfindley): it would be great if users could pass in a qualifier here,
@@ -149,6 +157,11 @@ func (check *Checker) satisfies(pos token.Pos, targ Type, tpar *TypeParam, smap
                return errors.New(sprintf(nil, qf, false, format, args...))
        }
 
+       // No type argument with non-empty type set satisfies the empty type set.
+       if iface.typeSet().IsEmpty() {
+               return errorf("%s does not satisfy %s (constraint type set is empty)", targ, tpar.bound)
+       }
+
        // The type parameter bound is parameterized with the same type parameters
        // as the instantiated type; before we can use it for bounds checking we
        // need to instantiate it with the type arguments with which we instantiate
@@ -190,28 +203,27 @@ func (check *Checker) satisfies(pos token.Pos, targ Type, tpar *TypeParam, smap
                }
        }
 
-       // targ's underlying type must also be one of the interface types listed, if any
+       // targ must also be in the set of types of iface, if any.
+       // Constraints with empty type sets were already excluded above.
        if !iface.typeSet().hasTerms() {
                return nil // nothing to do
        }
 
-       // If targ is itself a type parameter, each of its possible types, but at least one, must be in the
-       // list of iface types (i.e., the targ type list must be a non-empty subset of the iface types).
+       // If targ is itself a type parameter, each of its possible types must be in the set
+       // of iface types (i.e., the targ type set must be a subset of the iface type set).
+       // Type arguments with empty type sets were already excluded above.
        if targ := asTypeParam(targ); targ != nil {
                targBound := targ.iface()
-               if !targBound.typeSet().hasTerms() {
-                       return errorf("%s does not satisfy %s (%s has no type constraints)", targ, tpar.bound, targ)
-               }
                if !targBound.typeSet().subsetOf(iface.typeSet()) {
-                       // TODO(gri) need better error message
+                       // TODO(gri) report which type is missing
                        return errorf("%s does not satisfy %s", targ, tpar.bound)
                }
                return nil
        }
 
-       // Otherwise, targ's type or underlying type must also be one of the interface types listed, if any.
+       // Otherwise, targ's type must be included in the iface type set.
        if !iface.typeSet().includes(targ) {
-               // TODO(gri) better error message
+               // TODO(gri) report which type is missing
                return errorf("%s does not satisfy %s", targ, tpar.bound)
        }
 
index 37d32263d4accba949b5a8871ddce2796ecc8c7c..ebcc3006750f9482ff1908030506ef0a46db25ae 100644 (file)
@@ -226,10 +226,10 @@ type I012 interface {
 }
 
 func f012[T I012]() {}
-var _ = f012[int /* ERROR does not satisfy I012 */ ]
-var _ = f012[bool /* ERROR does not satisfy I012 */ ]
-var _ = f012[string /* ERROR does not satisfy I012 */ ]
-var _ = f012[float64 /* ERROR does not satisfy I012 */ ]
+var _ = f012[int /* ERROR does not satisfy I012.*type set is empty */ ]
+var _ = f012[bool /* ERROR does not satisfy I012.*type set is empty */ ]
+var _ = f012[string /* ERROR does not satisfy I012.*type set is empty */ ]
+var _ = f012[float64 /* ERROR does not satisfy I012.*type set is empty */ ]
 
 type I12 interface {
        E1
@@ -256,3 +256,23 @@ var _ = f0_[float64 /* ERROR does not satisfy I0_ */ ]
 // Using a function instance as a type is an error.
 var _ f0 // ERROR not a type
 var _ f0 /* ERROR not a type */ [int]
+
+// Empty type sets can only be satisfied by empty type sets.
+type none interface {
+       // force an empty type set
+        int
+        string
+}
+
+func ff[T none]() {}
+func gg[T any]() {}
+func hh[T ~int]() {}
+
+func _[T none]() {
+        _ = ff[int /* ERROR int does not satisfy none \(constraint type set is empty\) */ ]
+        _ = ff[T]  // pathological but ok because T's type set is empty, too
+        _ = gg[int]
+        _ = gg[T]
+       _ = hh[int]
+       _ = hh[T]
+}
index 2fc26d9e8591097dcb7c4a7e389942c5c6084ac8..fde704bb416d45fa1293d3d8efbc0e75fb64079c 100644 (file)
@@ -19,7 +19,7 @@ func _[P comparable,
         _ = f[R /* ERROR R has no constraints */ ]
 
         _ = g[int]
-        _ = g[P /* ERROR P has no type constraints */ ]
+       _ = g[P /* ERROR P does not satisfy interface{interface{comparable; ~int\|~string} */ ]
         _ = g[Q]
         _ = g[func /* ERROR does not satisfy comparable */()]
         _ = g[R /* ERROR R has no constraints */ ]
index a1893d0588fc573f9bbda5f4b273199b881a7978..d6c4e5cd8c7c10544d2ea7af206ec415570fab87 100644 (file)
@@ -99,7 +99,7 @@ func (s *_TypeSet) String() string {
 // ----------------------------------------------------------------------------
 // Implementation
 
-func (s *_TypeSet) hasTerms() bool              { return !s.terms.isAll() }
+func (s *_TypeSet) hasTerms() bool              { return !s.terms.isEmpty() && !s.terms.isAll() }
 func (s *_TypeSet) structuralType() Type        { return s.terms.structuralType() }
 func (s *_TypeSet) includes(t Type) bool        { return s.terms.includes(t) }
 func (s1 *_TypeSet) subsetOf(s2 *_TypeSet) bool { return s1.terms.subsetOf(s2.terms) }