From d2fce6bc64839d555a2c3211091589919eddae42 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 7 Jan 2020 21:55:00 -0800 Subject: [PATCH] go/types: implement len/cap for arguments of type paramater type Change-Id: Ieeaa41573e5ec10df46ab25ec0a3563e3ae5b32b --- src/go/types/NOTES | 2 ++ src/go/types/builtins.go | 25 +++++++++++++++++++++++++ src/go/types/testdata/typeparams.go2 | 22 ++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/src/go/types/NOTES b/src/go/types/NOTES index 7752b1f138..18a2c876fa 100644 --- a/src/go/types/NOTES +++ b/src/go/types/NOTES @@ -23,6 +23,8 @@ KNOWN ISSUES ---------------------------------------------------------------------------------------------------- OPEN QUESTIONS +- for len/cap(x) where x is of type parameter type and the bound contains arrays only, should the + result be a constant? (right now it is not) - confirm that it's ok to use inference in missingMethod to compare parameterized methods - now that we allow parenthesized embedded interfaces, should we allow parenthesized embedded fields? (probably yes, for symmetry and consistency). diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go index 2ff8142755..1285ff178b 100644 --- a/src/go/types/builtins.go +++ b/src/go/types/builtins.go @@ -174,6 +174,31 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b if id == _Len { mode = value } + + case *TypeParam: + if types := t.Interface().allTypes; len(types) > 0 { + mode = value // assume we're ok + L: + for _, t := range types { + switch t.(type) { + case *Basic: + if !isString(t) || id != _Len { + mode = invalid + break L + } + case *Array, *Slice, *Chan: + // ok + case *Map: + if id != _Len { + mode = invalid + break L + } + default: + mode = invalid + break L + } + } + } } if mode == invalid && typ != Typ[Invalid] { diff --git a/src/go/types/testdata/typeparams.go2 b/src/go/types/testdata/typeparams.go2 index 796d69506c..87c9ca7b11 100644 --- a/src/go/types/testdata/typeparams.go2 +++ b/src/go/types/testdata/typeparams.go2 @@ -79,6 +79,28 @@ func f3(type A, B, C)(A, struct{x B}, func(A, struct{x B}, *C)) int var _ = f3(int, rune, bool)(1, struct{x rune}{}, nil) +// len/cap built-ins + +func _(type T)(x T) { _ = len(x /* ERROR invalid argument */ ) } +func _(type T interface{ type int })(x T) { _ = len(x /* ERROR invalid argument */ ) } +func _(type T interface{ type string, []byte, int })(x T) { _ = len(x /* ERROR invalid argument */ ) } +func _(type T interface{ type string })(x T) { _ = len(x) } +func _(type T interface{ type [10]int })(x T) { _ = len(x) } +func _(type T interface{ type []byte })(x T) { _ = len(x) } +func _(type T interface{ type map[int]int })(x T) { _ = len(x) } +func _(type T interface{ type chan int })(x T) { _ = len(x) } +func _(type T interface{ type string, []byte, chan int })(x T) { _ = len(x) } + +func _(type T)(x T) { _ = cap(x /* ERROR invalid argument */ ) } +func _(type T interface{ type int })(x T) { _ = cap(x /* ERROR invalid argument */ ) } +func _(type T interface{ type string, []byte, int })(x T) { _ = cap(x /* ERROR invalid argument */ ) } +func _(type T interface{ type string })(x T) { _ = cap(x /* ERROR invalid argument */ ) } +func _(type T interface{ type [10]int })(x T) { _ = cap(x) } +func _(type T interface{ type []byte })(x T) { _ = cap(x) } +func _(type T interface{ type map[int]int })(x T) { _ = cap(x /* ERROR invalid argument */ ) } +func _(type T interface{ type chan int })(x T) { _ = cap(x) } +func _(type T interface{ type []byte, chan int })(x T) { _ = cap(x) } + // type inference checks var _ = new() /* ERROR cannot infer T */