diff --git a/src/go/types/NOTES b/src/go/types/NOTES index 4a8ea7068a..8613b12fac 100644 --- a/src/go/types/NOTES +++ b/src/go/types/NOTES @@ -5,3 +5,6 @@ TODO - implement contract embedding - interface embedding doesn't take care of literal type constraints yet (need an allTypes list, like we have an allMethods list?) + +OPEN ISSUES +- do we allow parenthesized generic uninstantiated types? diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go index 079a7c6676..a973126206 100644 --- a/src/go/types/assignments.go +++ b/src/go/types/assignments.go @@ -50,9 +50,9 @@ func (check *Checker) assignment(x *operand, T Type, context string) { } // x.typ is typed - // A generic (non-instantiated) value cannot be assigned to a variable. - if isGeneric(x.typ) { - check.errorf(x.pos(), "cannot use generic %s in %s", x, context) + // A generic (non-instantiated) function value cannot be assigned to a variable. + if sig, _ := x.typ.Underlying().(*Signature); sig != nil && len(sig.tparams) > 0 { + check.errorf(x.pos(), "cannot use generic function %s without instantiation in %s", x, context) } // spec: "If a left-hand side is the blank identifier, any typed or diff --git a/src/go/types/examples/types.go2 b/src/go/types/examples/types.go2 index 8df0a0415c..2172c9f346 100644 --- a/src/go/types/examples/types.go2 +++ b/src/go/types/examples/types.go2 @@ -18,7 +18,8 @@ var _ List(byte) = []byte{} // A generic binary tree might be declared as follows. type Tree(type E) struct { - left, right *Tree + // TODO(gri) should be able to use Tree w/o repeating type args + left, right *Tree(E) payload E } @@ -98,4 +99,10 @@ var xbool T(bool) // consider such types identical. Consequently: func _() { xint = xbool // ERROR assignment -} \ No newline at end of file +} + +// Parameterized types cannot be used without instantiation. +var _ T // ERROR cannot use generic type T + +// TODO(gri) Should we allow parentheses around generic, uninstantiated types? +// var _ (T)(int) diff --git a/src/go/types/operand.go b/src/go/types/operand.go index a39c33d32f..1f5c968ea9 100644 --- a/src/go/types/operand.go +++ b/src/go/types/operand.go @@ -158,7 +158,11 @@ func operandString(x *operand, qf Qualifier) string { // if hasType { if x.typ != Typ[Invalid] { - buf.WriteString(" of type ") + intro := " of type " + if isGeneric(x.typ) { + intro = " of generic type " + } + buf.WriteString(intro) WriteType(&buf, x.typ, qf) } else { buf.WriteString(" with invalid type") diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go index 6b6dbf7555..22d2a5c8cd 100644 --- a/src/go/types/predicates.go +++ b/src/go/types/predicates.go @@ -19,6 +19,13 @@ func isNamed(typ Type) bool { return ok } +// isGeneric reports whether a type is a generic, uninstantiated type +// (generic signatures are not included). +func isGeneric(typ Type) bool { + named, _ := typ.(*Named) + return named != nil && named.obj != nil && named.obj.IsParameterized() && named.targs == nil +} + func is(typ Type, what BasicInfo) bool { switch t := typ.Underlying().(type) { case *Basic: @@ -60,12 +67,6 @@ func IsInterface(typ Type) bool { return ok } -func isGeneric(typ Type) bool { - // TODO(gri) can a defined type ever be generic? - t, ok := typ.Underlying().(*Signature) - return ok && len(t.tparams) > 0 -} - // Comparable reports whether values of type T are comparable. func Comparable(T Type) bool { switch t := T.Underlying().(type) { diff --git a/src/go/types/testdata/contracts.go2 b/src/go/types/testdata/contracts.go2 index f8063df5c6..8fc8b3cc7c 100644 --- a/src/go/types/testdata/contracts.go2 +++ b/src/go/types/testdata/contracts.go2 @@ -182,6 +182,4 @@ func adderSum(type T Adder(T))(data []T) T { return s } -// TODO(gri) Report an error if we use parameterized interface type bound -// and we forget to pass the type argument. -func buggySig(type T Adder)(data []T) T +func _(type T Adder /* ERROR cannot use generic type Adder */)(data []T) T diff --git a/src/go/types/testdata/typeinst.go2 b/src/go/types/testdata/typeinst.go2 index 0b53241573..f9b0e852f9 100644 --- a/src/go/types/testdata/typeinst.go2 +++ b/src/go/types/testdata/typeinst.go2 @@ -25,8 +25,8 @@ type A1( /* ERROR cannot be parameterized */ type P) = P /* ERROR undeclared */ var x int type _ x /* ERROR not a type */ (int) -type _ int /* ERROR not a parametrized type */ () -type _ myInt /* ERROR not a parametrized type */ () +type _ int /* ERROR not a generic type */ () +type _ myInt /* ERROR not a generic type */ () // TODO(gri) better error messages type _ T1 /* ERROR got 0 arguments but 1 type parameters */ () diff --git a/src/go/types/testdata/typeparams.go2 b/src/go/types/testdata/typeparams.go2 index 62bfa25aae..83aa57c91e 100644 --- a/src/go/types/testdata/typeparams.go2 +++ b/src/go/types/testdata/typeparams.go2 @@ -21,7 +21,7 @@ func reverse(type T)(list []T) []T { return rlist } -var _ = reverse /* ERROR cannot use generic reverse */ +var _ = reverse /* ERROR cannot use generic function reverse */ var _ = reverse(int, float32 /* ERROR got 2 type arguments */ ) ([]int{1, 2, 3}) var _ = reverse(int)([ /* ERROR cannot use */ ]float32{1, 2, 3}) var f = reverse(chan int) @@ -52,7 +52,7 @@ func new(type T)() *T { return &x } -var _ = new /* ERROR cannot use generic new */ +var _ = new /* ERROR cannot use generic function new */ var _ *int = new(int)() func _(type T)(map[T /* ERROR invalid map key type */]int) // w/o contract we don't know if T is comparable diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index b8497bf5f3..174f74594f 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -116,6 +116,7 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool) } // typ type-checks the type expression e and returns its type, or Typ[Invalid]. +// The type must not be an (uninstantiated) generic type. func (check *Checker) typ(e ast.Expr) Type { return check.definedType(e, nil) } @@ -125,21 +126,28 @@ func (check *Checker) typ(e ast.Expr) Type { // in a type declaration, and def.underlying will be set to the type of e before // any components of e are type-checked. // -func (check *Checker) definedType(e ast.Expr, def *Named) (T Type) { - if check.conf.Trace { - check.trace(e.Pos(), "%s", e) - check.indent++ - defer func() { - check.indent-- - check.trace(e.Pos(), "=> %s", T) - }() +func (check *Checker) definedType(e ast.Expr, def *Named) Type { + typ := check.typInternal(e, def) + assert(isTyped(typ)) + if isGeneric(typ) { + check.errorf(e.Pos(), "cannot use generic type %s without instantiation", typ) + typ = Typ[Invalid] } + check.recordTypeAndValue(e, typexpr, typ, nil) + return typ +} - T = check.typInternal(e, def) - assert(isTyped(T)) - check.recordTypeAndValue(e, typexpr, T, nil) - - return +// generic us like typ bit the type must be an (uninstantiated) generic type. +func (check *Checker) genericType(e ast.Expr) Type { + typ := check.typInternal(e, nil) + assert(isTyped(typ)) + if typ != Typ[Invalid] && !isGeneric(typ) { + check.errorf(e.Pos(), "%s is not a generic type", typ) + typ = Typ[Invalid] + } + // TODO(gri) what is the correct call below? + check.recordTypeAndValue(e, typexpr, typ, nil) + return typ } // funcType type-checks a function or method type. @@ -208,9 +216,18 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast } // typInternal drives type checking of types. -// Must only be called by definedType. +// Must only be called by definedType or genericType. // -func (check *Checker) typInternal(e ast.Expr, def *Named) Type { +func (check *Checker) typInternal(e ast.Expr, def *Named) (T Type) { + if check.conf.Trace { + check.trace(e.Pos(), "expr %s", e) + check.indent++ + defer func() { + check.indent-- + check.trace(e.Pos(), "=> %s", T) + }() + } + switch e := e.(type) { case *ast.BadExpr: // ignore - error reported before @@ -250,19 +267,14 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type { } case *ast.CallExpr: - typ := check.typ(e.Fun) // TODO(gri) what about cycles? + typ := check.genericType(e.Fun) // TODO(gri) what about cycles? if typ == Typ[Invalid] { return typ // error already reported } - named, _ := typ.(*Named) - if named == nil || named.obj == nil || !named.obj.IsParameterized() || named.targs != nil { - check.errorf(e.Pos(), "%s is not a parametrized type", typ) - return Typ[Invalid] - } - // the number of supplied types must match the number of type parameters // TODO(gri) fold into code below - we want to eval args always + named, _ := typ.(*Named) // generic types are defined (= Named) types tname := named.obj if len(e.Args) != len(tname.tparams) { // TODO(gri) provide better error message @@ -309,6 +321,7 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type { return typ case *ast.ParenExpr: + // TODO(gri) should we allow parenthesized generic (uninstantiated) types? return check.definedType(e.X, def) case *ast.ArrayType: