aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/types2
diff options
context:
space:
mode:
authorMichael Matloob <matloob@golang.org>2021-08-26 15:04:40 -0400
committerMichael Matloob <matloob@golang.org>2021-08-26 15:04:42 -0400
commitde83ef67acaaf5c2ce12dd831e8d3d04f02a6fc9 (patch)
treede1e9db916cb810d3360137a7b5ead7c00f7bce5 /src/cmd/compile/internal/types2
parentde23549a3967ade982d848a5b6ae3cb3fa0dba45 (diff)
parent5e6a7e9b860d7c8f589eec3c123469ea8071689f (diff)
downloadgo-de83ef67acaaf5c2ce12dd831e8d3d04f02a6fc9.tar.gz
go-de83ef67acaaf5c2ce12dd831e8d3d04f02a6fc9.zip
[dev.cmdgo] all: merge master (5e6a7e9) into dev.cmdgo
Merge List: + 2021-08-26 5e6a7e9b86 embed: remove reference to global variables in docs + 2021-08-26 166b691b65 cmd/compile/internal/types2: remove need for instance (struct) + 2021-08-26 d6bdae33e9 cmd/compile/internal/types2: address some TODOs (cleanup) + 2021-08-26 770df2e18d crypto/tls: fix typo in PreferServerCipherSuites comment + 2021-08-26 a6ff433d6a cmd/go: pass -gcflags after other flags generated by the go command + 2021-08-25 4f2620285d cmd/compile/internal/types2: fix type set printing and add test + 2021-08-25 0ac64f6d70 cmd/compile/internal/types2: rename IsMethodSet to IsConstraint (cleanup) + 2021-08-25 4068fb6c21 cmd/compile: always accept 1.18 syntax but complain if not 1.18 + 2021-08-25 bf0bc4122f go/types, types2: don't re-evaluate context string for each function argument (optimization) + 2021-08-25 4158e88f64 cmd/compile/internal/syntax: fix position of type parameter field + 2021-08-25 647bef6c59 go/types: implement NewTypeList and use it instead of composite literals + 2021-08-25 6cf1d5d0fa cmd/compile: generic SSA rules for simplifying 2 and 3 operand integer arithmetic expressions + 2021-08-25 5baf60d472 bytes, strings: optimize Trim for single byte cutsets + 2021-08-25 3d667671ad cmd/compile: fix function contains no TParam in generic function + 2021-08-25 4f2ebfe34b cmd/compile: allow embed into any byte slice type + 2021-08-25 d2f002cb39 time/format: avoid growslice in time.String()/time.GoString() + 2021-08-25 08d4cc20ca cmd/compile: fix stencil call expression. + 2021-08-25 099b819085 cmd/compile: fix CheckSize() calculation for -G=3 and stencils + 2021-08-25 e1fcf8857e test: add test that caused gofrontend compiler crash + 2021-08-25 d37b8dedf7 test: add test case that gofrontend miscompiled + 2021-08-25 41b99dab0f os/user: don't skip TestLookupGroup if supported + 2021-08-25 de1c934b97 cmd/compile: fix checkptr false positive for (*[Big]T)(ptr)[:n:n] pattern + 2021-08-24 54cdef1f10 reflect: add MapIter.SetKey and MapIter.SetValue + 2021-08-24 5d863f89fe cmd/compile: simplify bad conversion check Change-Id: I29ab927f0e47f44d82f9307c642900f75f4f678f
Diffstat (limited to 'src/cmd/compile/internal/types2')
-rw-r--r--src/cmd/compile/internal/types2/call.go18
-rw-r--r--src/cmd/compile/internal/types2/check_test.go3
-rw-r--r--src/cmd/compile/internal/types2/decl.go23
-rw-r--r--src/cmd/compile/internal/types2/expr.go4
-rw-r--r--src/cmd/compile/internal/types2/hilbert_test.go3
-rw-r--r--src/cmd/compile/internal/types2/instantiate.go51
-rw-r--r--src/cmd/compile/internal/types2/interface.go2
-rw-r--r--src/cmd/compile/internal/types2/named.go20
-rw-r--r--src/cmd/compile/internal/types2/resolver.go40
-rw-r--r--src/cmd/compile/internal/types2/self_test.go7
-rw-r--r--src/cmd/compile/internal/types2/signature.go7
-rw-r--r--src/cmd/compile/internal/types2/stmt.go6
-rw-r--r--src/cmd/compile/internal/types2/subst.go2
-rw-r--r--src/cmd/compile/internal/types2/testdata/check/decls0.src2
-rw-r--r--src/cmd/compile/internal/types2/testdata/check/issues.src6
-rw-r--r--src/cmd/compile/internal/types2/testdata/check/main.go22
-rw-r--r--src/cmd/compile/internal/types2/testdata/check/tinference.go26
-rw-r--r--src/cmd/compile/internal/types2/testdata/check/typeparams.go24
-rw-r--r--src/cmd/compile/internal/types2/testdata/fixedbugs/issue47818.go259
-rw-r--r--src/cmd/compile/internal/types2/tuple.go2
-rw-r--r--src/cmd/compile/internal/types2/type.go1
-rw-r--r--src/cmd/compile/internal/types2/typeset.go29
-rw-r--r--src/cmd/compile/internal/types2/typeset_test.go67
-rw-r--r--src/cmd/compile/internal/types2/typestring.go3
-rw-r--r--src/cmd/compile/internal/types2/typexpr.go17
-rw-r--r--src/cmd/compile/internal/types2/unify.go3
26 files changed, 254 insertions, 133 deletions
diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go
index 538fdc0fb7..0b062b4c94 100644
--- a/src/cmd/compile/internal/types2/call.go
+++ b/src/cmd/compile/internal/types2/call.go
@@ -15,6 +15,10 @@ import (
// funcInst type-checks a function instantiation inst and returns the result in x.
// The operand x must be the evaluation of inst.X and its type must be a signature.
func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) {
+ if !check.allowVersion(check.pkg, 1, 18) {
+ check.softErrorf(inst.Pos(), "function instantiation requires go1.18 or later")
+ }
+
xlist := unpackExpr(inst.Index)
targs := check.typeList(xlist)
if targs == nil {
@@ -318,6 +322,13 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T
// infer type arguments and instantiate signature if necessary
if sig.TParams().Len() > 0 {
+ if !check.allowVersion(check.pkg, 1, 18) {
+ if iexpr, _ := call.Fun.(*syntax.IndexExpr); iexpr != nil {
+ check.softErrorf(iexpr.Pos(), "function instantiation requires go1.18 or later")
+ } else {
+ check.softErrorf(call.Pos(), "implicit function instantiation requires go1.18 or later")
+ }
+ }
// TODO(gri) provide position information for targs so we can feed
// it to the instantiate call for better error reporting
targs := check.infer(call.Pos(), sig.TParams().list(), targs, sigParams, args, true)
@@ -341,8 +352,11 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T
}
// check arguments
- for i, a := range args {
- check.assignment(a, sigParams.vars[i].typ, check.sprintf("argument to %s", call.Fun))
+ if len(args) > 0 {
+ context := check.sprintf("argument to %s", call.Fun)
+ for i, a := range args {
+ check.assignment(a, sigParams.vars[i].typ, context)
+ }
}
return
diff --git a/src/cmd/compile/internal/types2/check_test.go b/src/cmd/compile/internal/types2/check_test.go
index 41b0c54702..bc68e76407 100644
--- a/src/cmd/compile/internal/types2/check_test.go
+++ b/src/cmd/compile/internal/types2/check_test.go
@@ -20,9 +20,6 @@
// _ = x /* ERROR "not declared" */ + 1
// }
-// TODO(gri) Also collect strict mode errors of the form /* STRICT ... */
-// and test against strict mode.
-
package types2_test
import (
diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go
index 342e1090de..d7a33546aa 100644
--- a/src/cmd/compile/internal/types2/decl.go
+++ b/src/cmd/compile/internal/types2/decl.go
@@ -514,11 +514,26 @@ func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init syntax.Expr) {
check.initVars(lhs, []syntax.Expr{init}, nopos)
}
+// isImportedConstraint reports whether typ is an imported type constraint.
+func (check *Checker) isImportedConstraint(typ Type) bool {
+ named, _ := typ.(*Named)
+ if named == nil || named.obj.pkg == check.pkg || named.obj.pkg == nil {
+ return false
+ }
+ u, _ := named.under().(*Interface)
+ return u != nil && u.IsConstraint()
+}
+
func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named) {
assert(obj.typ == nil)
+ var rhs Type
check.later(func() {
check.validType(obj.typ, nil)
+ // If typ is local, an error was already reported where typ is specified/defined.
+ if check.isImportedConstraint(rhs) && !check.allowVersion(check.pkg, 1, 18) {
+ check.errorf(tdecl.Type.Pos(), "using type constraint %s requires go1.18 or later", rhs)
+ }
})
alias := tdecl.Alias
@@ -540,7 +555,8 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
}
obj.typ = Typ[Invalid]
- obj.typ = check.anyType(tdecl.Type)
+ rhs = check.anyType(tdecl.Type)
+ obj.typ = rhs
return
}
@@ -555,8 +571,9 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
}
// determine underlying type of named
- named.fromRHS = check.definedType(tdecl.Type, named)
- assert(named.fromRHS != nil)
+ rhs = check.definedType(tdecl.Type, named)
+ assert(rhs != nil)
+ named.fromRHS = rhs
// The underlying type of named may be itself a named type that is
// incomplete:
//
diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go
index d108093dac..86a8444ee2 100644
--- a/src/cmd/compile/internal/types2/expr.go
+++ b/src/cmd/compile/internal/types2/expr.go
@@ -127,9 +127,7 @@ func (check *Checker) overflow(x *operand) {
}
// opName returns the name of an operation, or the empty string.
-// For now, only operations that might overflow are handled.
-// TODO(gri) Expand this to a general mechanism giving names to
-// nodes?
+// Only operations that might overflow are handled.
func opName(e *syntax.Operation) string {
op := int(e.Op)
if e.Y == nil {
diff --git a/src/cmd/compile/internal/types2/hilbert_test.go b/src/cmd/compile/internal/types2/hilbert_test.go
index 9f9dad6b64..03fea4fe7c 100644
--- a/src/cmd/compile/internal/types2/hilbert_test.go
+++ b/src/cmd/compile/internal/types2/hilbert_test.go
@@ -29,8 +29,7 @@ func TestHilbert(t *testing.T) {
}
// parse source
- // TODO(gri) get rid of []bytes to string conversion below
- f, err := parseSrc("hilbert.go", string(src))
+ f, err := syntax.Parse(syntax.NewFileBase("hilbert.go"), bytes.NewReader(src), nil, nil, 0)
if err != nil {
t.Fatal(err)
}
diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go
index 8bea63ec86..f9cde24dfc 100644
--- a/src/cmd/compile/internal/types2/instantiate.go
+++ b/src/cmd/compile/internal/types2/instantiate.go
@@ -122,27 +122,26 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, posLis
// instance creates a type or function instance using the given original type
// typ and arguments targs. For Named types the resulting instance will be
// unexpanded.
-func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type) (res Type) {
- // TODO(gri) What is better here: work with TypeParams, or work with TypeNames?
+func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type) Type {
switch t := typ.(type) {
case *Named:
h := instantiatedHash(t, targs)
if check != nil {
- // typ may already have been instantiated with identical type arguments. In
- // that case, re-use the existing instance.
+ // typ may already have been instantiated with identical type arguments.
+ // In that case, re-use the existing instance.
if named := check.typMap[h]; named != nil {
return named
}
}
-
tname := NewTypeName(pos, t.obj.pkg, t.obj.name, nil)
named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is loaded
named.targs = NewTypeList(targs)
- named.instance = &instance{pos}
+ named.instPos = &pos
if check != nil {
check.typMap[h] = named
}
- res = named
+ return named
+
case *Signature:
tparams := t.TParams()
if !check.validateTArgLen(pos, tparams.Len(), len(targs)) {
@@ -151,30 +150,22 @@ func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type) (res Type
if tparams.Len() == 0 {
return typ // nothing to do (minor optimization)
}
- defer func() {
- // If we had an unexpected failure somewhere don't panic below when
- // asserting res.(*Signature). Check for *Signature in case Typ[Invalid]
- // is returned.
- if _, ok := res.(*Signature); !ok {
- return
- }
- // If the signature doesn't use its type parameters, subst
- // will not make a copy. In that case, make a copy now (so
- // we can set tparams to nil w/o causing side-effects).
- if t == res {
- copy := *t
- res = &copy
- }
- // After instantiating a generic signature, it is not generic
- // anymore; we need to set tparams to nil.
- res.(*Signature).tparams = nil
- }()
- res = check.subst(pos, typ, makeSubstMap(tparams.list(), targs), nil)
- default:
- // only types and functions can be generic
- panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ))
+ sig := check.subst(pos, typ, makeSubstMap(tparams.list(), targs), nil).(*Signature)
+ // If the signature doesn't use its type parameters, subst
+ // will not make a copy. In that case, make a copy now (so
+ // we can set tparams to nil w/o causing side-effects).
+ if sig == t {
+ copy := *sig
+ sig = &copy
+ }
+ // After instantiating a generic signature, it is not generic
+ // anymore; we need to set tparams to nil.
+ sig.tparams = nil
+ return sig
}
- return res
+
+ // only types and functions can be generic
+ panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ))
}
// validateTArgLen verifies that the length of targs and tparams matches,
diff --git a/src/cmd/compile/internal/types2/interface.go b/src/cmd/compile/internal/types2/interface.go
index ccd3de0a6e..e57158d2d5 100644
--- a/src/cmd/compile/internal/types2/interface.go
+++ b/src/cmd/compile/internal/types2/interface.go
@@ -98,7 +98,7 @@ func (t *Interface) Empty() bool { return t.typeSet().IsAll() }
func (t *Interface) IsComparable() bool { return t.typeSet().IsComparable() }
// IsConstraint reports whether interface t is not just a method set.
-func (t *Interface) IsConstraint() bool { return !t.typeSet().IsMethodSet() }
+func (t *Interface) IsConstraint() bool { return t.typeSet().IsConstraint() }
func (t *Interface) Underlying() Type { return t }
func (t *Interface) String() string { return TypeString(t, nil) }
diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go
index a3a2595a22..b4074aa3dc 100644
--- a/src/cmd/compile/internal/types2/named.go
+++ b/src/cmd/compile/internal/types2/named.go
@@ -9,8 +9,6 @@ import (
"sync"
)
-// TODO(gri) Clean up Named struct below; specifically the fromRHS field (can we use underlying?).
-
// A Named represents a named (defined) type.
type Named struct {
check *Checker
@@ -19,7 +17,7 @@ type Named struct {
orig *Named // original, uninstantiated type
fromRHS Type // type (on RHS of declaration) this *Named type is derived from (for cycle reporting)
underlying Type // possibly a *Named during setup; never a *Named once set up completely
- instance *instance // position information for lazy instantiation, or nil
+ instPos *syntax.Pos // position information for lazy instantiation, or nil
tparams *TParamList // type parameters, or nil
targs *TypeList // type arguments (after instantiation), or nil
methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily
@@ -242,24 +240,16 @@ func (n *Named) setUnderlying(typ Type) {
}
}
-// instance holds position information for use in lazy instantiation.
-//
-// TODO(rfindley): instance is probably unnecessary now. See if it can be
-// eliminated.
-type instance struct {
- pos syntax.Pos // position of type instantiation; for error reporting only
-}
-
// expand ensures that the underlying type of n is instantiated.
// The underlying type will be Typ[Invalid] if there was an error.
func (n *Named) expand(typMap map[string]*Named) *Named {
- if n.instance != nil {
+ if n.instPos != nil {
// n must be loaded before instantiation, in order to have accurate
// tparams. This is done implicitly by the call to n.TParams, but making it
// explicit is harmless: load is idempotent.
n.load()
var u Type
- if n.check.validateTArgLen(n.instance.pos, n.tparams.Len(), n.targs.Len()) {
+ if n.check.validateTArgLen(*n.instPos, n.tparams.Len(), n.targs.Len()) {
if typMap == nil {
if n.check != nil {
typMap = n.check.typMap
@@ -272,13 +262,13 @@ func (n *Named) expand(typMap map[string]*Named) *Named {
typMap = map[string]*Named{h: n}
}
}
- u = n.check.subst(n.instance.pos, n.orig.underlying, makeSubstMap(n.TParams().list(), n.targs.list()), typMap)
+ u = n.check.subst(*n.instPos, n.orig.underlying, makeSubstMap(n.TParams().list(), n.targs.list()), typMap)
} else {
u = Typ[Invalid]
}
n.underlying = u
n.fromRHS = u
- n.instance = nil
+ n.instPos = nil
}
return n
}
diff --git a/src/cmd/compile/internal/types2/resolver.go b/src/cmd/compile/internal/types2/resolver.go
index 018a20cfb2..34fbc3d41b 100644
--- a/src/cmd/compile/internal/types2/resolver.go
+++ b/src/cmd/compile/internal/types2/resolver.go
@@ -412,52 +412,60 @@ func (check *Checker) collectObjects() {
}
case *syntax.TypeDecl:
+ if len(s.TParamList) != 0 && !check.allowVersion(pkg, 1, 18) {
+ check.softErrorf(s.TParamList[0], "type parameters require go1.18 or later")
+ }
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Value, nil)
check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, tdecl: s})
case *syntax.FuncDecl:
- d := s // TODO(gri) get rid of this
- name := d.Name.Value
- obj := NewFunc(d.Name.Pos(), pkg, name, nil)
- if d.Recv == nil {
+ name := s.Name.Value
+ obj := NewFunc(s.Name.Pos(), pkg, name, nil)
+ hasTParamError := false // avoid duplicate type parameter errors
+ if s.Recv == nil {
// regular function
if name == "init" || name == "main" && pkg.name == "main" {
- if d.TParamList != nil {
- check.softErrorf(d, "func %s must have no type parameters", name)
+ if len(s.TParamList) != 0 {
+ check.softErrorf(s.TParamList[0], "func %s must have no type parameters", name)
+ hasTParamError = true
}
- if t := d.Type; len(t.ParamList) != 0 || len(t.ResultList) != 0 {
- check.softErrorf(d, "func %s must have no arguments and no return values", name)
+ if t := s.Type; len(t.ParamList) != 0 || len(t.ResultList) != 0 {
+ check.softErrorf(s, "func %s must have no arguments and no return values", name)
}
}
// don't declare init functions in the package scope - they are invisible
if name == "init" {
obj.parent = pkg.scope
- check.recordDef(d.Name, obj)
+ check.recordDef(s.Name, obj)
// init functions must have a body
- if d.Body == nil {
+ if s.Body == nil {
// TODO(gri) make this error message consistent with the others above
check.softErrorf(obj.pos, "missing function body")
}
} else {
- check.declare(pkg.scope, d.Name, obj, nopos)
+ check.declare(pkg.scope, s.Name, obj, nopos)
}
} else {
// method
// d.Recv != nil
- if !acceptMethodTypeParams && len(d.TParamList) != 0 {
+ if !acceptMethodTypeParams && len(s.TParamList) != 0 {
//check.error(d.TParamList.Pos(), invalidAST + "method must have no type parameters")
- check.error(d, invalidAST+"method must have no type parameters")
+ check.error(s.TParamList[0], invalidAST+"method must have no type parameters")
+ hasTParamError = true
}
- ptr, recv, _ := check.unpackRecv(d.Recv.Type, false)
+ ptr, recv, _ := check.unpackRecv(s.Recv.Type, false)
// (Methods with invalid receiver cannot be associated to a type, and
// methods with blank _ names are never found; no need to collect any
// of them. They will still be type-checked with all the other functions.)
if recv != nil && name != "_" {
methods = append(methods, methodInfo{obj, ptr, recv})
}
- check.recordDef(d.Name, obj)
+ check.recordDef(s.Name, obj)
+ }
+ if len(s.TParamList) != 0 && !check.allowVersion(pkg, 1, 18) && !hasTParamError {
+ check.softErrorf(s.TParamList[0], "type parameters require go1.18 or later")
}
- info := &declInfo{file: fileScope, fdecl: d}
+ info := &declInfo{file: fileScope, fdecl: s}
// Methods are not package-level objects but we still track them in the
// object map so that we can handle them like regular functions (if the
// receiver is invalid); also we need their fdecl info when associating
diff --git a/src/cmd/compile/internal/types2/self_test.go b/src/cmd/compile/internal/types2/self_test.go
index 4722fec988..e0d2e1b07a 100644
--- a/src/cmd/compile/internal/types2/self_test.go
+++ b/src/cmd/compile/internal/types2/self_test.go
@@ -24,12 +24,7 @@ func TestSelf(t *testing.T) {
conf := Config{Importer: defaultImporter()}
_, err = conf.Check("cmd/compile/internal/types2", files, nil)
if err != nil {
- // Importing go/constant doesn't work in the
- // build dashboard environment. Don't report an error
- // for now so that the build remains green.
- // TODO(gri) fix this
- t.Log(err) // replace w/ t.Fatal eventually
- return
+ t.Fatal(err)
}
}
diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go
index f1bf60ae8e..d28e7b8944 100644
--- a/src/cmd/compile/internal/types2/signature.go
+++ b/src/cmd/compile/internal/types2/signature.go
@@ -150,12 +150,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
// bound is (possibly) parameterized in the context of the
// receiver type declaration. Substitute parameters for the
// current context.
- // TODO(gri) should we assume now that bounds always exist?
- // (no bound == empty interface)
- if bound != nil {
- bound = check.subst(tpar.obj.pos, bound, smap, nil)
- tpar.bound = bound
- }
+ tpar.bound = check.subst(tpar.obj.pos, bound, smap, nil)
}
}
}
diff --git a/src/cmd/compile/internal/types2/stmt.go b/src/cmd/compile/internal/types2/stmt.go
index 7865c2d4f4..8cfdf92e67 100644
--- a/src/cmd/compile/internal/types2/stmt.go
+++ b/src/cmd/compile/internal/types2/stmt.go
@@ -52,11 +52,6 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body
check.error(body.Rbrace, "missing return")
}
- // TODO(gri) Should we make it an error to declare generic functions
- // where the type parameters are not used?
- // 12/19/2018: Probably not - it can make sense to have an API with
- // all functions uniformly sharing the same type parameters.
-
// spec: "Implementation restriction: A compiler may make it illegal to
// declare a variable inside a function body if the variable is never used."
check.usage(sig.scope)
@@ -422,7 +417,6 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
check.assignVar(lhs[0], &x)
case *syntax.CallStmt:
- // TODO(gri) get rid of this conversion to string
kind := "go"
if s.Tok == syntax.Defer {
kind = "defer"
diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go
index 918e5f3043..ff8dd13b6d 100644
--- a/src/cmd/compile/internal/types2/subst.go
+++ b/src/cmd/compile/internal/types2/subst.go
@@ -38,7 +38,7 @@ func (m substMap) lookup(tpar *TypeParam) Type {
// subst returns the type typ with its type parameters tpars replaced by the
// corresponding type arguments targs, recursively. subst doesn't modify the
// incoming type. If a substitution took place, the result type is different
-// from from the incoming type.
+// from the incoming type.
//
// If the given typMap is non-nil, it is used in lieu of check.typMap.
func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, typMap map[string]*Named) Type {
diff --git a/src/cmd/compile/internal/types2/testdata/check/decls0.src b/src/cmd/compile/internal/types2/testdata/check/decls0.src
index f051a4f2ac..09e5d5c5ad 100644
--- a/src/cmd/compile/internal/types2/testdata/check/decls0.src
+++ b/src/cmd/compile/internal/types2/testdata/check/decls0.src
@@ -146,7 +146,7 @@ type (
m1(I5)
}
I6 interface {
- S0 /* ERROR "not an interface" */
+ S0 /* ERROR "non-interface type S0" */
}
I7 interface {
I1
diff --git a/src/cmd/compile/internal/types2/testdata/check/issues.src b/src/cmd/compile/internal/types2/testdata/check/issues.src
index 692ed37ef4..d83a95af0e 100644
--- a/src/cmd/compile/internal/types2/testdata/check/issues.src
+++ b/src/cmd/compile/internal/types2/testdata/check/issues.src
@@ -79,11 +79,11 @@ func issue9473(a []int, b ...int) {
// Check that embedding a non-interface type in an interface results in a good error message.
func issue10979() {
type _ interface {
- int /* ERROR int is not an interface */
+ int /* ERROR non-interface type int */
}
type T struct{}
type _ interface {
- T /* ERROR T is not an interface */
+ T /* ERROR non-interface type T */
}
type _ interface {
nosuchtype /* ERROR undeclared name: nosuchtype */
@@ -280,7 +280,7 @@ type issue25301b /* ERROR cycle */ = interface {
}
type issue25301c interface {
- notE // ERROR struct\{\} is not an interface
+ notE // ERROR non-interface type struct\{\}
}
type notE = struct{}
diff --git a/src/cmd/compile/internal/types2/testdata/check/main.go2 b/src/cmd/compile/internal/types2/testdata/check/main.go2
index b7ddeaa1a8..395e3bfec8 100644
--- a/src/cmd/compile/internal/types2/testdata/check/main.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/main.go2
@@ -4,4 +4,4 @@
package main
-func /* ERROR "func main must have no type parameters" */ main[T any]() {}
+func main [T /* ERROR "func main must have no type parameters" */ any]() {}
diff --git a/src/cmd/compile/internal/types2/testdata/check/tinference.go2 b/src/cmd/compile/internal/types2/testdata/check/tinference.go2
index 0afb77c1e4..2409fef4ae 100644
--- a/src/cmd/compile/internal/types2/testdata/check/tinference.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/tinference.go2
@@ -15,7 +15,7 @@ type any interface{}
// f("a", "b", "c", "d")
// f0("a", "b", "c", "d")
// }
-//
+//
// func f1[A any, B interface{~A}](A, B)
// func _() {
// f := f1[int]
@@ -60,9 +60,7 @@ func _() {
var _ string = x
}
-// TODO(gri) Need to flag invalid recursive constraints. At the
-// moment these cause infinite recursions and stack overflow.
-// func f7[A interface{type B}, B interface{~A}]()
+func f7[A interface{*B}, B interface{~*A}]() {}
// More realistic examples
diff --git a/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 b/src/cmd/compile/internal/types2/testdata/check/typeparams.go2
index 1ad80b1e1b..765d561f3b 100644
--- a/src/cmd/compile/internal/types2/testdata/check/typeparams.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/typeparams.go2
@@ -304,8 +304,8 @@ var _ = f8[int, float64](0, 0, nil...) // test case for #18268
// init functions cannot have type parameters
func init() {}
-func init[/* ERROR func init must have no type parameters */ _ any]() {}
-func init[/* ERROR func init must have no type parameters */ P any]() {}
+func init[_ /* ERROR func init must have no type parameters */ any]() {}
+func init[P /* ERROR func init must have no type parameters */ any]() {}
type T struct {}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47818.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47818.go2
new file mode 100644
index 0000000000..5334695b5e
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47818.go2
@@ -0,0 +1,59 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Parser accepts type parameters but the type checker
+// needs to report any operations that are not permitted
+// before Go 1.18.
+
+package go1_17
+
+type T[P /* ERROR type parameters require go1\.18 or later */ any] struct{}
+
+// for init (and main, but we're not in package main) we should only get one error
+func init[P /* ERROR func init must have no type parameters */ any]() {}
+func main[P /* ERROR type parameters require go1\.18 or later */ any]() {}
+
+func f[P /* ERROR type parameters require go1\.18 or later */ any](x P) {
+ var _ T[ /* ERROR type instantiation requires go1\.18 or later */ int]
+ var _ (T[ /* ERROR type instantiation requires go1\.18 or later */ int])
+ _ = T[ /* ERROR type instantiation requires go1\.18 or later */ int]{}
+ _ = T[ /* ERROR type instantiation requires go1\.18 or later */ int](struct{}{})
+}
+
+func (T[ /* ERROR type instantiation requires go1\.18 or later */ P]) g(x int) {
+ f[ /* ERROR function instantiation requires go1\.18 or later */ int](0) // explicit instantiation
+ (f[ /* ERROR function instantiation requires go1\.18 or later */ int])(0) // parentheses (different code path)
+ f( /* ERROR implicit function instantiation requires go1\.18 or later */ x) // implicit instantiation
+}
+
+type C1 interface {
+ comparable // ERROR undeclared name: comparable \(requires version go1\.18 or later\)
+}
+
+type C2 interface {
+ comparable // ERROR undeclared name: comparable \(requires version go1\.18 or later\)
+ int // ERROR embedding non-interface type int requires go1\.18 or later
+ ~ /* ERROR embedding interface element ~int requires go1\.18 or later */ int
+ int /* ERROR embedding interface element int\|~string requires go1\.18 or later */ | ~string
+}
+
+type _ interface {
+ // errors for these were reported with their declaration
+ C1
+ C2
+}
+
+type (
+ _ comparable // ERROR undeclared name: comparable \(requires version go1\.18 or later\)
+ // errors for these were reported with their declaration
+ _ C1
+ _ C2
+
+ _ = comparable // ERROR undeclared name: comparable \(requires version go1\.18 or later\)
+ // errors for these were reported with their declaration
+ _ = C1
+ _ = C2
+)
+
+// TODO(gri) need test cases for imported constraint types (see also issue #47967) \ No newline at end of file
diff --git a/src/cmd/compile/internal/types2/tuple.go b/src/cmd/compile/internal/types2/tuple.go
index a3946beab5..1356aae0b0 100644
--- a/src/cmd/compile/internal/types2/tuple.go
+++ b/src/cmd/compile/internal/types2/tuple.go
@@ -16,8 +16,6 @@ func NewTuple(x ...*Var) *Tuple {
if len(x) > 0 {
return &Tuple{vars: x}
}
- // TODO(gri) Don't represent empty tuples with a (*Tuple)(nil) pointer;
- // it's too subtle and causes problems.
return nil
}
diff --git a/src/cmd/compile/internal/types2/type.go b/src/cmd/compile/internal/types2/type.go
index 4b8642aa96..ca5ecdc434 100644
--- a/src/cmd/compile/internal/types2/type.go
+++ b/src/cmd/compile/internal/types2/type.go
@@ -34,7 +34,6 @@ func (t *top) String() string { return TypeString(t, nil) }
// under must only be called when a type is known
// to be fully set up.
func under(t Type) Type {
- // TODO(gri) is this correct for *Union?
if n := asNamed(t); n != nil {
return n.under()
}
diff --git a/src/cmd/compile/internal/types2/typeset.go b/src/cmd/compile/internal/types2/typeset.go
index 14596b68a3..ae39f26e4f 100644
--- a/src/cmd/compile/internal/types2/typeset.go
+++ b/src/cmd/compile/internal/types2/typeset.go
@@ -30,10 +30,8 @@ func (s *_TypeSet) IsAll() bool {
return !s.comparable && len(s.methods) == 0 && s.terms.isAll()
}
-// TODO(gri) IsMethodSet is not a great name for this predicate. Find a better one.
-
-// IsMethodSet reports whether the type set s is described by a single set of methods.
-func (s *_TypeSet) IsMethodSet() bool { return !s.comparable && s.terms.isAll() }
+// IsConstraint reports whether type set s is not just a set of methods.
+func (s *_TypeSet) IsConstraint() bool { return s.comparable || !s.terms.isAll() }
// IsComparable reports whether each type in the set is comparable.
func (s *_TypeSet) IsComparable() bool {
@@ -79,26 +77,24 @@ func (s *_TypeSet) String() string {
var buf bytes.Buffer
buf.WriteByte('{')
if s.comparable {
- buf.WriteString(" comparable")
+ buf.WriteString("comparable")
if hasMethods || hasTerms {
- buf.WriteByte(';')
+ buf.WriteString("; ")
}
}
for i, m := range s.methods {
if i > 0 {
- buf.WriteByte(';')
+ buf.WriteString("; ")
}
- buf.WriteByte(' ')
buf.WriteString(m.String())
}
if hasMethods && hasTerms {
- buf.WriteByte(';')
+ buf.WriteString("; ")
}
if hasTerms {
buf.WriteString(s.terms.String())
}
- buf.WriteString(" }") // there was at least one method or term
-
+ buf.WriteString("}")
return buf.String()
}
@@ -271,6 +267,11 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
switch u := under(typ).(type) {
case *Interface:
tset := computeInterfaceTypeSet(check, pos, u)
+ // If typ is local, an error was already reported where typ is specified/defined.
+ if check != nil && check.isImportedConstraint(typ) && !check.allowVersion(check.pkg, 1, 18) {
+ check.errorf(pos, "embedding constraint interface %s requires go1.18 or later", typ)
+ continue
+ }
if tset.comparable {
ityp.tset.comparable = true
}
@@ -279,6 +280,10 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
}
terms = tset.terms
case *Union:
+ if check != nil && !check.allowVersion(check.pkg, 1, 18) {
+ check.errorf(pos, "embedding interface element %s requires go1.18 or later", u)
+ continue
+ }
tset := computeUnionTypeSet(check, pos, u)
if tset == &invalidTypeSet {
continue // ignore invalid unions
@@ -293,7 +298,7 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
continue
}
if check != nil && !check.allowVersion(check.pkg, 1, 18) {
- check.errorf(pos, "%s is not an interface", typ)
+ check.errorf(pos, "embedding non-interface type %s requires go1.18 or later", typ)
continue
}
terms = termlist{{false, typ}}
diff --git a/src/cmd/compile/internal/types2/typeset_test.go b/src/cmd/compile/internal/types2/typeset_test.go
index 0e14d523c8..7f7cc06db9 100644
--- a/src/cmd/compile/internal/types2/typeset_test.go
+++ b/src/cmd/compile/internal/types2/typeset_test.go
@@ -4,7 +4,11 @@
package types2
-import "testing"
+import (
+ "cmd/compile/internal/syntax"
+ "strings"
+ "testing"
+)
func TestInvalidTypeSet(t *testing.T) {
if !invalidTypeSet.IsEmpty() {
@@ -12,4 +16,65 @@ func TestInvalidTypeSet(t *testing.T) {
}
}
+func TestTypeSetString(t *testing.T) {
+ for body, want := range map[string]string{
+ "{}": "𝓤",
+ "{int}": "{int}",
+ "{~int}": "{~int}",
+ "{int|string}": "{int ∪ string}",
+ "{int; string}": "∅",
+
+ "{comparable}": "{comparable}",
+ "{comparable; int}": "{comparable; int}",
+ "{~int; comparable}": "{comparable; ~int}",
+ "{int|string; comparable}": "{comparable; int ∪ string}",
+ "{comparable; int; string}": "∅",
+
+ "{m()}": "{func (p.T).m()}",
+ "{m1(); m2() int }": "{func (p.T).m1(); func (p.T).m2() int}",
+ "{error}": "{func (error).Error() string}",
+ "{m(); comparable}": "{comparable; func (p.T).m()}",
+ "{m1(); comparable; m2() int }": "{comparable; func (p.T).m1(); func (p.T).m2() int}",
+ "{comparable; error}": "{comparable; func (error).Error() string}",
+
+ "{m(); comparable; int|float32|string}": "{comparable; func (p.T).m(); int ∪ float32 ∪ string}",
+ "{m1(); int; m2(); comparable }": "{comparable; func (p.T).m1(); func (p.T).m2(); int}",
+
+ "{E}; type E interface{}": "𝓤",
+ "{E}; type E interface{int;string}": "∅",
+ "{E}; type E interface{comparable}": "{comparable}",
+ } {
+ // parse
+ errh := func(error) {} // dummy error handler so that parsing continues in presence of errors
+ src := "package p; type T interface" + body
+ file, err := syntax.Parse(nil, strings.NewReader(src), errh, nil, syntax.AllowGenerics)
+ if err != nil {
+ t.Fatalf("%s: %v (invalid test case)", body, err)
+ }
+
+ // type check
+ var conf Config
+ pkg, err := conf.Check(file.PkgName.Value, []*syntax.File{file}, nil)
+ if err != nil {
+ t.Fatalf("%s: %v (invalid test case)", body, err)
+ }
+
+ // lookup T
+ obj := pkg.scope.Lookup("T")
+ if obj == nil {
+ t.Fatalf("%s: T not found (invalid test case)", body)
+ }
+ T, ok := under(obj.Type()).(*Interface)
+ if !ok {
+ t.Fatalf("%s: %v is not an interface (invalid test case)", body, obj)
+ }
+
+ // verify test case
+ got := T.typeSet().String()
+ if got != want {
+ t.Errorf("%s: got %s; want %s", body, got, want)
+ }
+ }
+}
+
// TODO(gri) add more tests
diff --git a/src/cmd/compile/internal/types2/typestring.go b/src/cmd/compile/internal/types2/typestring.go
index 2c34d036db..1775fc6677 100644
--- a/src/cmd/compile/internal/types2/typestring.go
+++ b/src/cmd/compile/internal/types2/typestring.go
@@ -195,7 +195,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
}
case *Named:
- if t.instance != nil {
+ if t.instPos != nil {
buf.WriteByte(instanceMarker)
}
writeTypeName(buf, t.obj, qf)
@@ -279,6 +279,7 @@ func writeTParamList(buf *bytes.Buffer, list []*TypeParam, qf Qualifier, visited
func writeTypeName(buf *bytes.Buffer, obj *TypeName, qf Qualifier) {
if obj == nil {
+ assert(instanceHashing == 0) // we need an object for instance hashing
buf.WriteString("<Named w/o object>")
return
}
diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go
index 241c6d35fe..6938648bbc 100644
--- a/src/cmd/compile/internal/types2/typexpr.go
+++ b/src/cmd/compile/internal/types2/typexpr.go
@@ -38,14 +38,12 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *Named, wantType boo
}
return
case universeAny, universeComparable:
+ // complain if necessary but keep going
if !check.allowVersion(check.pkg, 1, 18) {
- check.errorf(e, "undeclared name: %s (requires version go1.18 or later)", e.Value)
- return
- }
- // If we allow "any" for general use, this if-statement can be removed (issue #33232).
- if obj == universeAny {
- check.error(e, "cannot use any outside constraint position")
- return
+ check.softErrorf(e, "undeclared name: %s (requires version go1.18 or later)", e.Value)
+ } else if obj == universeAny {
+ // If we allow "any" for general use, this if-statement can be removed (issue #33232).
+ check.softErrorf(e, "cannot use any outside constraint position")
}
}
check.recordUse(e, obj)
@@ -153,7 +151,7 @@ func (check *Checker) ordinaryType(pos syntax.Pos, typ Type) {
check.later(func() {
if t := asInterface(typ); t != nil {
tset := computeInterfaceTypeSet(check, pos, t) // TODO(gri) is this the correct position?
- if !tset.IsMethodSet() {
+ if tset.IsConstraint() {
if tset.comparable {
check.softErrorf(pos, "interface is (or embeds) comparable")
} else {
@@ -274,6 +272,9 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) {
}
case *syntax.IndexExpr:
+ if !check.allowVersion(check.pkg, 1, 18) {
+ check.softErrorf(e.Pos(), "type instantiation requires go1.18 or later")
+ }
return check.instantiatedType(e.X, unpackExpr(e.Index), def)
case *syntax.ParenExpr:
diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go
index d4fbebc11b..72542e7d2e 100644
--- a/src/cmd/compile/internal/types2/unify.go
+++ b/src/cmd/compile/internal/types2/unify.go
@@ -433,9 +433,6 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
xargs := x.targs.list()
yargs := y.targs.list()
- // TODO(gri) This is not always correct: two types may have the same names
- // in the same package if one of them is nested in a function.
- // Extremely unlikely but we need an always correct solution.
if x.obj.pkg == y.obj.pkg && x.obj.name == y.obj.name {
assert(len(xargs) == len(yargs))
for i, x := range xargs {