aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/go')
-rw-r--r--libgo/go/go/ast/ast.go47
-rw-r--r--libgo/go/go/ast/ast_notypeparams.go28
-rw-r--r--libgo/go/go/ast/ast_typeparams.go51
-rw-r--r--libgo/go/go/ast/print.go22
-rw-r--r--libgo/go/go/ast/print_test.go2
-rw-r--r--libgo/go/go/ast/resolve.go2
-rw-r--r--libgo/go/go/ast/scope.go8
-rw-r--r--libgo/go/go/ast/walk.go18
-rw-r--r--libgo/go/go/ast/walk_notypeparams.go17
-rw-r--r--libgo/go/go/ast/walk_typeparams.go36
-rw-r--r--libgo/go/go/build/build.go28
-rw-r--r--libgo/go/go/build/build_test.go2
-rw-r--r--libgo/go/go/build/deps_test.go51
-rw-r--r--libgo/go/go/build/gc.go1
-rw-r--r--libgo/go/go/build/gccgo.go1
-rw-r--r--libgo/go/go/build/read.go29
-rw-r--r--libgo/go/go/build/read_test.go21
-rw-r--r--libgo/go/go/build/syslist.go2
-rw-r--r--libgo/go/go/constant/kind_string.go28
-rw-r--r--libgo/go/go/constant/value.go6
-rw-r--r--libgo/go/go/constant/value_test.go16
-rw-r--r--libgo/go/go/doc/comment.go16
-rw-r--r--libgo/go/go/doc/doc.go2
-rw-r--r--libgo/go/go/doc/doc_test.go235
-rw-r--r--libgo/go/go/doc/example.go16
-rw-r--r--libgo/go/go/doc/example_test.go6
-rw-r--r--libgo/go/go/doc/exports.go58
-rw-r--r--libgo/go/go/doc/filter.go2
-rw-r--r--libgo/go/go/doc/headscan.go18
-rw-r--r--libgo/go/go/doc/reader.go80
-rw-r--r--libgo/go/go/doc/testdata/benchmark.go4
-rw-r--r--libgo/go/go/doc/testdata/generics.0.golden76
-rw-r--r--libgo/go/go/doc/testdata/generics.1.golden66
-rw-r--r--libgo/go/go/doc/testdata/generics.2.golden76
-rw-r--r--libgo/go/go/doc/testdata/generics.go74
-rw-r--r--libgo/go/go/doc/testdata/testing.0.golden24
-rw-r--r--libgo/go/go/doc/testdata/testing.1.golden40
-rw-r--r--libgo/go/go/doc/testdata/testing.2.golden24
-rw-r--r--libgo/go/go/doc/testdata/testing.go20
-rw-r--r--libgo/go/go/format/format.go2
-rw-r--r--libgo/go/go/importer/importer_test.go3
-rw-r--r--libgo/go/go/internal/gccgoimporter/parser.go36
-rw-r--r--libgo/go/go/internal/gccgoimporter/testdata/escapeinfo.goxbin824 -> 350 bytes
-rw-r--r--libgo/go/go/internal/gccgoimporter/testdata/time.goxbin7977 -> 7499 bytes
-rw-r--r--libgo/go/go/internal/gccgoimporter/testdata/unicode.goxbin7945 -> 7474 bytes
-rw-r--r--libgo/go/go/internal/gccgoimporter/testdata/v1reflect.goxbin10872 -> 10546 bytes
-rw-r--r--libgo/go/go/internal/gcimporter/gcimporter_test.go169
-rw-r--r--libgo/go/go/internal/gcimporter/iimport.go225
-rw-r--r--libgo/go/go/internal/gcimporter/support.go56
-rw-r--r--libgo/go/go/internal/gcimporter/testdata/exports.go25
-rw-r--r--libgo/go/go/internal/gcimporter/testdata/generics.go29
-rw-r--r--libgo/go/go/internal/typeparams/common.go10
-rw-r--r--libgo/go/go/internal/typeparams/notypeparams.go40
-rw-r--r--libgo/go/go/internal/typeparams/typeparams.go83
-rw-r--r--libgo/go/go/parser/error_test.go24
-rw-r--r--libgo/go/go/parser/interface.go6
-rw-r--r--libgo/go/go/parser/parser.go343
-rw-r--r--libgo/go/go/parser/resolver.go33
-rw-r--r--libgo/go/go/parser/resolver_test.go6
-rw-r--r--libgo/go/go/parser/short_test.go19
-rw-r--r--libgo/go/go/parser/testdata/linalg.go216
-rw-r--r--libgo/go/go/parser/testdata/resolution/typeparams.go24
-rw-r--r--libgo/go/go/printer/nodes.go65
-rw-r--r--libgo/go/go/printer/printer.go27
-rw-r--r--libgo/go/go/printer/printer_test.go4
-rw-r--r--libgo/go/go/printer/testdata/generics.golden11
-rw-r--r--libgo/go/go/printer/testdata/generics.input9
-rw-r--r--libgo/go/go/printer/testdata/parser.go4
-rw-r--r--libgo/go/go/scanner/scanner.go4
-rw-r--r--libgo/go/go/scanner/scanner_test.go3
-rw-r--r--libgo/go/go/token/position.go2
-rw-r--r--libgo/go/go/token/serialize.go4
-rw-r--r--libgo/go/go/token/serialize_test.go4
-rw-r--r--libgo/go/go/token/token.go11
-rw-r--r--libgo/go/go/types/api.go152
-rw-r--r--libgo/go/go/types/api_notypeparams.go104
-rw-r--r--libgo/go/go/types/api_test.go718
-rw-r--r--libgo/go/go/types/api_typeparams.go53
-rw-r--r--libgo/go/go/types/api_typeparams_test.go139
-rw-r--r--libgo/go/go/types/array.go25
-rw-r--r--libgo/go/go/types/assignments.go138
-rw-r--r--libgo/go/go/types/basic.go82
-rw-r--r--libgo/go/go/types/builtins.go264
-rw-r--r--libgo/go/go/types/builtins_test.go7
-rw-r--r--libgo/go/go/types/call.go318
-rw-r--r--libgo/go/go/types/chan.go35
-rw-r--r--libgo/go/go/types/check.go189
-rw-r--r--libgo/go/go/types/check_test.go141
-rw-r--r--libgo/go/go/types/context.go123
-rw-r--r--libgo/go/go/types/context_test.go70
-rw-r--r--libgo/go/go/types/conversions.go206
-rw-r--r--libgo/go/go/types/decl.go468
-rw-r--r--libgo/go/go/types/errorcodes.go115
-rw-r--r--libgo/go/go/types/errorcodes_test.go6
-rw-r--r--libgo/go/go/types/errors.go60
-rw-r--r--libgo/go/go/types/errors_test.go1
-rw-r--r--libgo/go/go/types/eval.go11
-rw-r--r--libgo/go/go/types/eval_test.go10
-rw-r--r--libgo/go/go/types/example_test.go1
-rw-r--r--libgo/go/go/types/expr.go324
-rw-r--r--libgo/go/go/types/exprstring.go36
-rw-r--r--libgo/go/go/types/exprstring_test.go27
-rw-r--r--libgo/go/go/types/gotype.go3
-rw-r--r--libgo/go/go/types/hilbert_test.go2
-rw-r--r--libgo/go/go/types/index.go207
-rw-r--r--libgo/go/go/types/infer.go330
-rw-r--r--libgo/go/go/types/initorder.go66
-rw-r--r--libgo/go/go/types/instantiate.go275
-rw-r--r--libgo/go/go/types/instantiate_test.go253
-rw-r--r--libgo/go/go/types/interface.go225
-rw-r--r--libgo/go/go/types/issues_test.go33
-rw-r--r--libgo/go/go/types/labels.go3
-rw-r--r--libgo/go/go/types/lookup.go270
-rw-r--r--libgo/go/go/types/map.go24
-rw-r--r--libgo/go/go/types/methodlist.go78
-rw-r--r--libgo/go/go/types/methodlist_test.go41
-rw-r--r--libgo/go/go/types/methodset.go75
-rw-r--r--libgo/go/go/types/methodset_test.go20
-rw-r--r--libgo/go/go/types/mono.go336
-rw-r--r--libgo/go/go/types/mono_test.go97
-rw-r--r--libgo/go/go/types/named.go391
-rw-r--r--libgo/go/go/types/object.go71
-rw-r--r--libgo/go/go/types/object_test.go106
-rw-r--r--libgo/go/go/types/operand.go141
-rw-r--r--libgo/go/go/types/pointer.go19
-rw-r--r--libgo/go/go/types/predicates.go372
-rw-r--r--libgo/go/go/types/resolver.go81
-rw-r--r--libgo/go/go/types/sanitize.go206
-rw-r--r--libgo/go/go/types/scope.go97
-rw-r--r--libgo/go/go/types/self_test.go8
-rw-r--r--libgo/go/go/types/signature.go320
-rw-r--r--libgo/go/go/types/sizeof_test.go23
-rw-r--r--libgo/go/go/types/sizes.go18
-rw-r--r--libgo/go/go/types/slice.go19
-rw-r--r--libgo/go/go/types/stdlib_test.go13
-rw-r--r--libgo/go/go/types/stmt.go285
-rw-r--r--libgo/go/go/types/struct.go217
-rw-r--r--libgo/go/go/types/subst.go480
-rw-r--r--libgo/go/go/types/termlist.go167
-rw-r--r--libgo/go/go/types/termlist_test.go313
-rw-r--r--libgo/go/go/types/testdata/check/builtins.go2277
-rw-r--r--libgo/go/go/types/testdata/check/builtins.src2
-rw-r--r--libgo/go/go/types/testdata/check/const0.src2
-rw-r--r--libgo/go/go/types/testdata/check/cycles.src1
-rw-r--r--libgo/go/go/types/testdata/check/cycles4.src15
-rw-r--r--libgo/go/go/types/testdata/check/cycles5.src4
-rw-r--r--libgo/go/go/types/testdata/check/decls0.src12
-rw-r--r--libgo/go/go/types/testdata/check/decls1.src2
-rw-r--r--libgo/go/go/types/testdata/check/errors.src28
-rw-r--r--libgo/go/go/types/testdata/check/expr1.src4
-rw-r--r--libgo/go/go/types/testdata/check/expr2.src6
-rw-r--r--libgo/go/go/types/testdata/check/expr3.src40
-rw-r--r--libgo/go/go/types/testdata/check/issues.go278
-rw-r--r--libgo/go/go/types/testdata/check/issues.src12
-rw-r--r--libgo/go/go/types/testdata/check/linalg.go277
-rw-r--r--libgo/go/go/types/testdata/check/main.go22
-rw-r--r--libgo/go/go/types/testdata/check/map2.go24
-rw-r--r--libgo/go/go/types/testdata/check/methodsets.src12
-rw-r--r--libgo/go/go/types/testdata/check/shifts.src2
-rw-r--r--libgo/go/go/types/testdata/check/stmt0.src48
-rw-r--r--libgo/go/go/types/testdata/check/tinference.go2108
-rw-r--r--libgo/go/go/types/testdata/check/tmp.go217
-rw-r--r--libgo/go/go/types/testdata/check/typeinst.go221
-rw-r--r--libgo/go/go/types/testdata/check/typeinst2.go2136
-rw-r--r--libgo/go/go/types/testdata/check/typeparams.go2309
-rw-r--r--libgo/go/go/types/testdata/check/vardecl.src6
-rw-r--r--libgo/go/go/types/testdata/examples/functions.go230
-rw-r--r--libgo/go/go/types/testdata/examples/inference.go235
-rw-r--r--libgo/go/go/types/testdata/examples/methods.go216
-rw-r--r--libgo/go/go/types/testdata/examples/types.go2109
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue25838.go26
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue28251.src2
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue39634.go227
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue39680.go28
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue39693.go217
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue39699.go22
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue39711.go26
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue39723.go22
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue39725.go24
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue39754.go27
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue39755.go24
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue39768.go219
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue39938.go236
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue39948.go22
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue39976.go22
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue40038.go24
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue40056.go22
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue40301.go24
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue40684.go26
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue41124.go222
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue42758.go24
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue45114.go8
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue45548.go22
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue45635.go27
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue45985.go25
-rw-r--r--libgo/go/go/types/testdata/fixedbugs/issue49003.go10
-rw-r--r--libgo/go/go/types/tuple.go34
-rw-r--r--libgo/go/go/types/type.go1024
-rw-r--r--libgo/go/go/types/typelists.go69
-rw-r--r--libgo/go/go/types/typeparam.go163
-rw-r--r--libgo/go/go/types/types_test.go19
-rw-r--r--libgo/go/go/types/typeset.go404
-rw-r--r--libgo/go/go/types/typeset_test.go81
-rw-r--r--libgo/go/go/types/typestring.go501
-rw-r--r--libgo/go/go/types/typestring_test.go70
-rw-r--r--libgo/go/go/types/typeterm.go166
-rw-r--r--libgo/go/go/types/typeterm_test.go240
-rw-r--r--libgo/go/go/types/typexpr.go973
-rw-r--r--libgo/go/go/types/unify.go142
-rw-r--r--libgo/go/go/types/union.go189
-rw-r--r--libgo/go/go/types/universe.go103
-rw-r--r--libgo/go/go/types/validtype.go147
212 files changed, 11813 insertions, 6203 deletions
diff --git a/libgo/go/go/ast/ast.go b/libgo/go/go/ast/ast.go
index 337c87f..a74a827 100644
--- a/libgo/go/go/ast/ast.go
+++ b/libgo/go/go/ast/ast.go
@@ -193,14 +193,10 @@ func isDirective(c string) bool {
// in a signature.
// Field.Names is nil for unnamed parameters (parameter lists which only contain types)
// and embedded struct fields. In the latter case, the field name is the type name.
-// Field.Names contains a single name "type" for elements of interface type lists.
-// Types belonging to the same type list share the same "type" identifier which also
-// records the position of that keyword.
-//
type Field struct {
Doc *CommentGroup // associated documentation; or nil
- Names []*Ident // field/method/(type) parameter names, or type "type"; or nil
- Type Expr // field/method/parameter type, type list type; or nil
+ Names []*Ident // field/method/(type) parameter names; or nil
+ Type Expr // field/method/parameter type; or nil
Tag *BasicLit // field tag; or nil
Comment *CommentGroup // line comments; or nil
}
@@ -228,11 +224,12 @@ func (f *Field) End() token.Pos {
return token.NoPos
}
-// A FieldList represents a list of Fields, enclosed by parentheses or braces.
+// A FieldList represents a list of Fields, enclosed by parentheses,
+// curly braces, or square brackets.
type FieldList struct {
- Opening token.Pos // position of opening parenthesis/brace, if any
+ Opening token.Pos // position of opening parenthesis/brace/bracket, if any
List []*Field // field list; or nil
- Closing token.Pos // position of closing parenthesis/brace, if any
+ Closing token.Pos // position of closing parenthesis/brace/bracket, if any
}
func (f *FieldList) Pos() token.Pos {
@@ -344,6 +341,15 @@ type (
Rbrack token.Pos // position of "]"
}
+ // An IndexListExpr node represents an expression followed by multiple
+ // indices.
+ IndexListExpr struct {
+ X Expr // expression
+ Lbrack token.Pos // position of "["
+ Indices []Expr // index expressions
+ Rbrack token.Pos // position of "]"
+ }
+
// A SliceExpr node represents an expression followed by slice indices.
SliceExpr struct {
X Expr // expression
@@ -440,6 +446,14 @@ type (
// Pointer types are represented via StarExpr nodes.
+ // A FuncType node represents a function type.
+ FuncType struct {
+ Func token.Pos // position of "func" keyword (token.NoPos if there is no "func")
+ TypeParams *FieldList // type parameters; or nil
+ Params *FieldList // (incoming) parameters; non-nil
+ Results *FieldList // (outgoing) results; or nil
+ }
+
// An InterfaceType node represents an interface type.
InterfaceType struct {
Interface token.Pos // position of "interface" keyword
@@ -479,6 +493,7 @@ func (x *CompositeLit) Pos() token.Pos {
func (x *ParenExpr) Pos() token.Pos { return x.Lparen }
func (x *SelectorExpr) Pos() token.Pos { return x.X.Pos() }
func (x *IndexExpr) Pos() token.Pos { return x.X.Pos() }
+func (x *IndexListExpr) Pos() token.Pos { return x.X.Pos() }
func (x *SliceExpr) Pos() token.Pos { return x.X.Pos() }
func (x *TypeAssertExpr) Pos() token.Pos { return x.X.Pos() }
func (x *CallExpr) Pos() token.Pos { return x.Fun.Pos() }
@@ -512,6 +527,7 @@ func (x *CompositeLit) End() token.Pos { return x.Rbrace + 1 }
func (x *ParenExpr) End() token.Pos { return x.Rparen + 1 }
func (x *SelectorExpr) End() token.Pos { return x.Sel.End() }
func (x *IndexExpr) End() token.Pos { return x.Rbrack + 1 }
+func (x *IndexListExpr) End() token.Pos { return x.Rbrack + 1 }
func (x *SliceExpr) End() token.Pos { return x.Rbrack + 1 }
func (x *TypeAssertExpr) End() token.Pos { return x.Rparen + 1 }
func (x *CallExpr) End() token.Pos { return x.Rparen + 1 }
@@ -543,6 +559,7 @@ func (*CompositeLit) exprNode() {}
func (*ParenExpr) exprNode() {}
func (*SelectorExpr) exprNode() {}
func (*IndexExpr) exprNode() {}
+func (*IndexListExpr) exprNode() {}
func (*SliceExpr) exprNode() {}
func (*TypeAssertExpr) exprNode() {}
func (*CallExpr) exprNode() {}
@@ -892,6 +909,16 @@ type (
Values []Expr // initial values; or nil
Comment *CommentGroup // line comments; or nil
}
+
+ // A TypeSpec node represents a type declaration (TypeSpec production).
+ TypeSpec struct {
+ Doc *CommentGroup // associated documentation; or nil
+ Name *Ident // type name
+ TypeParams *FieldList // type parameters; or nil
+ Assign token.Pos // position of '=', if any
+ Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes
+ Comment *CommentGroup // line comments; or nil
+ }
)
// Pos and End implementations for spec nodes.
@@ -968,8 +995,6 @@ type (
Name *Ident // function/method name
Type *FuncType // function signature: type and value parameters, results, and position of "func" keyword
Body *BlockStmt // function body; or nil for external (non-Go) function
- // TODO(rFindley) consider storing TParams here, rather than FuncType, as
- // they are only valid for declared functions
}
)
diff --git a/libgo/go/go/ast/ast_notypeparams.go b/libgo/go/go/ast/ast_notypeparams.go
deleted file mode 100644
index fa132fb..0000000
--- a/libgo/go/go/ast/ast_notypeparams.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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.
-
-//go:build !typeparams
-// +build !typeparams
-
-package ast
-
-import "go/token"
-
-type (
- // A FuncType node represents a function type.
- FuncType struct {
- Func token.Pos // position of "func" keyword (token.NoPos if there is no "func")
- Params *FieldList // (incoming) parameters; non-nil
- Results *FieldList // (outgoing) results; or nil
- }
-
- // A TypeSpec node represents a type declaration (TypeSpec production).
- TypeSpec struct {
- Doc *CommentGroup // associated documentation; or nil
- Name *Ident // type name
- Assign token.Pos // position of '=', if any
- Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes
- Comment *CommentGroup // line comments; or nil
- }
-)
diff --git a/libgo/go/go/ast/ast_typeparams.go b/libgo/go/go/ast/ast_typeparams.go
deleted file mode 100644
index 24fdc5f..0000000
--- a/libgo/go/go/ast/ast_typeparams.go
+++ /dev/null
@@ -1,51 +0,0 @@
-// 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.
-
-//go:build typeparams
-// +build typeparams
-
-package ast
-
-import "go/token"
-
-type (
- // A FuncType node represents a function type.
- FuncType struct {
- Func token.Pos // position of "func" keyword (token.NoPos if there is no "func")
- TParams *FieldList // type parameters; or nil
- Params *FieldList // (incoming) parameters; non-nil
- Results *FieldList // (outgoing) results; or nil
- }
-
- // A TypeSpec node represents a type declaration (TypeSpec production).
- TypeSpec struct {
- Doc *CommentGroup // associated documentation; or nil
- Name *Ident // type name
- TParams *FieldList // type parameters; or nil
- Assign token.Pos // position of '=', if any
- Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes
- Comment *CommentGroup // line comments; or nil
- }
-
- // A ListExpr node represents a list of expressions separated by commas.
- // ListExpr nodes are used as index in IndexExpr nodes representing type
- // or function instantiations with more than one type argument.
- ListExpr struct {
- ElemList []Expr
- }
-)
-
-func (*ListExpr) exprNode() {}
-func (x *ListExpr) Pos() token.Pos {
- if len(x.ElemList) > 0 {
- return x.ElemList[0].Pos()
- }
- return token.NoPos
-}
-func (x *ListExpr) End() token.Pos {
- if len(x.ElemList) > 0 {
- return x.ElemList[len(x.ElemList)-1].End()
- }
- return token.NoPos
-}
diff --git a/libgo/go/go/ast/print.go b/libgo/go/go/ast/print.go
index d86d9ba..85e6943 100644
--- a/libgo/go/go/ast/print.go
+++ b/libgo/go/go/ast/print.go
@@ -21,7 +21,7 @@ type FieldFilter func(name string, value reflect.Value) bool
// it returns false otherwise.
func NotNilFilter(_ string, v reflect.Value) bool {
switch v.Kind() {
- case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
+ case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
return !v.IsNil()
}
return true
@@ -36,17 +36,17 @@ func NotNilFilter(_ string, v reflect.Value) bool {
// struct fields for which f(fieldname, fieldvalue) is true are
// printed; all others are filtered from the output. Unexported
// struct fields are never printed.
-func Fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) error {
+func Fprint(w io.Writer, fset *token.FileSet, x any, f FieldFilter) error {
return fprint(w, fset, x, f)
}
-func fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) (err error) {
+func fprint(w io.Writer, fset *token.FileSet, x any, f FieldFilter) (err error) {
// setup printer
p := printer{
output: w,
fset: fset,
filter: f,
- ptrmap: make(map[interface{}]int),
+ ptrmap: make(map[any]int),
last: '\n', // force printing of line number on first line
}
@@ -70,7 +70,7 @@ func fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) (err
// Print prints x to standard output, skipping nil fields.
// Print(fset, x) is the same as Fprint(os.Stdout, fset, x, NotNilFilter).
-func Print(fset *token.FileSet, x interface{}) error {
+func Print(fset *token.FileSet, x any) error {
return Fprint(os.Stdout, fset, x, NotNilFilter)
}
@@ -78,10 +78,10 @@ type printer struct {
output io.Writer
fset *token.FileSet
filter FieldFilter
- ptrmap map[interface{}]int // *T -> line number
- indent int // current indentation level
- last byte // the last byte processed by Write
- line int // current line number
+ ptrmap map[any]int // *T -> line number
+ indent int // current indentation level
+ last byte // the last byte processed by Write
+ line int // current line number
}
var indent = []byte(". ")
@@ -125,7 +125,7 @@ type localError struct {
}
// printf is a convenience wrapper that takes care of print errors.
-func (p *printer) printf(format string, args ...interface{}) {
+func (p *printer) printf(format string, args ...any) {
if _, err := fmt.Fprintf(p, format, args...); err != nil {
panic(localError{err})
}
@@ -165,7 +165,7 @@ func (p *printer) print(x reflect.Value) {
}
p.printf("}")
- case reflect.Ptr:
+ case reflect.Pointer:
p.printf("*")
// type-checked ASTs may contain cycles - use ptrmap
// to keep track of objects that have been printed
diff --git a/libgo/go/go/ast/print_test.go b/libgo/go/go/ast/print_test.go
index 210f164..6691ccd 100644
--- a/libgo/go/go/ast/print_test.go
+++ b/libgo/go/go/ast/print_test.go
@@ -11,7 +11,7 @@ import (
)
var tests = []struct {
- x interface{} // x is printed as s
+ x any // x is printed as s
s string
}{
// basic types
diff --git a/libgo/go/go/ast/resolve.go b/libgo/go/go/ast/resolve.go
index c1830b5..126a27b 100644
--- a/libgo/go/go/ast/resolve.go
+++ b/libgo/go/go/ast/resolve.go
@@ -22,7 +22,7 @@ func (p *pkgBuilder) error(pos token.Pos, msg string) {
p.errors.Add(p.fset.Position(pos), msg)
}
-func (p *pkgBuilder) errorf(pos token.Pos, format string, args ...interface{}) {
+func (p *pkgBuilder) errorf(pos token.Pos, format string, args ...any) {
p.error(pos, fmt.Sprintf(format, args...))
}
diff --git a/libgo/go/go/ast/scope.go b/libgo/go/go/ast/scope.go
index a400c71..d24a5f0 100644
--- a/libgo/go/go/ast/scope.go
+++ b/libgo/go/go/ast/scope.go
@@ -75,10 +75,10 @@ func (s *Scope) String() string {
//
type Object struct {
Kind ObjKind
- Name string // declared name
- Decl interface{} // corresponding Field, XxxSpec, FuncDecl, LabeledStmt, AssignStmt, Scope; or nil
- Data interface{} // object-specific data; or nil
- Type interface{} // placeholder for type information; may be nil
+ Name string // declared name
+ Decl any // corresponding Field, XxxSpec, FuncDecl, LabeledStmt, AssignStmt, Scope; or nil
+ Data any // object-specific data; or nil
+ Type any // placeholder for type information; may be nil
}
// NewObj creates a new object of a given kind and name.
diff --git a/libgo/go/go/ast/walk.go b/libgo/go/go/ast/walk.go
index 9224264..308662f 100644
--- a/libgo/go/go/ast/walk.go
+++ b/libgo/go/go/ast/walk.go
@@ -4,6 +4,8 @@
package ast
+import "fmt"
+
// A Visitor's Visit method is invoked for each node encountered by Walk.
// If the result visitor w is not nil, Walk visits each of the children
// of node with the visitor w, followed by a call of w.Visit(nil).
@@ -114,6 +116,12 @@ func Walk(v Visitor, node Node) {
Walk(v, n.X)
Walk(v, n.Index)
+ case *IndexListExpr:
+ Walk(v, n.X)
+ for _, index := range n.Indices {
+ Walk(v, index)
+ }
+
case *SliceExpr:
Walk(v, n.X)
if n.Low != nil {
@@ -161,7 +169,9 @@ func Walk(v Visitor, node Node) {
Walk(v, n.Fields)
case *FuncType:
- walkFuncTypeParams(v, n)
+ if n.TypeParams != nil {
+ Walk(v, n.TypeParams)
+ }
if n.Params != nil {
Walk(v, n.Params)
}
@@ -316,7 +326,9 @@ func Walk(v Visitor, node Node) {
Walk(v, n.Doc)
}
Walk(v, n.Name)
- walkTypeSpecParams(v, n)
+ if n.TypeParams != nil {
+ Walk(v, n.TypeParams)
+ }
Walk(v, n.Type)
if n.Comment != nil {
Walk(v, n.Comment)
@@ -363,7 +375,7 @@ func Walk(v Visitor, node Node) {
}
default:
- walkOtherNodes(v, n)
+ panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n))
}
v.Visit(nil)
diff --git a/libgo/go/go/ast/walk_notypeparams.go b/libgo/go/go/ast/walk_notypeparams.go
deleted file mode 100644
index d43e13d..0000000
--- a/libgo/go/go/ast/walk_notypeparams.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// 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.
-
-//go:build !typeparams
-// +build !typeparams
-
-package ast
-
-import "fmt"
-
-func walkFuncTypeParams(v Visitor, n *FuncType) {}
-func walkTypeSpecParams(v Visitor, n *TypeSpec) {}
-
-func walkOtherNodes(v Visitor, n Node) {
- panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n))
-}
diff --git a/libgo/go/go/ast/walk_typeparams.go b/libgo/go/go/ast/walk_typeparams.go
deleted file mode 100644
index b662133..0000000
--- a/libgo/go/go/ast/walk_typeparams.go
+++ /dev/null
@@ -1,36 +0,0 @@
-// 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.
-
-//go:build typeparams
-// +build typeparams
-
-package ast
-
-import (
- "fmt"
-)
-
-func walkFuncTypeParams(v Visitor, n *FuncType) {
- if n.TParams != nil {
- Walk(v, n.TParams)
- }
-}
-
-func walkTypeSpecParams(v Visitor, n *TypeSpec) {
- if n.TParams != nil {
- Walk(v, n.TParams)
- }
-}
-
-func walkOtherNodes(v Visitor, n Node) {
- if e, ok := n.(*ListExpr); ok {
- if e != nil {
- for _, elem := range e.ElemList {
- Walk(v, elem)
- }
- }
- } else {
- panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n))
- }
-}
diff --git a/libgo/go/go/build/build.go b/libgo/go/go/build/build.go
index f19f3d4..1e91e6e 100644
--- a/libgo/go/go/build/build.go
+++ b/libgo/go/go/build/build.go
@@ -791,7 +791,7 @@ Found:
}
// package was not found
- return p, fmt.Errorf("cannot find package %q in:\n\t%s", path, p.Dir)
+ return p, fmt.Errorf("cannot find package %q in:\n\t%s", p.ImportPath, p.Dir)
}
if mode&FindOnly != 0 {
@@ -896,7 +896,7 @@ Found:
isTest := strings.HasSuffix(name, "_test.go")
isXTest := false
- if isTest && strings.HasSuffix(pkg, "_test") {
+ if isTest && strings.HasSuffix(pkg, "_test") && p.Name != pkg {
isXTest = true
pkg = pkg[:len(pkg)-len("_test")]
}
@@ -1260,19 +1260,14 @@ func findImportComment(data []byte) (s string, line int) {
var comment []byte
switch {
case bytes.HasPrefix(data, slashSlash):
- i := bytes.Index(data, newline)
- if i < 0 {
- i = len(data)
- }
- comment = data[2:i]
+ comment, _, _ = bytes.Cut(data[2:], newline)
case bytes.HasPrefix(data, slashStar):
- data = data[2:]
- i := bytes.Index(data, starSlash)
- if i < 0 {
+ var ok bool
+ comment, _, ok = bytes.Cut(data[2:], starSlash)
+ if !ok {
// malformed comment
return "", 0
}
- comment = data[:i]
if bytes.Contains(comment, newline) {
return "", 0
}
@@ -1656,12 +1651,10 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
}
// Split at colon.
- line = strings.TrimSpace(line[4:])
- i := strings.Index(line, ":")
- if i < 0 {
+ line, argstr, ok := strings.Cut(strings.TrimSpace(line[4:]), ":")
+ if !ok {
return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
}
- line, argstr := line[:i], line[i+1:]
// Parse GOOS/GOARCH stuff.
f := strings.Fields(line)
@@ -1687,7 +1680,6 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
if err != nil {
return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
}
- var ok bool
for i, arg := range args {
if arg, ok = expandSrcDir(arg, di.Dir); !ok {
return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg)
@@ -1946,9 +1938,7 @@ func (ctxt *Context) matchTag(name string, allTags map[string]bool) bool {
// if GOOS=illumos, then files with GOOS=solaris are also matched.
// if GOOS=ios, then files with GOOS=darwin are also matched.
func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
- if dot := strings.Index(name, "."); dot != -1 {
- name = name[:dot]
- }
+ name, _, _ = strings.Cut(name, ".")
// Before Go 1.4, a file called "linux.go" would be equivalent to having a
// build tag "linux" in that file. For Go 1.4 and beyond, we require this
diff --git a/libgo/go/go/build/build_test.go b/libgo/go/go/build/build_test.go
index a8f361a..438dd47 100644
--- a/libgo/go/go/build/build_test.go
+++ b/libgo/go/go/build/build_test.go
@@ -714,7 +714,7 @@ func TestMissingImportErrorRepetition(t *testing.T) {
// Also don't count instances in suggested "go get" or similar commands
// (see https://golang.org/issue/41576). The suggested command typically
// follows a semicolon.
- errStr = strings.SplitN(errStr, ";", 2)[0]
+ errStr, _, _ = strings.Cut(errStr, ";")
if n := strings.Count(errStr, pkgPath); n != 1 {
t.Fatalf("package path %q appears in error %d times; should appear once\nerror: %v", pkgPath, n, err)
diff --git a/libgo/go/go/build/deps_test.go b/libgo/go/go/build/deps_test.go
index e9b4e71..ab92b56 100644
--- a/libgo/go/go/build/deps_test.go
+++ b/libgo/go/go/build/deps_test.go
@@ -70,18 +70,20 @@ import (
var depsRules = `
# No dependencies allowed for any of these packages.
NONE
- < container/list, container/ring,
- internal/cfg, internal/cpu, internal/goexperiment,
+ < constraints, container/list, container/ring,
+ internal/cfg, internal/cpu, internal/goarch,
+ internal/goexperiment, internal/goos,
internal/goversion, internal/nettrace,
unicode/utf8, unicode/utf16, unicode,
unsafe;
- # These packages depend only on unsafe.
- unsafe
+ # These packages depend only on internal/goarch and unsafe.
+ internal/goarch, unsafe
< internal/abi;
# RUNTIME is the core runtime group of packages, all of them very light-weight.
- internal/abi, internal/cpu, internal/goexperiment, unsafe
+ internal/abi, internal/cpu, internal/goarch,
+ internal/goexperiment, internal/goos, unsafe
< internal/bytealg
< internal/itoa
< internal/unsafeheader
@@ -171,7 +173,7 @@ var depsRules = `
io/fs
< embed;
- unicode, fmt !< os, os/signal;
+ unicode, fmt !< net, os, os/signal;
os/signal, STR
< path/filepath
@@ -185,6 +187,8 @@ var depsRules = `
OS
< golang.org/x/sys/cpu;
+ os < internal/godebug;
+
# FMT is OS (which includes string routines) plus reflect and fmt.
# It does not include package log, which should be avoided in core packages.
strconv, unicode
@@ -212,7 +216,6 @@ var depsRules = `
mime/quotedprintable,
net/internal/socktest,
net/url,
- runtime/debug,
runtime/trace,
text/scanner,
text/tabwriter;
@@ -269,8 +272,10 @@ var depsRules = `
# executable parsing
FMT, encoding/binary, compress/zlib
+ < runtime/debug
< debug/dwarf
< debug/elf, debug/gosym, debug/macho, debug/pe, debug/plan9obj, internal/xcoff
+ < debug/buildinfo
< DEBUG;
# go parser and friends.
@@ -328,7 +333,7 @@ var depsRules = `
< C
< runtime/cgo
< CGO
- < runtime/race, runtime/msan;
+ < runtime/race, runtime/msan, runtime/asan;
# Bulk of the standard library must not use cgo.
# The prohibition stops at net and os/user.
@@ -349,6 +354,13 @@ var depsRules = `
golang.org/x/net/lif,
golang.org/x/net/route;
+ os, runtime, strconv, sync, unsafe,
+ internal/godebug
+ < internal/intern;
+
+ internal/bytealg, internal/intern, internal/itoa, math/bits, sort, strconv
+ < net/netip;
+
# net is unavoidable when doing any networking,
# so large dependencies must be kept out.
# This is a long-looking list but most of these
@@ -357,10 +369,12 @@ var depsRules = `
golang.org/x/net/dns/dnsmessage,
golang.org/x/net/lif,
golang.org/x/net/route,
+ internal/godebug,
internal/nettrace,
internal/poll,
internal/singleflight,
internal/race,
+ net/netip,
os
< net;
@@ -393,7 +407,8 @@ var depsRules = `
< crypto/subtle
< crypto/internal/subtle
< crypto/elliptic/internal/fiat
- < crypto/ed25519/internal/edwards25519/field
+ < crypto/elliptic/internal/nistec
+ < crypto/ed25519/internal/edwards25519/field, golang.org/x/crypto/curve25519/internal/field
< crypto/ed25519/internal/edwards25519
< crypto/cipher
< crypto/aes, crypto/des, crypto/hmac, crypto/md5, crypto/rc4,
@@ -421,7 +436,7 @@ var depsRules = `
CRYPTO-MATH, NET, container/list, encoding/hex, encoding/pem
< golang.org/x/crypto/internal/subtle
< golang.org/x/crypto/chacha20
- < golang.org/x/crypto/poly1305
+ < golang.org/x/crypto/internal/poly1305
< golang.org/x/crypto/chacha20poly1305
< golang.org/x/crypto/hkdf
< crypto/x509/internal/macos
@@ -508,10 +523,14 @@ var depsRules = `
FMT, flag, math/rand
< testing/quick;
- FMT, flag, runtime/debug, runtime/trace, internal/sysinfo, math/rand
+ FMT, DEBUG, flag, runtime/trace, internal/sysinfo, math/rand
< testing;
- internal/testlog, runtime/pprof, regexp
+ FMT, crypto/sha256, encoding/json, go/ast, go/parser, go/token,
+ internal/godebug, math/rand, encoding/hex, crypto/sha256
+ < internal/fuzz;
+
+ internal/fuzz, internal/testlog, runtime/pprof, regexp
< testing/internal/testdeps;
OS, flag, testing, internal/cfg
@@ -526,6 +545,9 @@ var depsRules = `
NET, testing, math/rand
< golang.org/x/net/nettest;
+ syscall
+ < os/exec/internal/fdtest;
+
FMT, container/heap, math/rand
< internal/trace;
`
@@ -615,7 +637,7 @@ func TestDependencies(t *testing.T) {
}
}
-var buildIgnore = []byte("\n// +build ignore")
+var buildIgnore = []byte("\n//go:build ignore")
func findImports(pkg string) ([]string, error) {
vpkg := pkg
@@ -652,6 +674,9 @@ func findImports(pkg string) ([]string, error) {
if err != nil {
return nil, fmt.Errorf("reading %v: %v", name, err)
}
+ if info.parsed.Name.Name == "main" {
+ continue
+ }
if bytes.Contains(info.header, buildIgnore) {
continue
}
diff --git a/libgo/go/go/build/gc.go b/libgo/go/go/build/gc.go
index e16e186..434991f 100644
--- a/libgo/go/go/build/gc.go
+++ b/libgo/go/go/build/gc.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build gc
-// +build gc
package build
diff --git a/libgo/go/go/build/gccgo.go b/libgo/go/go/build/gccgo.go
index c8ec704..f806729 100644
--- a/libgo/go/go/build/gccgo.go
+++ b/libgo/go/go/build/gccgo.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build gccgo
-// +build gccgo
package build
diff --git a/libgo/go/go/build/read.go b/libgo/go/go/build/read.go
index b98c793..de5c33a 100644
--- a/libgo/go/go/build/read.go
+++ b/libgo/go/go/build/read.go
@@ -240,6 +240,27 @@ func (r *importReader) findEmbed(first bool) bool {
}
}
+ case '\'':
+ startLine = false
+ for r.err == nil {
+ if r.eof {
+ r.syntaxError()
+ }
+ c = r.readByteNoBuf()
+ if c == '\\' {
+ r.readByteNoBuf()
+ if r.err != nil {
+ r.syntaxError()
+ return false
+ }
+ continue
+ }
+ if c == '\'' {
+ c = r.readByteNoBuf()
+ goto Reswitch
+ }
+ }
+
case '/':
c = r.readByteNoBuf()
switch c {
@@ -516,12 +537,12 @@ func parseGoEmbed(args string, pos token.Position) ([]fileEmbed, error) {
trimBytes(i)
case '`':
- i := strings.Index(args[1:], "`")
- if i < 0 {
+ var ok bool
+ path, _, ok = strings.Cut(args[1:], "`")
+ if !ok {
return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
}
- path = args[1 : 1+i]
- trimBytes(1 + i + 1)
+ trimBytes(1 + len(path) + 1)
case '"':
i := 1
diff --git a/libgo/go/go/build/read_test.go b/libgo/go/go/build/read_test.go
index 1e5e1c2..6851e6b 100644
--- a/libgo/go/go/build/read_test.go
+++ b/libgo/go/go/build/read_test.go
@@ -119,20 +119,15 @@ var readCommentsTests = []readTest{
func testRead(t *testing.T, tests []readTest, read func(io.Reader) ([]byte, error)) {
for i, tt := range tests {
- var in, testOut string
- j := strings.Index(tt.in, "ℙ")
- if j < 0 {
- in = tt.in
- testOut = tt.in
- } else {
- in = tt.in[:j] + tt.in[j+len("ℙ"):]
- testOut = tt.in[:j]
- }
- d := strings.Index(tt.in, "𝔻")
- if d >= 0 {
- in = in[:d] + in[d+len("𝔻"):]
- testOut = testOut[d+len("𝔻"):]
+ beforeP, afterP, _ := strings.Cut(tt.in, "ℙ")
+ in := beforeP + afterP
+ testOut := beforeP
+
+ if beforeD, afterD, ok := strings.Cut(beforeP, "𝔻"); ok {
+ in = beforeD + afterD + afterP
+ testOut = afterD
}
+
r := strings.NewReader(in)
buf, err := read(r)
if err != nil {
diff --git a/libgo/go/go/build/syslist.go b/libgo/go/go/build/syslist.go
index cb7d876..1b11365 100644
--- a/libgo/go/go/build/syslist.go
+++ b/libgo/go/go/build/syslist.go
@@ -8,4 +8,4 @@ package build
// Do not remove from this list, as these are used for go/build filename matching.
const goosList = "aix android darwin dragonfly freebsd hurd illumos ios js linux nacl netbsd openbsd plan9 solaris windows zos "
-const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le loong64 mips mipsle mips64 mips64le mips64p32 mips64p32le ppc riscv riscv64 s390 s390x sparc sparc64 wasm alpha m68k nios2 sh shbe "
+const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be loong64 mips mipsle mips64 mips64le ppc ppc64 ppc64le riscv riscv64 s390 s390x sparc sparc64 wasm alpha m68k nios2 sh shbe "
diff --git a/libgo/go/go/constant/kind_string.go b/libgo/go/go/constant/kind_string.go
new file mode 100644
index 0000000..7003325
--- /dev/null
+++ b/libgo/go/go/constant/kind_string.go
@@ -0,0 +1,28 @@
+// Code generated by "stringer -type Kind"; DO NOT EDIT.
+
+package constant
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[Unknown-0]
+ _ = x[Bool-1]
+ _ = x[String-2]
+ _ = x[Int-3]
+ _ = x[Float-4]
+ _ = x[Complex-5]
+}
+
+const _Kind_name = "UnknownBoolStringIntFloatComplex"
+
+var _Kind_index = [...]uint8{0, 7, 11, 17, 20, 25, 32}
+
+func (i Kind) String() string {
+ if i < 0 || i >= Kind(len(_Kind_index)-1) {
+ return "Kind(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _Kind_name[_Kind_index[i]:_Kind_index[i+1]]
+}
diff --git a/libgo/go/go/constant/value.go b/libgo/go/go/constant/value.go
index 78cb3f8..dee3bce 100644
--- a/libgo/go/go/constant/value.go
+++ b/libgo/go/go/constant/value.go
@@ -24,6 +24,8 @@ import (
"unicode/utf8"
)
+//go:generate stringer -type Kind
+
// Kind specifies the kind of value represented by a Value.
type Kind int
@@ -577,7 +579,7 @@ func Float64Val(x Value) (float64, bool) {
// Float *big.Float or *big.Rat
// everything else nil
//
-func Val(x Value) interface{} {
+func Val(x Value) any {
switch x := x.(type) {
case boolVal:
return bool(x)
@@ -608,7 +610,7 @@ func Val(x Value) interface{} {
// *big.Rat Float
// anything else Unknown
//
-func Make(x interface{}) Value {
+func Make(x any) Value {
switch x := x.(type) {
case bool:
return boolVal(x)
diff --git a/libgo/go/go/constant/value_test.go b/libgo/go/go/constant/value_test.go
index 91ad0b0..e41315e 100644
--- a/libgo/go/go/constant/value_test.go
+++ b/libgo/go/go/constant/value_test.go
@@ -143,9 +143,9 @@ func testNumbers(t *testing.T, kind token.Token, tests []string) {
if a[1] == "?" {
y = MakeUnknown()
} else {
- if i := strings.Index(a[1], "/"); i >= 0 && kind == token.FLOAT {
- n := MakeFromLiteral(a[1][:i], token.INT, 0)
- d := MakeFromLiteral(a[1][i+1:], token.INT, 0)
+ if ns, ds, ok := strings.Cut(a[1], "/"); ok && kind == token.FLOAT {
+ n := MakeFromLiteral(ns, token.INT, 0)
+ d := MakeFromLiteral(ds, token.INT, 0)
y = BinaryOp(n, token.QUO, d)
} else {
y = MakeFromLiteral(a[1], kind, 0)
@@ -454,10 +454,10 @@ func val(lit string) Value {
return MakeBool(false)
}
- if i := strings.IndexByte(lit, '/'); i >= 0 {
+ if as, bs, ok := strings.Cut(lit, "/"); ok {
// assume fraction
- a := MakeFromLiteral(lit[:i], token.INT, 0)
- b := MakeFromLiteral(lit[i+1:], token.INT, 0)
+ a := MakeFromLiteral(as, token.INT, 0)
+ b := MakeFromLiteral(bs, token.INT, 0)
return BinaryOp(a, token.QUO, b)
}
@@ -659,10 +659,10 @@ func TestMakeFloat64(t *testing.T) {
type makeTestCase struct {
kind Kind
- arg, want interface{}
+ arg, want any
}
-func dup(k Kind, x interface{}) makeTestCase { return makeTestCase{k, x, x} }
+func dup(k Kind, x any) makeTestCase { return makeTestCase{k, x, x} }
func TestMake(t *testing.T) {
for _, test := range []makeTestCase{
diff --git a/libgo/go/go/doc/comment.go b/libgo/go/go/doc/comment.go
index 92131a3..a93c05f 100644
--- a/libgo/go/go/doc/comment.go
+++ b/libgo/go/go/doc/comment.go
@@ -236,26 +236,24 @@ func heading(line string) string {
// allow "'" for possessive "'s" only
for b := line; ; {
- i := strings.IndexRune(b, '\'')
- if i < 0 {
+ var ok bool
+ if _, b, ok = strings.Cut(b, "'"); !ok {
break
}
- if i+1 >= len(b) || b[i+1] != 's' || (i+2 < len(b) && b[i+2] != ' ') {
- return "" // not followed by "s "
+ if b != "s" && !strings.HasPrefix(b, "s ") {
+ return "" // ' not followed by s and then end-of-word
}
- b = b[i+2:]
}
// allow "." when followed by non-space
for b := line; ; {
- i := strings.IndexRune(b, '.')
- if i < 0 {
+ var ok bool
+ if _, b, ok = strings.Cut(b, "."); !ok {
break
}
- if i+1 >= len(b) || b[i+1] == ' ' {
+ if b == "" || strings.HasPrefix(b, " ") {
return "" // not followed by non-space
}
- b = b[i+1:]
}
return line
diff --git a/libgo/go/go/doc/doc.go b/libgo/go/go/doc/doc.go
index 79d3899..5ab854d 100644
--- a/libgo/go/go/doc/doc.go
+++ b/libgo/go/go/doc/doc.go
@@ -157,7 +157,7 @@ func New(pkg *ast.Package, importPath string, mode Mode) *Package {
// NewFromFiles takes ownership of the AST files and may edit them,
// unless the PreserveAST Mode bit is on.
//
-func NewFromFiles(fset *token.FileSet, files []*ast.File, importPath string, opts ...interface{}) (*Package, error) {
+func NewFromFiles(fset *token.FileSet, files []*ast.File, importPath string, opts ...any) (*Package, error) {
// Check for invalid API usage.
if fset == nil {
panic(fmt.Errorf("doc.NewFromFiles: no token.FileSet provided (fset == nil)"))
diff --git a/libgo/go/go/doc/doc_test.go b/libgo/go/go/doc/doc_test.go
index cbdca62..00cb085 100644
--- a/libgo/go/go/doc/doc_test.go
+++ b/libgo/go/go/doc/doc_test.go
@@ -16,6 +16,7 @@ import (
"os"
"path/filepath"
"regexp"
+ "runtime"
"strings"
"testing"
"text/template"
@@ -38,7 +39,7 @@ func readTemplate(filename string) *template.Template {
return template.Must(t.ParseFiles(filepath.Join(dataDir, filename)))
}
-func nodeFmt(node interface{}, fset *token.FileSet) string {
+func nodeFmt(node any, fset *token.FileSet) string {
var buf bytes.Buffer
printer.Fprint(&buf, fset, node)
return strings.ReplaceAll(strings.TrimSpace(buf.String()), "\n", "\n\t")
@@ -100,58 +101,56 @@ func test(t *testing.T, mode Mode) {
// test packages
for _, pkg := range pkgs {
- importPath := dataDir + "/" + pkg.Name
- var files []*ast.File
- for _, f := range pkg.Files {
- files = append(files, f)
- }
- doc, err := NewFromFiles(fset, files, importPath, mode)
- if err != nil {
- t.Error(err)
- continue
- }
+ t.Run(pkg.Name, func(t *testing.T) {
+ importPath := dataDir + "/" + pkg.Name
+ var files []*ast.File
+ for _, f := range pkg.Files {
+ files = append(files, f)
+ }
+ doc, err := NewFromFiles(fset, files, importPath, mode)
+ if err != nil {
+ t.Fatal(err)
+ }
- // golden files always use / in filenames - canonicalize them
- for i, filename := range doc.Filenames {
- doc.Filenames[i] = filepath.ToSlash(filename)
- }
+ // golden files always use / in filenames - canonicalize them
+ for i, filename := range doc.Filenames {
+ doc.Filenames[i] = filepath.ToSlash(filename)
+ }
- // print documentation
- var buf bytes.Buffer
- if err := templateTxt.Execute(&buf, bundle{doc, fset}); err != nil {
- t.Error(err)
- continue
- }
- got := buf.Bytes()
+ // print documentation
+ var buf bytes.Buffer
+ if err := templateTxt.Execute(&buf, bundle{doc, fset}); err != nil {
+ t.Fatal(err)
+ }
+ got := buf.Bytes()
- // update golden file if necessary
- golden := filepath.Join(dataDir, fmt.Sprintf("%s.%d.golden", pkg.Name, mode))
- if *update {
- err := os.WriteFile(golden, got, 0644)
- if err != nil {
- t.Error(err)
+ // update golden file if necessary
+ golden := filepath.Join(dataDir, fmt.Sprintf("%s.%d.golden", pkg.Name, mode))
+ if *update {
+ err := os.WriteFile(golden, got, 0644)
+ if err != nil {
+ t.Fatal(err)
+ }
}
- continue
- }
- // get golden file
- want, err := os.ReadFile(golden)
- if err != nil {
- t.Error(err)
- continue
- }
+ // get golden file
+ want, err := os.ReadFile(golden)
+ if err != nil {
+ t.Fatal(err)
+ }
- // compare
- if !bytes.Equal(got, want) {
- t.Errorf("package %s\n\tgot:\n%s\n\twant:\n%s", pkg.Name, got, want)
- }
+ // compare
+ if !bytes.Equal(got, want) {
+ t.Errorf("package %s\n\tgot:\n%s\n\twant:\n%s", pkg.Name, got, want)
+ }
+ })
}
}
func Test(t *testing.T) {
- test(t, 0)
- test(t, AllDecls)
- test(t, AllMethods)
+ t.Run("default", func(t *testing.T) { test(t, 0) })
+ t.Run("AllDecls", func(t *testing.T) { test(t, AllDecls) })
+ t.Run("AllMethods", func(t *testing.T) { test(t, AllMethods) })
}
func TestAnchorID(t *testing.T) {
@@ -162,3 +161,153 @@ func TestAnchorID(t *testing.T) {
t.Errorf("anchorID(%q) = %q; want %q", in, got, want)
}
}
+
+func TestFuncs(t *testing.T) {
+ if runtime.Compiler == "gccgo" {
+ t.Skip("FIXME skipping for gofrontend: generics")
+ }
+
+ fset := token.NewFileSet()
+ file, err := parser.ParseFile(fset, "funcs.go", strings.NewReader(funcsTestFile), parser.ParseComments)
+ if err != nil {
+ t.Fatal(err)
+ }
+ doc, err := NewFromFiles(fset, []*ast.File{file}, "importPath", Mode(0))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, f := range doc.Funcs {
+ f.Decl = nil
+ }
+ for _, ty := range doc.Types {
+ for _, f := range ty.Funcs {
+ f.Decl = nil
+ }
+ for _, m := range ty.Methods {
+ m.Decl = nil
+ }
+ }
+
+ /* FIXME: Commented out for gofrontend.
+
+ compareFuncs := func(t *testing.T, msg string, got, want *Func) {
+ // ignore Decl and Examples
+ got.Decl = nil
+ got.Examples = nil
+ if !(got.Doc == want.Doc &&
+ got.Name == want.Name &&
+ got.Recv == want.Recv &&
+ got.Orig == want.Orig &&
+ got.Level == want.Level) {
+ t.Errorf("%s:\ngot %+v\nwant %+v", msg, got, want)
+ }
+ }
+
+ compareSlices(t, "Funcs", doc.Funcs, funcsPackage.Funcs, compareFuncs)
+ compareSlices(t, "Types", doc.Types, funcsPackage.Types, func(t *testing.T, msg string, got, want *Type) {
+ if got.Name != want.Name {
+ t.Errorf("%s.Name: got %q, want %q", msg, got.Name, want.Name)
+ } else {
+ compareSlices(t, got.Name+".Funcs", got.Funcs, want.Funcs, compareFuncs)
+ compareSlices(t, got.Name+".Methods", got.Methods, want.Methods, compareFuncs)
+ }
+ })
+ */
+}
+
+/* FIXME: Commented out for gofrontend.
+
+func compareSlices[E any](t *testing.T, name string, got, want []E, compareElem func(*testing.T, string, E, E)) {
+ if len(got) != len(want) {
+ t.Errorf("%s: got %d, want %d", name, len(got), len(want))
+ }
+ for i := 0; i < len(got) && i < len(want); i++ {
+ compareElem(t, fmt.Sprintf("%s[%d]", name, i), got[i], want[i])
+ }
+}
+
+*/
+
+const funcsTestFile = `
+package funcs
+
+func F() {}
+
+type S1 struct {
+ S2 // embedded, exported
+ s3 // embedded, unexported
+}
+
+func NewS1() S1 {return S1{} }
+func NewS1p() *S1 { return &S1{} }
+
+func (S1) M1() {}
+func (r S1) M2() {}
+func(S1) m3() {} // unexported not shown
+func (*S1) P1() {} // pointer receiver
+
+type S2 int
+func (S2) M3() {} // shown on S2
+
+type s3 int
+func (s3) M4() {} // shown on S1
+
+type G1[T any] struct {
+ *s3
+}
+
+func NewG1[T any]() G1[T] { return G1[T]{} }
+
+func (G1[T]) MG1() {}
+func (*G1[U]) MG2() {}
+
+type G2[T, U any] struct {}
+
+func NewG2[T, U any]() G2[T, U] { return G2[T, U]{} }
+
+func (G2[T, U]) MG3() {}
+func (*G2[A, B]) MG4() {}
+
+
+`
+
+var funcsPackage = &Package{
+ Funcs: []*Func{{Name: "F"}},
+ Types: []*Type{
+ {
+ Name: "G1",
+ Funcs: []*Func{{Name: "NewG1"}},
+ Methods: []*Func{
+ {Name: "M4", Recv: "G1", // TODO: synthesize a param for G1?
+ Orig: "s3", Level: 1},
+ {Name: "MG1", Recv: "G1[T]", Orig: "G1[T]", Level: 0},
+ {Name: "MG2", Recv: "*G1[U]", Orig: "*G1[U]", Level: 0},
+ },
+ },
+ {
+ Name: "G2",
+ Funcs: []*Func{{Name: "NewG2"}},
+ Methods: []*Func{
+ {Name: "MG3", Recv: "G2[T, U]", Orig: "G2[T, U]", Level: 0},
+ {Name: "MG4", Recv: "*G2[A, B]", Orig: "*G2[A, B]", Level: 0},
+ },
+ },
+ {
+ Name: "S1",
+ Funcs: []*Func{{Name: "NewS1"}, {Name: "NewS1p"}},
+ Methods: []*Func{
+ {Name: "M1", Recv: "S1", Orig: "S1", Level: 0},
+ {Name: "M2", Recv: "S1", Orig: "S1", Level: 0},
+ {Name: "M4", Recv: "S1", Orig: "s3", Level: 1},
+ {Name: "P1", Recv: "*S1", Orig: "*S1", Level: 0},
+ },
+ },
+ {
+ Name: "S2",
+ Methods: []*Func{
+ {Name: "M3", Recv: "S2", Orig: "S2", Level: 0},
+ },
+ },
+ },
+}
diff --git a/libgo/go/go/doc/example.go b/libgo/go/go/doc/example.go
index 274000c..0a880cd 100644
--- a/libgo/go/go/doc/example.go
+++ b/libgo/go/go/doc/example.go
@@ -44,13 +44,13 @@ type Example struct {
// identifiers from other packages (or predeclared identifiers, such as
// "int") and the test file does not include a dot import.
// - The entire test file is the example: the file contains exactly one
-// example function, zero test or benchmark functions, and at least one
-// top-level function, type, variable, or constant declaration other
-// than the example function.
+// example function, zero test, fuzz test, or benchmark function, and at
+// least one top-level function, type, variable, or constant declaration
+// other than the example function.
func Examples(testFiles ...*ast.File) []*Example {
var list []*Example
for _, file := range testFiles {
- hasTests := false // file contains tests or benchmarks
+ hasTests := false // file contains tests, fuzz test, or benchmarks
numDecl := 0 // number of non-import declarations in the file
var flist []*Example
for _, decl := range file.Decls {
@@ -64,7 +64,7 @@ func Examples(testFiles ...*ast.File) []*Example {
}
numDecl++
name := f.Name.Name
- if isTest(name, "Test") || isTest(name, "Benchmark") {
+ if isTest(name, "Test") || isTest(name, "Benchmark") || isTest(name, "Fuzz") {
hasTests = true
continue
}
@@ -133,9 +133,9 @@ func exampleOutput(b *ast.BlockStmt, comments []*ast.CommentGroup) (output strin
return "", false, false // no suitable comment found
}
-// isTest tells whether name looks like a test, example, or benchmark.
-// It is a Test (say) if there is a character after Test that is not a
-// lower-case letter. (We don't want Testiness.)
+// isTest tells whether name looks like a test, example, fuzz test, or
+// benchmark. It is a Test (say) if there is a character after Test that is not
+// a lower-case letter. (We don't want Testiness.)
func isTest(name, prefix string) bool {
if !strings.HasPrefix(name, prefix) {
return false
diff --git a/libgo/go/go/doc/example_test.go b/libgo/go/go/doc/example_test.go
index cf1b702..21b7129 100644
--- a/libgo/go/go/doc/example_test.go
+++ b/libgo/go/go/doc/example_test.go
@@ -307,6 +307,9 @@ func (X) TestBlah() {
func (X) BenchmarkFoo() {
}
+func (X) FuzzFoo() {
+}
+
func Example() {
fmt.Println("Hello, world!")
// Output: Hello, world!
@@ -326,6 +329,9 @@ func (X) TestBlah() {
func (X) BenchmarkFoo() {
}
+func (X) FuzzFoo() {
+}
+
func main() {
fmt.Println("Hello, world!")
}
diff --git a/libgo/go/go/doc/exports.go b/libgo/go/go/doc/exports.go
index 819c030..671c622 100644
--- a/libgo/go/go/doc/exports.go
+++ b/libgo/go/go/doc/exports.go
@@ -79,18 +79,15 @@ func hasExportedName(list []*ast.Ident) bool {
return false
}
-// removeErrorField removes anonymous fields named "error" from an interface.
-// This is called when "error" has been determined to be a local name,
-// not the predeclared type.
-//
-func removeErrorField(ityp *ast.InterfaceType) {
+// removeAnonymousField removes anonymous fields named name from an interface.
+func removeAnonymousField(name string, ityp *ast.InterfaceType) {
list := ityp.Methods.List // we know that ityp.Methods != nil
j := 0
for _, field := range list {
keepField := true
if n := len(field.Names); n == 0 {
// anonymous field
- if fname, _ := baseTypeName(field.Type); fname == "error" {
+ if fname, _ := baseTypeName(field.Type); fname == name {
keepField = false
}
}
@@ -119,16 +116,25 @@ func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList, ityp
for _, field := range list {
keepField := false
if n := len(field.Names); n == 0 {
- // anonymous field
+ // anonymous field or embedded type or union element
fname := r.recordAnonymousField(parent, field.Type)
- if token.IsExported(fname) {
- keepField = true
- } else if ityp != nil && fname == "error" {
- // possibly the predeclared error interface; keep
- // it for now but remember this interface so that
- // it can be fixed if error is also defined locally
- keepField = true
- r.remember(ityp)
+ if fname != "" {
+ if token.IsExported(fname) {
+ keepField = true
+ } else if ityp != nil && predeclaredTypes[fname] {
+ // possibly an embedded predeclared type; keep it for now but
+ // remember this interface so that it can be fixed if name is also
+ // defined locally
+ keepField = true
+ r.remember(fname, ityp)
+ }
+ } else {
+ // If we're operating on an interface, assume that this is an embedded
+ // type or union element.
+ //
+ // TODO(rfindley): consider traversing into approximation/unions
+ // elements to see if they are entirely unexported.
+ keepField = ityp != nil
}
} else {
field.Names = filterIdentList(field.Names)
@@ -172,6 +178,17 @@ func (r *reader) filterType(parent *namedType, typ ast.Expr) {
// nothing to do
case *ast.ParenExpr:
r.filterType(nil, t.X)
+ case *ast.StarExpr: // possibly an embedded type literal
+ r.filterType(nil, t.X)
+ case *ast.UnaryExpr:
+ if t.Op == token.TILDE { // approximation element
+ r.filterType(nil, t.X)
+ }
+ case *ast.BinaryExpr:
+ if t.Op == token.OR { // union
+ r.filterType(nil, t.X)
+ r.filterType(nil, t.Y)
+ }
case *ast.ArrayType:
r.filterType(nil, t.Elt)
case *ast.StructType:
@@ -179,6 +196,7 @@ func (r *reader) filterType(parent *namedType, typ ast.Expr) {
t.Incomplete = true
}
case *ast.FuncType:
+ r.filterParamList(t.TypeParams)
r.filterParamList(t.Params)
r.filterParamList(t.Results)
case *ast.InterfaceType:
@@ -219,12 +237,16 @@ func (r *reader) filterSpec(spec ast.Spec) bool {
}
}
case *ast.TypeSpec:
+ // Don't filter type parameters here, by analogy with function parameters
+ // which are not filtered for top-level function declarations.
if name := s.Name.Name; token.IsExported(name) {
r.filterType(r.lookupType(s.Name.Name), s.Type)
return true
- } else if name == "error" {
- // special case: remember that error is declared locally
- r.errorDecl = true
+ } else if IsPredeclared(name) {
+ if r.shadowedPredecl == nil {
+ r.shadowedPredecl = make(map[string]bool)
+ }
+ r.shadowedPredecl[name] = true
}
}
return false
diff --git a/libgo/go/go/doc/filter.go b/libgo/go/go/doc/filter.go
index a6f243f..9904da1 100644
--- a/libgo/go/go/doc/filter.go
+++ b/libgo/go/go/doc/filter.go
@@ -34,6 +34,8 @@ func matchDecl(d *ast.GenDecl, f Filter) bool {
if f(v.Name.Name) {
return true
}
+ // We don't match ordinary parameters in filterFuncs, so by analogy don't
+ // match type parameters here.
switch t := v.Type.(type) {
case *ast.StructType:
if matchFields(t.Fields, f) {
diff --git a/libgo/go/go/doc/headscan.go b/libgo/go/go/doc/headscan.go
index 28cb84f..320895e 100644
--- a/libgo/go/go/doc/headscan.go
+++ b/libgo/go/go/doc/headscan.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build ignore
-// +build ignore
/*
The headscan command extracts comment headings from package files;
@@ -23,10 +22,10 @@ import (
"go/doc"
"go/parser"
"go/token"
- "internal/lazyregexp"
"io/fs"
"os"
"path/filepath"
+ "regexp"
"runtime"
"strings"
)
@@ -37,7 +36,7 @@ var (
)
// ToHTML in comment.go assigns a (possibly blank) ID to each heading
-var html_h = lazyregexp.New(`<h3 id="[^"]*">`)
+var html_h = regexp.MustCompile(`<h3 id="[^"]*">`)
const html_endh = "</h3>\n"
@@ -49,19 +48,14 @@ func isGoFile(fi fs.FileInfo) bool {
func appendHeadings(list []string, comment string) []string {
var buf bytes.Buffer
doc.ToHTML(&buf, comment, nil)
- for s := buf.String(); ; {
+ for s := buf.String(); s != ""; {
loc := html_h.FindStringIndex(s)
if len(loc) == 0 {
break
}
- i := loc[1]
- j := strings.Index(s, html_endh)
- if j < 0 {
- list = append(list, s[i:]) // incorrect HTML
- break
- }
- list = append(list, s[i:j])
- s = s[j+len(html_endh):]
+ var inner string
+ inner, s, _ = strings.Cut(s[loc[1]:], html_endh)
+ list = append(list, inner)
}
return list
}
diff --git a/libgo/go/go/doc/reader.go b/libgo/go/go/doc/reader.go
index c277b35..de1d422 100644
--- a/libgo/go/go/doc/reader.go
+++ b/libgo/go/go/doc/reader.go
@@ -5,11 +5,13 @@
package doc
import (
+ "fmt"
"go/ast"
"go/token"
"internal/lazyregexp"
"sort"
"strconv"
+ "strings"
)
// ----------------------------------------------------------------------------
@@ -22,8 +24,8 @@ import (
//
type methodSet map[string]*Func
-// recvString returns a string representation of recv of the
-// form "T", "*T", or "BADRECV" (if not a proper receiver type).
+// recvString returns a string representation of recv of the form "T", "*T",
+// "T[A, ...]", "*T[A, ...]" or "BADRECV" (if not a proper receiver type).
//
func recvString(recv ast.Expr) string {
switch t := recv.(type) {
@@ -31,10 +33,34 @@ func recvString(recv ast.Expr) string {
return t.Name
case *ast.StarExpr:
return "*" + recvString(t.X)
+ case *ast.IndexExpr:
+ // Generic type with one parameter.
+ return fmt.Sprintf("%s[%s]", recvString(t.X), recvParam(t.Index))
+ case *ast.IndexListExpr:
+ // Generic type with multiple parameters.
+ if len(t.Indices) > 0 {
+ var b strings.Builder
+ b.WriteString(recvString(t.X))
+ b.WriteByte('[')
+ b.WriteString(recvParam(t.Indices[0]))
+ for _, e := range t.Indices[1:] {
+ b.WriteString(", ")
+ b.WriteString(recvParam(e))
+ }
+ b.WriteByte(']')
+ return b.String()
+ }
}
return "BADRECV"
}
+func recvParam(p ast.Expr) string {
+ if id, ok := p.(*ast.Ident); ok {
+ return id.Name
+ }
+ return "BADPARAM"
+}
+
// set creates the corresponding Func for f and adds it to mset.
// If there are multiple f's with the same name, set keeps the first
// one with documentation; conflicts are ignored. The boolean
@@ -101,6 +127,10 @@ func baseTypeName(x ast.Expr) (name string, imported bool) {
switch t := x.(type) {
case *ast.Ident:
return t.Name, false
+ case *ast.IndexExpr:
+ return baseTypeName(t.X)
+ case *ast.IndexListExpr:
+ return baseTypeName(t.X)
case *ast.SelectorExpr:
if _, ok := t.X.(*ast.Ident); ok {
// only possible for qualified type names;
@@ -112,7 +142,7 @@ func baseTypeName(x ast.Expr) (name string, imported bool) {
case *ast.StarExpr:
return baseTypeName(t.X)
}
- return
+ return "", false
}
// An embeddedSet describes a set of embedded types.
@@ -163,9 +193,9 @@ type reader struct {
types map[string]*namedType
funcs methodSet
- // support for package-local error type declarations
- errorDecl bool // if set, type "error" was declared locally
- fixlist []*ast.InterfaceType // list of interfaces containing anonymous field "error"
+ // support for package-local shadowing of predeclared types
+ shadowedPredecl map[string]bool
+ fixmap map[string][]*ast.InterfaceType
}
func (r *reader) isVisible(name string) bool {
@@ -224,8 +254,11 @@ func (r *reader) readDoc(comment *ast.CommentGroup) {
r.doc += "\n" + text
}
-func (r *reader) remember(typ *ast.InterfaceType) {
- r.fixlist = append(r.fixlist, typ)
+func (r *reader) remember(predecl string, typ *ast.InterfaceType) {
+ if r.fixmap == nil {
+ r.fixmap = make(map[string][]*ast.InterfaceType)
+ }
+ r.fixmap[predecl] = append(r.fixmap[predecl], typ)
}
func specNames(specs []ast.Spec) []string {
@@ -418,6 +451,11 @@ func (r *reader) readFunc(fun *ast.FuncDecl) {
factoryType = t.Elt
}
if n, imp := baseTypeName(factoryType); !imp && r.isVisible(n) && !r.isPredeclared(n) {
+ if lookupTypeParam(n, fun.Type.TypeParams) != nil {
+ // Issue #49477: don't associate fun with its type parameter result.
+ // A type parameter is not a defined type.
+ continue
+ }
if t := r.lookupType(n); t != nil {
typ = t
numResultTypes++
@@ -439,6 +477,22 @@ func (r *reader) readFunc(fun *ast.FuncDecl) {
r.funcs.set(fun, r.mode&PreserveAST != 0)
}
+// lookupTypeParam searches for type parameters named name within the tparams
+// field list, returning the relevant identifier if found, or nil if not.
+func lookupTypeParam(name string, tparams *ast.FieldList) *ast.Ident {
+ if tparams == nil {
+ return nil
+ }
+ for _, field := range tparams.List {
+ for _, id := range field.Names {
+ if id.Name == name {
+ return id
+ }
+ }
+ }
+ return nil
+}
+
var (
noteMarker = `([A-Z][A-Z]+)\(([^)]+)\):?` // MARKER(uid), MARKER at least 2 chars, uid at least 1 char
noteMarkerRx = lazyregexp.New(`^[ \t]*` + noteMarker) // MARKER(uid) at text start
@@ -679,10 +733,11 @@ func (r *reader) computeMethodSets() {
}
}
- // if error was declared locally, don't treat it as exported field anymore
- if r.errorDecl {
- for _, ityp := range r.fixlist {
- removeErrorField(ityp)
+ // For any predeclared names that are declared locally, don't treat them as
+ // exported fields anymore.
+ for predecl := range r.shadowedPredecl {
+ for _, ityp := range r.fixmap[predecl] {
+ removeAnonymousField(predecl, ityp)
}
}
}
@@ -869,6 +924,7 @@ func IsPredeclared(s string) bool {
}
var predeclaredTypes = map[string]bool{
+ "any": true,
"bool": true,
"byte": true,
"complex64": true,
diff --git a/libgo/go/go/doc/testdata/benchmark.go b/libgo/go/go/doc/testdata/benchmark.go
index 1d581f0..d27bf11 100644
--- a/libgo/go/go/doc/testdata/benchmark.go
+++ b/libgo/go/go/doc/testdata/benchmark.go
@@ -232,7 +232,7 @@ func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks [
runtime.GOMAXPROCS(procs)
b := &B{
common: common{
- signal: make(chan interface{}),
+ signal: make(chan any),
},
benchmark: Benchmark,
}
@@ -285,7 +285,7 @@ func (b *B) trimOutput() {
func Benchmark(f func(b *B)) BenchmarkResult {
b := &B{
common: common{
- signal: make(chan interface{}),
+ signal: make(chan any),
},
benchmark: InternalBenchmark{"", f},
}
diff --git a/libgo/go/go/doc/testdata/generics.0.golden b/libgo/go/go/doc/testdata/generics.0.golden
new file mode 100644
index 0000000..91c874c
--- /dev/null
+++ b/libgo/go/go/doc/testdata/generics.0.golden
@@ -0,0 +1,76 @@
+// Package generics contains the new syntax supporting generic ...
+PACKAGE generics
+
+IMPORTPATH
+ testdata/generics
+
+FILENAMES
+ testdata/generics.go
+
+FUNCTIONS
+ // AnotherFunc has an implicit constraint interface. Neither type ...
+ func AnotherFunc[T ~struct{ f int }](_ struct{ f int })
+
+ // Func has an instantiated constraint.
+ func Func[T Constraint[string, Type[int]]]()
+
+ // Single is not a factory function.
+ func Single[T any]() *T
+
+ // Slice is not a factory function.
+ func Slice[T any]() []T
+
+
+TYPES
+ // AFuncType demonstrates filtering of parameters and type ...
+ type AFuncType[T ~struct{ f int }] func(_ struct {
+ // contains filtered or unexported fields
+ })
+
+ // Constraint is a constraint interface with two type parameters.
+ type Constraint[P, Q interface{ string | ~int | Type[int] }] interface {
+ ~int | ~byte | Type[string]
+ M() P
+ }
+
+ // NewEmbeddings demonstrates how we filter the new embedded ...
+ type NewEmbeddings interface {
+ string // should not be filtered
+
+ struct {
+ // contains filtered or unexported fields
+ }
+ ~struct {
+ // contains filtered or unexported fields
+ }
+ *struct {
+ // contains filtered or unexported fields
+ }
+ struct {
+ // contains filtered or unexported fields
+ } | ~struct {
+ // contains filtered or unexported fields
+ }
+ // contains filtered or unexported methods
+ }
+
+ // Parameterized types should be shown.
+ type Type[P any] struct {
+ Field P
+ }
+
+ // Variables with an instantiated type should be shown.
+ var X Type[int]
+
+ // Constructors for parameterized types should be shown.
+ func Constructor[lowerCase any]() Type[lowerCase]
+
+ // MethodA uses a different name for its receiver type parameter.
+ func (t Type[A]) MethodA(p A)
+
+ // MethodB has a blank receiver type parameter.
+ func (t Type[_]) MethodB()
+
+ // MethodC has a lower-case receiver type parameter.
+ func (t Type[c]) MethodC()
+
diff --git a/libgo/go/go/doc/testdata/generics.1.golden b/libgo/go/go/doc/testdata/generics.1.golden
new file mode 100644
index 0000000..923a4ce
--- /dev/null
+++ b/libgo/go/go/doc/testdata/generics.1.golden
@@ -0,0 +1,66 @@
+// Package generics contains the new syntax supporting generic ...
+PACKAGE generics
+
+IMPORTPATH
+ testdata/generics
+
+FILENAMES
+ testdata/generics.go
+
+FUNCTIONS
+ // AnotherFunc has an implicit constraint interface. Neither type ...
+ func AnotherFunc[T ~struct{ f int }](_ struct{ f int })
+
+ // Func has an instantiated constraint.
+ func Func[T Constraint[string, Type[int]]]()
+
+ // Single is not a factory function.
+ func Single[T any]() *T
+
+ // Slice is not a factory function.
+ func Slice[T any]() []T
+
+
+TYPES
+ // AFuncType demonstrates filtering of parameters and type ...
+ type AFuncType[T ~struct{ f int }] func(_ struct{ f int })
+
+ // Constraint is a constraint interface with two type parameters.
+ type Constraint[P, Q interface{ string | ~int | Type[int] }] interface {
+ ~int | ~byte | Type[string]
+ M() P
+ }
+
+ // NewEmbeddings demonstrates how we filter the new embedded ...
+ type NewEmbeddings interface {
+ string // should not be filtered
+ int16
+ struct{ f int }
+ ~struct{ f int }
+ *struct{ f int }
+ struct{ f int } | ~struct{ f int }
+ }
+
+ // Parameterized types should be shown.
+ type Type[P any] struct {
+ Field P
+ }
+
+ // Variables with an instantiated type should be shown.
+ var X Type[int]
+
+ // Constructors for parameterized types should be shown.
+ func Constructor[lowerCase any]() Type[lowerCase]
+
+ // MethodA uses a different name for its receiver type parameter.
+ func (t Type[A]) MethodA(p A)
+
+ // MethodB has a blank receiver type parameter.
+ func (t Type[_]) MethodB()
+
+ // MethodC has a lower-case receiver type parameter.
+ func (t Type[c]) MethodC()
+
+ // int16 shadows the predeclared type int16.
+ type int16 int
+
diff --git a/libgo/go/go/doc/testdata/generics.2.golden b/libgo/go/go/doc/testdata/generics.2.golden
new file mode 100644
index 0000000..91c874c
--- /dev/null
+++ b/libgo/go/go/doc/testdata/generics.2.golden
@@ -0,0 +1,76 @@
+// Package generics contains the new syntax supporting generic ...
+PACKAGE generics
+
+IMPORTPATH
+ testdata/generics
+
+FILENAMES
+ testdata/generics.go
+
+FUNCTIONS
+ // AnotherFunc has an implicit constraint interface. Neither type ...
+ func AnotherFunc[T ~struct{ f int }](_ struct{ f int })
+
+ // Func has an instantiated constraint.
+ func Func[T Constraint[string, Type[int]]]()
+
+ // Single is not a factory function.
+ func Single[T any]() *T
+
+ // Slice is not a factory function.
+ func Slice[T any]() []T
+
+
+TYPES
+ // AFuncType demonstrates filtering of parameters and type ...
+ type AFuncType[T ~struct{ f int }] func(_ struct {
+ // contains filtered or unexported fields
+ })
+
+ // Constraint is a constraint interface with two type parameters.
+ type Constraint[P, Q interface{ string | ~int | Type[int] }] interface {
+ ~int | ~byte | Type[string]
+ M() P
+ }
+
+ // NewEmbeddings demonstrates how we filter the new embedded ...
+ type NewEmbeddings interface {
+ string // should not be filtered
+
+ struct {
+ // contains filtered or unexported fields
+ }
+ ~struct {
+ // contains filtered or unexported fields
+ }
+ *struct {
+ // contains filtered or unexported fields
+ }
+ struct {
+ // contains filtered or unexported fields
+ } | ~struct {
+ // contains filtered or unexported fields
+ }
+ // contains filtered or unexported methods
+ }
+
+ // Parameterized types should be shown.
+ type Type[P any] struct {
+ Field P
+ }
+
+ // Variables with an instantiated type should be shown.
+ var X Type[int]
+
+ // Constructors for parameterized types should be shown.
+ func Constructor[lowerCase any]() Type[lowerCase]
+
+ // MethodA uses a different name for its receiver type parameter.
+ func (t Type[A]) MethodA(p A)
+
+ // MethodB has a blank receiver type parameter.
+ func (t Type[_]) MethodB()
+
+ // MethodC has a lower-case receiver type parameter.
+ func (t Type[c]) MethodC()
+
diff --git a/libgo/go/go/doc/testdata/generics.go b/libgo/go/go/doc/testdata/generics.go
new file mode 100644
index 0000000..ba7187e
--- /dev/null
+++ b/libgo/go/go/doc/testdata/generics.go
@@ -0,0 +1,74 @@
+// 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.
+
+// Package generics contains the new syntax supporting generic programming in
+// Go.
+package generics
+
+// Variables with an instantiated type should be shown.
+var X Type[int]
+
+// Parameterized types should be shown.
+type Type[P any] struct {
+ Field P
+}
+
+// Constructors for parameterized types should be shown.
+func Constructor[lowerCase any]() Type[lowerCase] {
+ return Type[lowerCase]{}
+}
+
+// MethodA uses a different name for its receiver type parameter.
+func (t Type[A]) MethodA(p A) {}
+
+// MethodB has a blank receiver type parameter.
+func (t Type[_]) MethodB() {}
+
+// MethodC has a lower-case receiver type parameter.
+func (t Type[c]) MethodC() {}
+
+// Constraint is a constraint interface with two type parameters.
+type Constraint[P, Q interface{ string | ~int | Type[int] }] interface {
+ ~int | ~byte | Type[string]
+ M() P
+}
+
+// int16 shadows the predeclared type int16.
+type int16 int
+
+// NewEmbeddings demonstrates how we filter the new embedded elements.
+type NewEmbeddings interface {
+ string // should not be filtered
+ int16
+ struct{ f int }
+ ~struct{ f int }
+ *struct{ f int }
+ struct{ f int } | ~struct{ f int }
+}
+
+// Func has an instantiated constraint.
+func Func[T Constraint[string, Type[int]]]() {}
+
+// AnotherFunc has an implicit constraint interface.
+//
+// Neither type parameters nor regular parameters should be filtered.
+func AnotherFunc[T ~struct{ f int }](_ struct{ f int }) {}
+
+// AFuncType demonstrates filtering of parameters and type parameters. Here we
+// don't filter type parameters (to be consistent with function declarations),
+// but DO filter the RHS.
+type AFuncType[T ~struct{ f int }] func(_ struct{ f int })
+
+// See issue #49477: type parameters should not be interpreted as named types
+// for the purpose of determining whether a function is a factory function.
+
+// Slice is not a factory function.
+func Slice[T any]() []T {
+ return nil
+}
+
+// Single is not a factory function.
+func Single[T any]() *T {
+ return nil
+}
diff --git a/libgo/go/go/doc/testdata/testing.0.golden b/libgo/go/go/doc/testdata/testing.0.golden
index 83cf37c..61dac8b 100644
--- a/libgo/go/go/doc/testdata/testing.0.golden
+++ b/libgo/go/go/doc/testdata/testing.0.golden
@@ -46,10 +46,10 @@ TYPES
}
// Error is equivalent to Log() followed by Fail().
- func (c *B) Error(args ...interface{})
+ func (c *B) Error(args ...any)
// Errorf is equivalent to Logf() followed by Fail().
- func (c *B) Errorf(format string, args ...interface{})
+ func (c *B) Errorf(format string, args ...any)
// Fail marks the function as having failed but continues ...
func (c *B) Fail()
@@ -61,16 +61,16 @@ TYPES
func (c *B) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
- func (c *B) Fatal(args ...interface{})
+ func (c *B) Fatal(args ...any)
// Fatalf is equivalent to Logf() followed by FailNow().
- func (c *B) Fatalf(format string, args ...interface{})
+ func (c *B) Fatalf(format string, args ...any)
// Log formats its arguments using default formatting, analogous ...
- func (c *B) Log(args ...interface{})
+ func (c *B) Log(args ...any)
// Logf formats its arguments according to the format, analogous ...
- func (c *B) Logf(format string, args ...interface{})
+ func (c *B) Logf(format string, args ...any)
// ResetTimer sets the elapsed benchmark time to zero. It does not ...
func (b *B) ResetTimer()
@@ -125,10 +125,10 @@ TYPES
}
// Error is equivalent to Log() followed by Fail().
- func (c *T) Error(args ...interface{})
+ func (c *T) Error(args ...any)
// Errorf is equivalent to Logf() followed by Fail().
- func (c *T) Errorf(format string, args ...interface{})
+ func (c *T) Errorf(format string, args ...any)
// Fail marks the function as having failed but continues ...
func (c *T) Fail()
@@ -140,16 +140,16 @@ TYPES
func (c *T) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
- func (c *T) Fatal(args ...interface{})
+ func (c *T) Fatal(args ...any)
// Fatalf is equivalent to Logf() followed by FailNow().
- func (c *T) Fatalf(format string, args ...interface{})
+ func (c *T) Fatalf(format string, args ...any)
// Log formats its arguments using default formatting, analogous ...
- func (c *T) Log(args ...interface{})
+ func (c *T) Log(args ...any)
// Logf formats its arguments according to the format, analogous ...
- func (c *T) Logf(format string, args ...interface{})
+ func (c *T) Logf(format string, args ...any)
// Parallel signals that this test is to be run in parallel with ...
func (t *T) Parallel()
diff --git a/libgo/go/go/doc/testdata/testing.1.golden b/libgo/go/go/doc/testdata/testing.1.golden
index b9d1451..1655af1 100644
--- a/libgo/go/go/doc/testdata/testing.1.golden
+++ b/libgo/go/go/doc/testdata/testing.1.golden
@@ -119,10 +119,10 @@ TYPES
}
// Error is equivalent to Log() followed by Fail().
- func (c *B) Error(args ...interface{})
+ func (c *B) Error(args ...any)
// Errorf is equivalent to Logf() followed by Fail().
- func (c *B) Errorf(format string, args ...interface{})
+ func (c *B) Errorf(format string, args ...any)
// Fail marks the function as having failed but continues ...
func (c *B) Fail()
@@ -134,16 +134,16 @@ TYPES
func (c *B) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
- func (c *B) Fatal(args ...interface{})
+ func (c *B) Fatal(args ...any)
// Fatalf is equivalent to Logf() followed by FailNow().
- func (c *B) Fatalf(format string, args ...interface{})
+ func (c *B) Fatalf(format string, args ...any)
// Log formats its arguments using default formatting, analogous ...
- func (c *B) Log(args ...interface{})
+ func (c *B) Log(args ...any)
// Logf formats its arguments according to the format, analogous ...
- func (c *B) Logf(format string, args ...interface{})
+ func (c *B) Logf(format string, args ...any)
// ResetTimer sets the elapsed benchmark time to zero. It does not ...
func (b *B) ResetTimer()
@@ -221,10 +221,10 @@ TYPES
}
// Error is equivalent to Log() followed by Fail().
- func (c *T) Error(args ...interface{})
+ func (c *T) Error(args ...any)
// Errorf is equivalent to Logf() followed by Fail().
- func (c *T) Errorf(format string, args ...interface{})
+ func (c *T) Errorf(format string, args ...any)
// Fail marks the function as having failed but continues ...
func (c *T) Fail()
@@ -236,16 +236,16 @@ TYPES
func (c *T) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
- func (c *T) Fatal(args ...interface{})
+ func (c *T) Fatal(args ...any)
// Fatalf is equivalent to Logf() followed by FailNow().
- func (c *T) Fatalf(format string, args ...interface{})
+ func (c *T) Fatalf(format string, args ...any)
// Log formats its arguments using default formatting, analogous ...
- func (c *T) Log(args ...interface{})
+ func (c *T) Log(args ...any)
// Logf formats its arguments according to the format, analogous ...
- func (c *T) Logf(format string, args ...interface{})
+ func (c *T) Logf(format string, args ...any)
// Parallel signals that this test is to be run in parallel with ...
func (t *T) Parallel()
@@ -262,15 +262,15 @@ TYPES
failed bool // Test or benchmark has failed.
start time.Time // Time test or benchmark started
duration time.Duration
- self interface{} // To be sent on signal channel when done.
- signal chan interface{} // Output for serial tests.
+ self any // To be sent on signal channel when done.
+ signal chan any // Output for serial tests.
}
// Error is equivalent to Log() followed by Fail().
- func (c *common) Error(args ...interface{})
+ func (c *common) Error(args ...any)
// Errorf is equivalent to Logf() followed by Fail().
- func (c *common) Errorf(format string, args ...interface{})
+ func (c *common) Errorf(format string, args ...any)
// Fail marks the function as having failed but continues ...
func (c *common) Fail()
@@ -282,16 +282,16 @@ TYPES
func (c *common) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
- func (c *common) Fatal(args ...interface{})
+ func (c *common) Fatal(args ...any)
// Fatalf is equivalent to Logf() followed by FailNow().
- func (c *common) Fatalf(format string, args ...interface{})
+ func (c *common) Fatalf(format string, args ...any)
// Log formats its arguments using default formatting, analogous ...
- func (c *common) Log(args ...interface{})
+ func (c *common) Log(args ...any)
// Logf formats its arguments according to the format, analogous ...
- func (c *common) Logf(format string, args ...interface{})
+ func (c *common) Logf(format string, args ...any)
// log generates the output. It's always at the same stack depth.
func (c *common) log(s string)
diff --git a/libgo/go/go/doc/testdata/testing.2.golden b/libgo/go/go/doc/testdata/testing.2.golden
index 83cf37c..61dac8b 100644
--- a/libgo/go/go/doc/testdata/testing.2.golden
+++ b/libgo/go/go/doc/testdata/testing.2.golden
@@ -46,10 +46,10 @@ TYPES
}
// Error is equivalent to Log() followed by Fail().
- func (c *B) Error(args ...interface{})
+ func (c *B) Error(args ...any)
// Errorf is equivalent to Logf() followed by Fail().
- func (c *B) Errorf(format string, args ...interface{})
+ func (c *B) Errorf(format string, args ...any)
// Fail marks the function as having failed but continues ...
func (c *B) Fail()
@@ -61,16 +61,16 @@ TYPES
func (c *B) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
- func (c *B) Fatal(args ...interface{})
+ func (c *B) Fatal(args ...any)
// Fatalf is equivalent to Logf() followed by FailNow().
- func (c *B) Fatalf(format string, args ...interface{})
+ func (c *B) Fatalf(format string, args ...any)
// Log formats its arguments using default formatting, analogous ...
- func (c *B) Log(args ...interface{})
+ func (c *B) Log(args ...any)
// Logf formats its arguments according to the format, analogous ...
- func (c *B) Logf(format string, args ...interface{})
+ func (c *B) Logf(format string, args ...any)
// ResetTimer sets the elapsed benchmark time to zero. It does not ...
func (b *B) ResetTimer()
@@ -125,10 +125,10 @@ TYPES
}
// Error is equivalent to Log() followed by Fail().
- func (c *T) Error(args ...interface{})
+ func (c *T) Error(args ...any)
// Errorf is equivalent to Logf() followed by Fail().
- func (c *T) Errorf(format string, args ...interface{})
+ func (c *T) Errorf(format string, args ...any)
// Fail marks the function as having failed but continues ...
func (c *T) Fail()
@@ -140,16 +140,16 @@ TYPES
func (c *T) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
- func (c *T) Fatal(args ...interface{})
+ func (c *T) Fatal(args ...any)
// Fatalf is equivalent to Logf() followed by FailNow().
- func (c *T) Fatalf(format string, args ...interface{})
+ func (c *T) Fatalf(format string, args ...any)
// Log formats its arguments using default formatting, analogous ...
- func (c *T) Log(args ...interface{})
+ func (c *T) Log(args ...any)
// Logf formats its arguments according to the format, analogous ...
- func (c *T) Logf(format string, args ...interface{})
+ func (c *T) Logf(format string, args ...any)
// Parallel signals that this test is to be run in parallel with ...
func (t *T) Parallel()
diff --git a/libgo/go/go/doc/testdata/testing.go b/libgo/go/go/doc/testdata/testing.go
index 52810f7..80238df 100644
--- a/libgo/go/go/doc/testdata/testing.go
+++ b/libgo/go/go/doc/testdata/testing.go
@@ -77,8 +77,8 @@ type common struct {
failed bool // Test or benchmark has failed.
start time.Time // Time test or benchmark started
duration time.Duration
- self interface{} // To be sent on signal channel when done.
- signal chan interface{} // Output for serial tests.
+ self any // To be sent on signal channel when done.
+ signal chan any // Output for serial tests.
}
// Short reports whether the -test.short flag is set.
@@ -167,32 +167,32 @@ func (c *common) log(s string) {
// Log formats its arguments using default formatting, analogous to Println(),
// and records the text in the error log.
-func (c *common) Log(args ...interface{}) { c.log(fmt.Sprintln(args...)) }
+func (c *common) Log(args ...any) { c.log(fmt.Sprintln(args...)) }
// Logf formats its arguments according to the format, analogous to Printf(),
// and records the text in the error log.
-func (c *common) Logf(format string, args ...interface{}) { c.log(fmt.Sprintf(format, args...)) }
+func (c *common) Logf(format string, args ...any) { c.log(fmt.Sprintf(format, args...)) }
// Error is equivalent to Log() followed by Fail().
-func (c *common) Error(args ...interface{}) {
+func (c *common) Error(args ...any) {
c.log(fmt.Sprintln(args...))
c.Fail()
}
// Errorf is equivalent to Logf() followed by Fail().
-func (c *common) Errorf(format string, args ...interface{}) {
+func (c *common) Errorf(format string, args ...any) {
c.log(fmt.Sprintf(format, args...))
c.Fail()
}
// Fatal is equivalent to Log() followed by FailNow().
-func (c *common) Fatal(args ...interface{}) {
+func (c *common) Fatal(args ...any) {
c.log(fmt.Sprintln(args...))
c.FailNow()
}
// Fatalf is equivalent to Logf() followed by FailNow().
-func (c *common) Fatalf(format string, args ...interface{}) {
+func (c *common) Fatalf(format string, args ...any) {
c.log(fmt.Sprintf(format, args...))
c.FailNow()
}
@@ -269,7 +269,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT
// If all tests pump to the same channel, a bug can occur where a test
// kicks off a goroutine that Fails, yet the test still delivers a completion signal,
// which skews the counting.
- var collector = make(chan interface{})
+ var collector = make(chan any)
numParallel := 0
startParallel := make(chan bool)
@@ -289,7 +289,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT
}
t := &T{
common: common{
- signal: make(chan interface{}),
+ signal: make(chan any),
},
name: testName,
startParallel: startParallel,
diff --git a/libgo/go/go/format/format.go b/libgo/go/go/format/format.go
index a603d96..ea8dd20 100644
--- a/libgo/go/go/format/format.go
+++ b/libgo/go/go/format/format.go
@@ -51,7 +51,7 @@ const parserMode = parser.ParseComments
// The function may return early (before the entire result is written)
// and return a formatting error, for instance due to an incorrect AST.
//
-func Node(dst io.Writer, fset *token.FileSet, node interface{}) error {
+func Node(dst io.Writer, fset *token.FileSet, node any) error {
// Determine if we have a complete source file (file != nil).
var file *ast.File
var cnode *printer.CommentedNode
diff --git a/libgo/go/go/importer/importer_test.go b/libgo/go/go/importer/importer_test.go
index 0f5121d..27c4aa7 100644
--- a/libgo/go/go/importer/importer_test.go
+++ b/libgo/go/go/importer/importer_test.go
@@ -24,8 +24,7 @@ func TestForCompiler(t *testing.T) {
t.Fatalf("go list %s: %v\n%s", thePackage, err, out)
}
target := strings.TrimSpace(string(out))
- i := strings.Index(target, ":")
- compiler, target := target[:i], target[i+1:]
+ compiler, target, _ := strings.Cut(target, ":")
if !strings.HasSuffix(target, ".a") {
t.Fatalf("unexpected package %s target %q (not *.a)", thePackage, target)
}
diff --git a/libgo/go/go/internal/gccgoimporter/parser.go b/libgo/go/go/internal/gccgoimporter/parser.go
index 1b1d07d..48335fa 100644
--- a/libgo/go/go/internal/gccgoimporter/parser.go
+++ b/libgo/go/go/internal/gccgoimporter/parser.go
@@ -80,7 +80,7 @@ func (e importError) Error() string {
return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err)
}
-func (p *parser) error(err interface{}) {
+func (p *parser) error(err any) {
if s, ok := err.(string); ok {
err = errors.New(s)
}
@@ -88,7 +88,7 @@ func (p *parser) error(err interface{}) {
panic(importError{p.scanner.Pos(), err.(error)})
}
-func (p *parser) errorf(format string, args ...interface{}) {
+func (p *parser) errorf(format string, args ...any) {
p.error(fmt.Errorf(format, args...))
}
@@ -474,7 +474,7 @@ func (p *parser) reserve(n int) {
// used to resolve named types, or it can be a *types.Pointer,
// used to resolve pointers to named types in case they are referenced
// by embedded fields.
-func (p *parser) update(t types.Type, nlist []interface{}) {
+func (p *parser) update(t types.Type, nlist []any) {
if t == reserved {
p.errorf("internal error: update(%v) invoked on reserved", nlist)
}
@@ -509,7 +509,7 @@ func (p *parser) update(t types.Type, nlist []interface{}) {
// NamedType = TypeName [ "=" ] Type { Method } .
// TypeName = ExportedName .
// Method = "func" "(" Param ")" Name ParamList ResultList [InlineBody] ";" .
-func (p *parser) parseNamedType(nlist []interface{}) types.Type {
+func (p *parser) parseNamedType(nlist []any) types.Type {
pkg, name := p.parseExportedName()
scope := pkg.Scope()
obj := scope.Lookup(name)
@@ -599,7 +599,7 @@ func (p *parser) parseNamedType(nlist []interface{}) types.Type {
p.skipInlineBody()
p.expectEOL()
- sig := types.NewSignature(receiver, params, results, isVariadic)
+ sig := types.NewSignatureType(receiver, nil, nil, params, results, isVariadic)
nt.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig))
}
}
@@ -626,7 +626,7 @@ func (p *parser) parseInt() int {
}
// ArrayOrSliceType = "[" [ int ] "]" Type .
-func (p *parser) parseArrayOrSliceType(pkg *types.Package, nlist []interface{}) types.Type {
+func (p *parser) parseArrayOrSliceType(pkg *types.Package, nlist []any) types.Type {
p.expect('[')
if p.tok == ']' {
p.next()
@@ -649,7 +649,7 @@ func (p *parser) parseArrayOrSliceType(pkg *types.Package, nlist []interface{})
}
// MapType = "map" "[" Type "]" Type .
-func (p *parser) parseMapType(pkg *types.Package, nlist []interface{}) types.Type {
+func (p *parser) parseMapType(pkg *types.Package, nlist []any) types.Type {
p.expectKeyword("map")
t := new(types.Map)
@@ -665,7 +665,7 @@ func (p *parser) parseMapType(pkg *types.Package, nlist []interface{}) types.Typ
}
// ChanType = "chan" ["<-" | "-<"] Type .
-func (p *parser) parseChanType(pkg *types.Package, nlist []interface{}) types.Type {
+func (p *parser) parseChanType(pkg *types.Package, nlist []any) types.Type {
p.expectKeyword("chan")
t := new(types.Chan)
@@ -692,7 +692,7 @@ func (p *parser) parseChanType(pkg *types.Package, nlist []interface{}) types.Ty
}
// StructType = "struct" "{" { Field } "}" .
-func (p *parser) parseStructType(pkg *types.Package, nlist []interface{}) types.Type {
+func (p *parser) parseStructType(pkg *types.Package, nlist []any) types.Type {
p.expectKeyword("struct")
t := new(types.Struct)
@@ -759,14 +759,14 @@ func (p *parser) parseResultList(pkg *types.Package) *types.Tuple {
}
// FunctionType = ParamList ResultList .
-func (p *parser) parseFunctionType(pkg *types.Package, nlist []interface{}) *types.Signature {
+func (p *parser) parseFunctionType(pkg *types.Package, nlist []any) *types.Signature {
t := new(types.Signature)
p.update(t, nlist)
params, isVariadic := p.parseParamList(pkg)
results := p.parseResultList(pkg)
- *t = *types.NewSignature(nil, params, results, isVariadic)
+ *t = *types.NewSignatureType(nil, nil, nil, params, results, isVariadic)
return t
}
@@ -799,7 +799,7 @@ func (p *parser) parseFunc(pkg *types.Package) *types.Func {
}
// InterfaceType = "interface" "{" { ("?" Type | Func) ";" } "}" .
-func (p *parser) parseInterfaceType(pkg *types.Package, nlist []interface{}) types.Type {
+func (p *parser) parseInterfaceType(pkg *types.Package, nlist []any) types.Type {
p.expectKeyword("interface")
t := new(types.Interface)
@@ -828,7 +828,7 @@ func (p *parser) parseInterfaceType(pkg *types.Package, nlist []interface{}) typ
}
// PointerType = "*" ("any" | Type) .
-func (p *parser) parsePointerType(pkg *types.Package, nlist []interface{}) types.Type {
+func (p *parser) parsePointerType(pkg *types.Package, nlist []any) types.Type {
p.expect('*')
if p.tok == scanner.Ident {
p.expectKeyword("any")
@@ -846,7 +846,7 @@ func (p *parser) parsePointerType(pkg *types.Package, nlist []interface{}) types
}
// TypeSpec = NamedType | MapType | ChanType | StructType | InterfaceType | PointerType | ArrayOrSliceType | FunctionType .
-func (p *parser) parseTypeSpec(pkg *types.Package, nlist []interface{}) types.Type {
+func (p *parser) parseTypeSpec(pkg *types.Package, nlist []any) types.Type {
switch p.tok {
case scanner.String:
return p.parseNamedType(nlist)
@@ -935,14 +935,14 @@ func lookupBuiltinType(typ int) types.Type {
//
// parseType updates the type map to t for all type numbers n.
//
-func (p *parser) parseType(pkg *types.Package, n ...interface{}) types.Type {
+func (p *parser) parseType(pkg *types.Package, n ...any) types.Type {
p.expect('<')
t, _ := p.parseTypeAfterAngle(pkg, n...)
return t
}
// (*parser).Type after reading the "<".
-func (p *parser) parseTypeAfterAngle(pkg *types.Package, n ...interface{}) (t types.Type, n1 int) {
+func (p *parser) parseTypeAfterAngle(pkg *types.Package, n ...any) (t types.Type, n1 int) {
p.expectKeyword("type")
n1 = 0
@@ -985,7 +985,7 @@ func (p *parser) parseTypeAfterAngle(pkg *types.Package, n ...interface{}) (t ty
// parseTypeExtended is identical to parseType, but if the type in
// question is a saved type, returns the index as well as the type
// pointer (index returned is zero if we parsed a builtin).
-func (p *parser) parseTypeExtended(pkg *types.Package, n ...interface{}) (t types.Type, n1 int) {
+func (p *parser) parseTypeExtended(pkg *types.Package, n ...any) (t types.Type, n1 int) {
p.expect('<')
t, n1 = p.parseTypeAfterAngle(pkg, n...)
return
@@ -1072,7 +1072,7 @@ func (p *parser) parseTypes(pkg *types.Package) {
}
// parseSavedType parses one saved type definition.
-func (p *parser) parseSavedType(pkg *types.Package, i int, nlist []interface{}) {
+func (p *parser) parseSavedType(pkg *types.Package, i int, nlist []any) {
defer func(s *scanner.Scanner, tok rune, lit string) {
p.scanner = s
p.tok = tok
diff --git a/libgo/go/go/internal/gccgoimporter/testdata/escapeinfo.gox b/libgo/go/go/internal/gccgoimporter/testdata/escapeinfo.gox
index 1db8156..94ce039 100644
--- a/libgo/go/go/internal/gccgoimporter/testdata/escapeinfo.gox
+++ b/libgo/go/go/internal/gccgoimporter/testdata/escapeinfo.gox
Binary files differ
diff --git a/libgo/go/go/internal/gccgoimporter/testdata/time.gox b/libgo/go/go/internal/gccgoimporter/testdata/time.gox
index 80c2dbc..a6822ea 100644
--- a/libgo/go/go/internal/gccgoimporter/testdata/time.gox
+++ b/libgo/go/go/internal/gccgoimporter/testdata/time.gox
Binary files differ
diff --git a/libgo/go/go/internal/gccgoimporter/testdata/unicode.gox b/libgo/go/go/internal/gccgoimporter/testdata/unicode.gox
index e70e539..ae1a6f7 100644
--- a/libgo/go/go/internal/gccgoimporter/testdata/unicode.gox
+++ b/libgo/go/go/internal/gccgoimporter/testdata/unicode.gox
Binary files differ
diff --git a/libgo/go/go/internal/gccgoimporter/testdata/v1reflect.gox b/libgo/go/go/internal/gccgoimporter/testdata/v1reflect.gox
index ea46841..d693fe6 100644
--- a/libgo/go/go/internal/gccgoimporter/testdata/v1reflect.gox
+++ b/libgo/go/go/internal/gccgoimporter/testdata/v1reflect.gox
Binary files differ
diff --git a/libgo/go/go/internal/gcimporter/gcimporter_test.go b/libgo/go/go/internal/gcimporter/gcimporter_test.go
index 3c76aaf..c9c5946 100644
--- a/libgo/go/go/internal/gcimporter/gcimporter_test.go
+++ b/libgo/go/go/internal/gcimporter/gcimporter_test.go
@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gcimporter
+package gcimporter_test
import (
"bytes"
"fmt"
+ "internal/goexperiment"
"internal/testenv"
"os"
"os/exec"
@@ -16,8 +17,13 @@ import (
"testing"
"time"
+ "go/ast"
+ "go/importer"
+ "go/parser"
"go/token"
"go/types"
+
+ . "go/internal/gcimporter"
)
// skipSpecialPlatforms causes the test to be skipped for platforms where
@@ -63,6 +69,8 @@ func testPath(t *testing.T, path, srcDir string) *types.Package {
const maxTime = 30 * time.Second
+var pkgExts = [...]string{".a", ".o"} // keep in sync with gcimporter.go
+
func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)
list, err := os.ReadDir(dirname)
@@ -110,28 +118,153 @@ func TestImportTestdata(t *testing.T) {
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
}
+ testfiles := map[string][]string{
+ "exports.go": {"go/ast", "go/token"},
+ }
+ if !goexperiment.Unified {
+ testfiles["generics.go"] = nil
+ }
+
+ for testfile, wantImports := range testfiles {
+ tmpdir := mktmpdir(t)
+ defer os.RemoveAll(tmpdir)
+
+ compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata"))
+ path := "./testdata/" + strings.TrimSuffix(testfile, ".go")
+
+ if pkg := testPath(t, path, tmpdir); pkg != nil {
+ // The package's Imports list must include all packages
+ // explicitly imported by testfile, plus all packages
+ // referenced indirectly via exported objects in testfile.
+ got := fmt.Sprint(pkg.Imports())
+ for _, want := range wantImports {
+ if !strings.Contains(got, want) {
+ t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
+ }
+ }
+ }
+ }
+}
+
+func TestImportTypeparamTests(t *testing.T) {
+ // This test doesn't yet work with the unified export format.
+ if goexperiment.Unified {
+ t.Skip("unified export data format is currently unsupported")
+ }
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
tmpdir := mktmpdir(t)
defer os.RemoveAll(tmpdir)
- compile(t, "testdata", "exports.go", filepath.Join(tmpdir, "testdata"))
-
- if pkg := testPath(t, "./testdata/exports", tmpdir); pkg != nil {
- // The package's Imports list must include all packages
- // explicitly imported by exports.go, plus all packages
- // referenced indirectly via exported objects in exports.go.
- // With the textual export format, the list may also include
- // additional packages that are not strictly required for
- // import processing alone (they are exported to err "on
- // the safe side").
- // TODO(gri) update the want list to be precise, now that
- // the textual export data is gone.
- got := fmt.Sprint(pkg.Imports())
- for _, want := range []string{"go/ast", "go/token"} {
- if !strings.Contains(got, want) {
- t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
+ // Check go files in test/typeparam, except those that fail for a known
+ // reason.
+ rootDir := filepath.Join(runtime.GOROOT(), "test", "typeparam")
+ list, err := os.ReadDir(rootDir)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ skip := map[string]string{
+ "equal.go": "inconsistent embedded sorting", // TODO(rfindley): investigate this.
+ "nested.go": "fails to compile", // TODO(rfindley): investigate this.
+ "issue50417.go": "inconsistent interface member sorting",
+ }
+
+ for _, entry := range list {
+ if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") {
+ // For now, only consider standalone go files.
+ continue
+ }
+
+ t.Run(entry.Name(), func(t *testing.T) {
+ if reason, ok := skip[entry.Name()]; ok {
+ t.Skip(reason)
}
+
+ filename := filepath.Join(rootDir, entry.Name())
+ src, err := os.ReadFile(filename)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.HasPrefix(src, []byte("// run")) && !bytes.HasPrefix(src, []byte("// compile")) {
+ // We're bypassing the logic of run.go here, so be conservative about
+ // the files we consider in an attempt to make this test more robust to
+ // changes in test/typeparams.
+ t.Skipf("not detected as a run test")
+ }
+
+ // Compile and import, and compare the resulting package with the package
+ // that was type-checked directly.
+ compile(t, rootDir, entry.Name(), filepath.Join(tmpdir, "testdata"))
+ pkgName := strings.TrimSuffix(entry.Name(), ".go")
+ imported := importPkg(t, "./testdata/"+pkgName, tmpdir)
+ checked := checkFile(t, filename, src)
+
+ seen := make(map[string]bool)
+ for _, name := range imported.Scope().Names() {
+ if !token.IsExported(name) {
+ continue // ignore synthetic names like .inittask and .dict.*
+ }
+ seen[name] = true
+
+ importedObj := imported.Scope().Lookup(name)
+ got := types.ObjectString(importedObj, types.RelativeTo(imported))
+ got = sanitizeObjectString(got)
+
+ checkedObj := checked.Scope().Lookup(name)
+ if checkedObj == nil {
+ t.Fatalf("imported object %q was not type-checked", name)
+ }
+ want := types.ObjectString(checkedObj, types.RelativeTo(checked))
+ want = sanitizeObjectString(want)
+
+ if got != want {
+ t.Errorf("imported %q as %q, want %q", name, got, want)
+ }
+ }
+
+ for _, name := range checked.Scope().Names() {
+ if !token.IsExported(name) || seen[name] {
+ continue
+ }
+ t.Errorf("did not import object %q", name)
+ }
+ })
+ }
+}
+
+// sanitizeObjectString removes type parameter debugging markers from an object
+// string, to normalize it for comparison.
+// TODO(rfindley): this should not be necessary.
+func sanitizeObjectString(s string) string {
+ var runes []rune
+ for _, r := range s {
+ if '₀' <= r && r < '₀'+10 {
+ continue // trim type parameter subscripts
}
+ runes = append(runes, r)
+ }
+ return string(runes)
+}
+
+func checkFile(t *testing.T, filename string, src []byte) *types.Package {
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, filename, src, 0)
+ if err != nil {
+ t.Fatal(err)
}
+ config := types.Config{
+ Importer: importer.Default(),
+ }
+ pkg, err := config.Check("", fset, []*ast.File{f}, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return pkg
}
func TestVersionHandling(t *testing.T) {
@@ -258,7 +391,7 @@ var importedObjectTests = []struct {
{"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string)"},
// interfaces
- {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key interface{}) interface{}}"},
+ {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key any) any}"},
{"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"},
{"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"},
{"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
diff --git a/libgo/go/go/internal/gcimporter/iimport.go b/libgo/go/go/internal/gcimporter/iimport.go
index 76d47d0..8ec4c54 100644
--- a/libgo/go/go/internal/gcimporter/iimport.go
+++ b/libgo/go/go/internal/gcimporter/iimport.go
@@ -18,6 +18,7 @@ import (
"io"
"math/big"
"sort"
+ "strings"
)
type intReader struct {
@@ -41,6 +42,21 @@ func (r *intReader) uint64() uint64 {
return i
}
+// Keep this in sync with constants in iexport.go.
+const (
+ iexportVersionGo1_11 = 0
+ iexportVersionPosCol = 1
+ iexportVersionGenerics = 2
+ iexportVersionGo1_18 = 2
+
+ iexportVersionCurrent = 2
+)
+
+type ident struct {
+ pkg string
+ name string
+}
+
const predeclReserved = 32
type itag uint64
@@ -56,6 +72,9 @@ const (
signatureType
structType
interfaceType
+ typeParamType
+ instanceType
+ unionType
)
// iImportData imports a package from the serialized package data
@@ -63,7 +82,7 @@ const (
// If the export data version is not recognized or the format is otherwise
// compromised, an error is returned.
func iImportData(fset *token.FileSet, imports map[string]*types.Package, dataReader *bufio.Reader, path string) (pkg *types.Package, err error) {
- const currentVersion = 1
+ const currentVersion = iexportVersionCurrent
version := int64(-1)
defer func() {
if e := recover(); e != nil {
@@ -79,7 +98,7 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, dataRea
version = int64(r.uint64())
switch version {
- case currentVersion, 0:
+ case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11:
default:
errorf("unknown iexport format version %d", version)
}
@@ -95,8 +114,9 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, dataRea
declData := data[sLen:]
p := iimporter{
- ipath: path,
- version: int(version),
+ exportVersion: version,
+ ipath: path,
+ version: int(version),
stringData: stringData,
stringCache: make(map[uint64]string),
@@ -105,12 +125,16 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, dataRea
declData: declData,
pkgIndex: make(map[*types.Package]map[string]uint64),
typCache: make(map[uint64]types.Type),
+ // Separate map for typeparams, keyed by their package and unique
+ // name (name with subscript).
+ tparamIndex: make(map[ident]*types.TypeParam),
fake: fakeFileSet{
fset: fset,
- files: make(map[string]*token.File),
+ files: make(map[string]*fileInfo),
},
}
+ defer p.fake.setLines() // set lines for files in fset
for i, pt := range predeclared {
p.typCache[uint64(i)] = pt
@@ -172,16 +196,18 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, dataRea
}
type iimporter struct {
- ipath string
- version int
+ exportVersion int64
+ ipath string
+ version int
stringData []byte
stringCache map[uint64]string
pkgCache map[uint64]*types.Package
- declData []byte
- pkgIndex map[*types.Package]map[string]uint64
- typCache map[uint64]types.Type
+ declData []byte
+ pkgIndex map[*types.Package]map[string]uint64
+ typCache map[uint64]types.Type
+ tparamIndex map[ident]*types.TypeParam
fake fakeFileSet
interfaceList []*types.Interface
@@ -229,7 +255,7 @@ func (p *iimporter) pkgAt(off uint64) *types.Package {
}
func (p *iimporter) typAt(off uint64, base *types.Named) types.Type {
- if t, ok := p.typCache[off]; ok && (base == nil || !isInterface(t)) {
+ if t, ok := p.typCache[off]; ok && canReuse(base, t) {
return t
}
@@ -241,12 +267,30 @@ func (p *iimporter) typAt(off uint64, base *types.Named) types.Type {
r.declReader.Reset(p.declData[off-predeclReserved:])
t := r.doType(base)
- if base == nil || !isInterface(t) {
+ if canReuse(base, t) {
p.typCache[off] = t
}
return t
}
+// canReuse reports whether the type rhs on the RHS of the declaration for def
+// may be re-used.
+//
+// Specifically, if def is non-nil and rhs is an interface type with methods, it
+// may not be re-used because we have a convention of setting the receiver type
+// for interface methods to def.
+func canReuse(def *types.Named, rhs types.Type) bool {
+ if def == nil {
+ return true
+ }
+ iface, _ := rhs.(*types.Interface)
+ if iface == nil {
+ return true
+ }
+ // Don't use iface.Empty() here as iface may not be complete.
+ return iface.NumEmbeddeds() == 0 && iface.NumExplicitMethods() == 0
+}
+
type importReader struct {
p *iimporter
declReader bytes.Reader
@@ -271,17 +315,26 @@ func (r *importReader) obj(name string) {
r.declare(types.NewConst(pos, r.currPkg, name, typ, val))
- case 'F':
- sig := r.signature(nil)
-
+ case 'F', 'G':
+ var tparams []*types.TypeParam
+ if tag == 'G' {
+ tparams = r.tparamList()
+ }
+ sig := r.signature(nil, nil, tparams)
r.declare(types.NewFunc(pos, r.currPkg, name, sig))
- case 'T':
+ case 'T', 'U':
// Types can be recursive. We need to setup a stub
// declaration before recursing.
obj := types.NewTypeName(pos, r.currPkg, name, nil)
named := types.NewNamed(obj, nil, nil)
+ // Declare obj before calling r.tparamList, so the new type name is recognized
+ // if used in the constraint of one of its own typeparams (see #48280).
r.declare(obj)
+ if tag == 'U' {
+ tparams := r.tparamList()
+ named.SetTypeParams(tparams)
+ }
underlying := r.p.typAt(r.uint64(), named).Underlying()
named.SetUnderlying(underlying)
@@ -291,12 +344,55 @@ func (r *importReader) obj(name string) {
mpos := r.pos()
mname := r.ident()
recv := r.param()
- msig := r.signature(recv)
+
+ // If the receiver has any targs, set those as the
+ // rparams of the method (since those are the
+ // typeparams being used in the method sig/body).
+ targs := baseType(recv.Type()).TypeArgs()
+ var rparams []*types.TypeParam
+ if targs.Len() > 0 {
+ rparams = make([]*types.TypeParam, targs.Len())
+ for i := range rparams {
+ rparams[i], _ = targs.At(i).(*types.TypeParam)
+ }
+ }
+ msig := r.signature(recv, rparams, nil)
named.AddMethod(types.NewFunc(mpos, r.currPkg, mname, msig))
}
}
+ case 'P':
+ // We need to "declare" a typeparam in order to have a name that
+ // can be referenced recursively (if needed) in the type param's
+ // bound.
+ if r.p.exportVersion < iexportVersionGenerics {
+ errorf("unexpected type param type")
+ }
+ // Remove the "path" from the type param name that makes it unique,
+ // and revert any unique name used for blank typeparams.
+ name0 := tparamName(name)
+ tn := types.NewTypeName(pos, r.currPkg, name0, nil)
+ t := types.NewTypeParam(tn, nil)
+ // To handle recursive references to the typeparam within its
+ // bound, save the partial type in tparamIndex before reading the bounds.
+ id := ident{r.currPkg.Name(), name}
+ r.p.tparamIndex[id] = t
+
+ var implicit bool
+ if r.p.exportVersion >= iexportVersionGo1_18 {
+ implicit = r.bool()
+ }
+ constraint := r.typ()
+ if implicit {
+ iface, _ := constraint.(*types.Interface)
+ if iface == nil {
+ errorf("non-interface constraint marked implicit")
+ }
+ iface.MarkImplicit()
+ }
+ t.SetConstraint(constraint)
+
case 'V':
typ := r.typ()
@@ -313,6 +409,10 @@ func (r *importReader) declare(obj types.Object) {
func (r *importReader) value() (typ types.Type, val constant.Value) {
typ = r.typ()
+ if r.p.exportVersion >= iexportVersionGo1_18 {
+ // TODO: add support for using the kind
+ _ = constant.Kind(r.int64())
+ }
switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType {
case types.IsBoolean:
@@ -502,7 +602,7 @@ func (r *importReader) doType(base *types.Named) types.Type {
return types.NewMap(r.typ(), r.typ())
case signatureType:
r.currPkg = r.pkg()
- return r.signature(nil)
+ return r.signature(nil, nil, nil)
case structType:
r.currPkg = r.pkg()
@@ -542,13 +642,56 @@ func (r *importReader) doType(base *types.Named) types.Type {
recv = types.NewVar(token.NoPos, r.currPkg, "", base)
}
- msig := r.signature(recv)
+ msig := r.signature(recv, nil, nil)
methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig)
}
typ := types.NewInterfaceType(methods, embeddeds)
r.p.interfaceList = append(r.p.interfaceList, typ)
return typ
+
+ case typeParamType:
+ if r.p.exportVersion < iexportVersionGenerics {
+ errorf("unexpected type param type")
+ }
+ pkg, name := r.qualifiedIdent()
+ id := ident{pkg.Name(), name}
+ if t, ok := r.p.tparamIndex[id]; ok {
+ // We're already in the process of importing this typeparam.
+ return t
+ }
+ // Otherwise, import the definition of the typeparam now.
+ r.p.doDecl(pkg, name)
+ return r.p.tparamIndex[id]
+
+ case instanceType:
+ if r.p.exportVersion < iexportVersionGenerics {
+ errorf("unexpected instantiation type")
+ }
+ // pos does not matter for instances: they are positioned on the original
+ // type.
+ _ = r.pos()
+ len := r.uint64()
+ targs := make([]types.Type, len)
+ for i := range targs {
+ targs[i] = r.typ()
+ }
+ baseType := r.typ()
+ // The imported instantiated type doesn't include any methods, so
+ // we must always use the methods of the base (orig) type.
+ // TODO provide a non-nil *Context
+ t, _ := types.Instantiate(nil, baseType, targs, false)
+ return t
+
+ case unionType:
+ if r.p.exportVersion < iexportVersionGenerics {
+ errorf("unexpected instantiation type")
+ }
+ terms := make([]*types.Term, r.uint64())
+ for i := range terms {
+ terms[i] = types.NewTerm(r.bool(), r.typ())
+ }
+ return types.NewUnion(terms)
}
}
@@ -556,11 +699,23 @@ func (r *importReader) kind() itag {
return itag(r.uint64())
}
-func (r *importReader) signature(recv *types.Var) *types.Signature {
+func (r *importReader) signature(recv *types.Var, rparams, tparams []*types.TypeParam) *types.Signature {
params := r.paramList()
results := r.paramList()
variadic := params.Len() > 0 && r.bool()
- return types.NewSignature(recv, params, results, variadic)
+ return types.NewSignatureType(recv, rparams, tparams, params, results, variadic)
+}
+
+func (r *importReader) tparamList() []*types.TypeParam {
+ n := r.uint64()
+ if n == 0 {
+ return nil
+ }
+ xs := make([]*types.TypeParam, n)
+ for i := range xs {
+ xs[i], _ = r.typ().(*types.TypeParam)
+ }
+ return xs
}
func (r *importReader) paramList() *types.Tuple {
@@ -605,3 +760,31 @@ func (r *importReader) byte() byte {
}
return x
}
+
+func baseType(typ types.Type) *types.Named {
+ // pointer receivers are never types.Named types
+ if p, _ := typ.(*types.Pointer); p != nil {
+ typ = p.Elem()
+ }
+ // receiver base types are always (possibly generic) types.Named types
+ n, _ := typ.(*types.Named)
+ return n
+}
+
+const blankMarker = "$"
+
+// tparamName returns the real name of a type parameter, after stripping its
+// qualifying prefix and reverting blank-name encoding. See tparamExportName
+// for details.
+func tparamName(exportName string) string {
+ // Remove the "path" from the type param name that makes it unique.
+ ix := strings.LastIndex(exportName, ".")
+ if ix < 0 {
+ errorf("malformed type parameter export name %s: missing prefix", exportName)
+ }
+ name := exportName[ix+1:]
+ if strings.HasPrefix(name, blankMarker) {
+ return "_"
+ }
+ return name
+}
diff --git a/libgo/go/go/internal/gcimporter/support.go b/libgo/go/go/internal/gcimporter/support.go
index b8bb14d..61d1b46 100644
--- a/libgo/go/go/internal/gcimporter/support.go
+++ b/libgo/go/go/internal/gcimporter/support.go
@@ -13,7 +13,7 @@ import (
"sync"
)
-func errorf(format string, args ...interface{}) {
+func errorf(format string, args ...any) {
panic(fmt.Sprintf(format, args...))
}
@@ -25,37 +25,50 @@ const deltaNewFile = -64
// Synthesize a token.Pos
type fakeFileSet struct {
fset *token.FileSet
- files map[string]*token.File
+ files map[string]*fileInfo
}
+type fileInfo struct {
+ file *token.File
+ lastline int
+}
+
+const maxlines = 64 * 1024
+
func (s *fakeFileSet) pos(file string, line, column int) token.Pos {
// TODO(mdempsky): Make use of column.
- // Since we don't know the set of needed file positions, we
- // reserve maxlines positions per file.
- const maxlines = 64 * 1024
+ // Since we don't know the set of needed file positions, we reserve
+ // maxlines positions per file. We delay calling token.File.SetLines until
+ // all positions have been calculated (by way of fakeFileSet.setLines), so
+ // that we can avoid setting unnecessary lines. See also golang/go#46586.
f := s.files[file]
if f == nil {
- f = s.fset.AddFile(file, -1, maxlines)
+ f = &fileInfo{file: s.fset.AddFile(file, -1, maxlines)}
s.files[file] = f
- // Allocate the fake linebreak indices on first use.
- // TODO(adonovan): opt: save ~512KB using a more complex scheme?
- fakeLinesOnce.Do(func() {
- fakeLines = make([]int, maxlines)
- for i := range fakeLines {
- fakeLines[i] = i
- }
- })
- f.SetLines(fakeLines)
}
if line > maxlines {
line = 1
}
+ if line > f.lastline {
+ f.lastline = line
+ }
- // Treat the file as if it contained only newlines
- // and column=1: use the line number as the offset.
- return f.Pos(line - 1)
+ // Return a fake position assuming that f.file consists only of newlines.
+ return token.Pos(f.file.Base() + line - 1)
+}
+
+func (s *fakeFileSet) setLines() {
+ fakeLinesOnce.Do(func() {
+ fakeLines = make([]int, maxlines)
+ for i := range fakeLines {
+ fakeLines[i] = i
+ }
+ })
+ for _, f := range s.files {
+ f.file.SetLines(fakeLines[:f.lastline])
+ }
}
var (
@@ -121,7 +134,14 @@ var predeclared = []types.Type{
types.Typ[types.Invalid], // only appears in packages with errors
// used internally by gc; never used by this package or in .a files
+ // not to be confused with the universe any
anyType{},
+
+ // comparable
+ types.Universe.Lookup("comparable").Type(),
+
+ // any
+ types.Universe.Lookup("any").Type(),
}
type anyType struct{}
diff --git a/libgo/go/go/internal/gcimporter/testdata/exports.go b/libgo/go/go/internal/gcimporter/testdata/exports.go
index 8ba3242..3d5a8c9 100644
--- a/libgo/go/go/internal/gcimporter/testdata/exports.go
+++ b/libgo/go/go/internal/gcimporter/testdata/exports.go
@@ -15,14 +15,17 @@ const init1 = 0
func init() {}
const (
- C0 int = 0
- C1 = 3.14159265
- C2 = 2.718281828i
- C3 = -123.456e-789
- C4 = +123.456e+789
- C5 = 1234i
- C6 = "foo\n"
- C7 = `bar\n`
+ C0 int = 0
+ C1 = 3.14159265
+ C2 = 2.718281828i
+ C3 = -123.456e-789
+ C4 = +123.456e+789
+ C5 = 1234i
+ C6 = "foo\n"
+ C7 = `bar\n`
+ C8 = 42
+ C9 int = 42
+ C10 float64 = 42
)
type (
@@ -47,7 +50,7 @@ type (
_ *T10
}
T11 map[int]string
- T12 interface{}
+ T12 any
T13 interface {
m1()
m2(int) float32
@@ -62,7 +65,7 @@ type (
T17 func(x int)
T18 func() float32
T19 func() (x float32)
- T20 func(...interface{})
+ T20 func(...any)
T21 struct{ next *T21 }
T22 struct{ link *T23 }
T23 struct{ link *T22 }
@@ -83,6 +86,6 @@ func F1() {}
func F2(x int) {}
func F3() int { return 0 }
func F4() float32 { return 0 }
-func F5(a, b, c int, u, v, w struct{ x, y T1 }, more ...interface{}) (p, q, r chan<- T10)
+func F5(a, b, c int, u, v, w struct{ x, y T1 }, more ...any) (p, q, r chan<- T10)
func (p *T1) M1()
diff --git a/libgo/go/go/internal/gcimporter/testdata/generics.go b/libgo/go/go/internal/gcimporter/testdata/generics.go
new file mode 100644
index 0000000..00bf040
--- /dev/null
+++ b/libgo/go/go/internal/gcimporter/testdata/generics.go
@@ -0,0 +1,29 @@
+// 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.
+
+// This file is used to generate an object file which
+// serves as test file for gcimporter_test.go.
+
+package generics
+
+type Any any
+
+var x any
+
+type T[A, B any] struct {
+ Left A
+ Right B
+}
+
+var X T[int, string] = T[int, string]{1, "hi"}
+
+func ToInt[P interface{ ~int }](p P) int { return int(p) }
+
+var IntID = ToInt[int]
+
+type G[C comparable] int
+
+func ImplicitFunc[T ~int]() {}
+
+type ImplicitType[T ~int] int
diff --git a/libgo/go/go/internal/typeparams/common.go b/libgo/go/go/internal/typeparams/common.go
index 47b8f7c..9b82e60 100644
--- a/libgo/go/go/internal/typeparams/common.go
+++ b/libgo/go/go/internal/typeparams/common.go
@@ -7,7 +7,9 @@
// constraint.
package typeparams
-// DisallowParsing is the numeric value of a parsing mode that disallows type
-// parameters. This only matters if the typeparams experiment is active, and
-// may be used for running tests that disallow generics.
-const DisallowParsing = 1 << 30
+// 'Hidden' parser modes to control the parsing of type-parameter related
+// features.
+const (
+ DisallowTypeSets = 1 << 29 // Disallow eliding 'interface' in constraint type sets.
+ DisallowParsing = 1 << 30 // Disallow type parameters entirely.
+)
diff --git a/libgo/go/go/internal/typeparams/notypeparams.go b/libgo/go/go/internal/typeparams/notypeparams.go
deleted file mode 100644
index 2ceafaa..0000000
--- a/libgo/go/go/internal/typeparams/notypeparams.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// 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.
-
-//go:build !typeparams
-// +build !typeparams
-
-package typeparams
-
-import (
- "go/ast"
-)
-
-const Enabled = false
-
-func PackExpr(list []ast.Expr) ast.Expr {
- switch len(list) {
- case 1:
- return list[0]
- default:
- // The parser should not attempt to pack multiple expressions into an
- // IndexExpr if type params are disabled.
- panic("multiple index expressions are unsupported without type params")
- }
-}
-
-func UnpackExpr(expr ast.Expr) []ast.Expr {
- return []ast.Expr{expr}
-}
-
-func IsListExpr(n ast.Node) bool {
- return false
-}
-
-func Get(ast.Node) *ast.FieldList {
- return nil
-}
-
-func Set(node ast.Node, params *ast.FieldList) {
-}
diff --git a/libgo/go/go/internal/typeparams/typeparams.go b/libgo/go/go/internal/typeparams/typeparams.go
index 871e95d..3f84f2f 100644
--- a/libgo/go/go/internal/typeparams/typeparams.go
+++ b/libgo/go/go/internal/typeparams/typeparams.go
@@ -2,68 +2,53 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build typeparams
-// +build typeparams
-
package typeparams
import (
- "fmt"
"go/ast"
+ "go/token"
)
-const Enabled = true
-
-func PackExpr(list []ast.Expr) ast.Expr {
- switch len(list) {
+func PackIndexExpr(x ast.Expr, lbrack token.Pos, exprs []ast.Expr, rbrack token.Pos) ast.Expr {
+ switch len(exprs) {
case 0:
- // Return an empty ListExpr here, rather than nil, as IndexExpr.Index must
- // never be nil.
- // TODO(rFindley) would a BadExpr be more appropriate here?
- return &ast.ListExpr{}
+ panic("internal error: PackIndexExpr with empty expr slice")
case 1:
- return list[0]
+ return &ast.IndexExpr{
+ X: x,
+ Lbrack: lbrack,
+ Index: exprs[0],
+ Rbrack: rbrack,
+ }
default:
- return &ast.ListExpr{ElemList: list}
+ return &ast.IndexListExpr{
+ X: x,
+ Lbrack: lbrack,
+ Indices: exprs,
+ Rbrack: rbrack,
+ }
}
}
-// TODO(gri) Should find a more efficient solution that doesn't
-// require introduction of a new slice for simple
-// expressions.
-func UnpackExpr(x ast.Expr) []ast.Expr {
- if x, _ := x.(*ast.ListExpr); x != nil {
- return x.ElemList
- }
- if x != nil {
- return []ast.Expr{x}
- }
- return nil
-}
-
-func IsListExpr(n ast.Node) bool {
- _, ok := n.(*ast.ListExpr)
- return ok
+// IndexExpr wraps an ast.IndexExpr or ast.IndexListExpr.
+//
+// Orig holds the original ast.Expr from which this IndexExpr was derived.
+type IndexExpr struct {
+ Orig ast.Expr // the wrapped expr, which may be distinct from the IndexListExpr below.
+ *ast.IndexListExpr
}
-func Get(n ast.Node) *ast.FieldList {
- switch n := n.(type) {
- case *ast.TypeSpec:
- return n.TParams
- case *ast.FuncType:
- return n.TParams
- default:
- panic(fmt.Sprintf("node type %T has no type parameters", n))
- }
-}
-
-func Set(n ast.Node, params *ast.FieldList) {
- switch n := n.(type) {
- case *ast.TypeSpec:
- n.TParams = params
- case *ast.FuncType:
- n.TParams = params
- default:
- panic(fmt.Sprintf("node type %T has no type parameters", n))
+func UnpackIndexExpr(n ast.Node) *IndexExpr {
+ switch e := n.(type) {
+ case *ast.IndexExpr:
+ return &IndexExpr{e, &ast.IndexListExpr{
+ X: e.X,
+ Lbrack: e.Lbrack,
+ Indices: []ast.Expr{e.Index},
+ Rbrack: e.Rbrack,
+ }}
+ case *ast.IndexListExpr:
+ return &IndexExpr{e, e}
}
+ return nil
}
diff --git a/libgo/go/go/parser/error_test.go b/libgo/go/go/parser/error_test.go
index f4f0a52..bedfc26 100644
--- a/libgo/go/go/parser/error_test.go
+++ b/libgo/go/go/parser/error_test.go
@@ -23,6 +23,7 @@
package parser
import (
+ "flag"
"go/internal/typeparams"
"go/scanner"
"go/token"
@@ -33,6 +34,8 @@ import (
"testing"
)
+var traceErrs = flag.Bool("trace_errs", false, "whether to enable tracing for error tests")
+
const testdata = "testdata"
// getFile assumes that each filename occurs at most once
@@ -151,7 +154,7 @@ func compareErrors(t *testing.T, fset *token.FileSet, expected map[token.Pos]str
}
}
-func checkErrors(t *testing.T, filename string, input interface{}, mode Mode, expectErrors bool) {
+func checkErrors(t *testing.T, filename string, input any, mode Mode, expectErrors bool) {
t.Helper()
src, err := readSource(filename, input)
if err != nil {
@@ -186,16 +189,17 @@ func TestErrors(t *testing.T) {
}
for _, d := range list {
name := d.Name()
- if !d.IsDir() && !strings.HasPrefix(name, ".") && (strings.HasSuffix(name, ".src") || strings.HasSuffix(name, ".go2")) {
- mode := DeclarationErrors | AllErrors
- if strings.HasSuffix(name, ".go2") {
- if !typeparams.Enabled {
- continue
+ t.Run(name, func(t *testing.T) {
+ if !d.IsDir() && !strings.HasPrefix(name, ".") && (strings.HasSuffix(name, ".src") || strings.HasSuffix(name, ".go2")) {
+ mode := DeclarationErrors | AllErrors
+ if !strings.HasSuffix(name, ".go2") {
+ mode |= typeparams.DisallowParsing
}
- } else {
- mode |= typeparams.DisallowParsing
+ if *traceErrs {
+ mode |= Trace
+ }
+ checkErrors(t, filepath.Join(testdata, name), nil, mode, true)
}
- checkErrors(t, filepath.Join(testdata, name), nil, mode, true)
- }
+ })
}
}
diff --git a/libgo/go/go/parser/interface.go b/libgo/go/go/parser/interface.go
index 85486d2..e4f8c28 100644
--- a/libgo/go/go/parser/interface.go
+++ b/libgo/go/go/parser/interface.go
@@ -22,7 +22,7 @@ import (
// otherwise it returns an error. If src == nil, readSource returns
// the result of reading the file specified by filename.
//
-func readSource(filename string, src interface{}) ([]byte, error) {
+func readSource(filename string, src any) ([]byte, error) {
if src != nil {
switch s := src.(type) {
case string:
@@ -82,7 +82,7 @@ const (
// representing the fragments of erroneous source code). Multiple errors
// are returned via a scanner.ErrorList which is sorted by source position.
//
-func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) (f *ast.File, err error) {
+func ParseFile(fset *token.FileSet, filename string, src any, mode Mode) (f *ast.File, err error) {
if fset == nil {
panic("parser.ParseFile: no token.FileSet provided (fset == nil)")
}
@@ -188,7 +188,7 @@ func ParseDir(fset *token.FileSet, path string, filter func(fs.FileInfo) bool, m
// representing the fragments of erroneous source code). Multiple errors
// are returned via a scanner.ErrorList which is sorted by source position.
//
-func ParseExprFrom(fset *token.FileSet, filename string, src interface{}, mode Mode) (expr ast.Expr, err error) {
+func ParseExprFrom(fset *token.FileSet, filename string, src any, mode Mode) (expr ast.Expr, err error) {
if fset == nil {
panic("parser.ParseExprFrom: no token.FileSet provided (fset == nil)")
}
diff --git a/libgo/go/go/parser/parser.go b/libgo/go/go/parser/parser.go
index f10c865..e456e29 100644
--- a/libgo/go/go/parser/parser.go
+++ b/libgo/go/go/parser/parser.go
@@ -76,14 +76,13 @@ func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mod
p.next()
}
-func (p *parser) parseTypeParams() bool {
- return typeparams.Enabled && p.mode&typeparams.DisallowParsing == 0
-}
+func (p *parser) allowGenerics() bool { return p.mode&typeparams.DisallowParsing == 0 }
+func (p *parser) allowTypeSets() bool { return p.mode&typeparams.DisallowTypeSets == 0 }
// ----------------------------------------------------------------------------
// Parsing support
-func (p *parser) printTrace(a ...interface{}) {
+func (p *parser) printTrace(a ...any) {
const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "
const n = len(dots)
pos := p.file.Position(p.pos)
@@ -499,7 +498,7 @@ func (p *parser) parseQualifiedIdent(ident *ast.Ident) ast.Expr {
}
typ := p.parseTypeName(ident)
- if p.tok == token.LBRACK && p.parseTypeParams() {
+ if p.tok == token.LBRACK && p.allowGenerics() {
typ = p.parseTypeInstance(typ)
}
@@ -526,23 +525,27 @@ func (p *parser) parseTypeName(ident *ast.Ident) ast.Expr {
return ident
}
-func (p *parser) parseArrayLen() ast.Expr {
+// "[" has already been consumed, and lbrack is its position.
+// If len != nil it is the already consumed array length.
+func (p *parser) parseArrayType(lbrack token.Pos, len ast.Expr) *ast.ArrayType {
if p.trace {
- defer un(trace(p, "ArrayLen"))
+ defer un(trace(p, "ArrayType"))
}
- p.exprLev++
- var len ast.Expr
- // always permit ellipsis for more fault-tolerant parsing
- if p.tok == token.ELLIPSIS {
- len = &ast.Ellipsis{Ellipsis: p.pos}
- p.next()
- } else if p.tok != token.RBRACK {
- len = p.parseRhs()
+ if len == nil {
+ p.exprLev++
+ // always permit ellipsis for more fault-tolerant parsing
+ if p.tok == token.ELLIPSIS {
+ len = &ast.Ellipsis{Ellipsis: p.pos}
+ p.next()
+ } else if p.tok != token.RBRACK {
+ len = p.parseRhs()
+ }
+ p.exprLev--
}
- p.exprLev--
-
- return len
+ p.expect(token.RBRACK)
+ elt := p.parseType()
+ return &ast.ArrayType{Lbrack: lbrack, Len: len, Elt: elt}
}
func (p *parser) parseArrayFieldOrTypeInstance(x *ast.Ident) (*ast.Ident, ast.Expr) {
@@ -558,7 +561,7 @@ func (p *parser) parseArrayFieldOrTypeInstance(x *ast.Ident) (*ast.Ident, ast.Ex
// TODO(rfindley): consider changing parseRhsOrType so that this function variable
// is not needed.
argparser := p.parseRhsOrType
- if !p.parseTypeParams() {
+ if !p.allowGenerics() {
argparser = p.parseRhs
}
if p.tok != token.RBRACK {
@@ -588,19 +591,19 @@ func (p *parser) parseArrayFieldOrTypeInstance(x *ast.Ident) (*ast.Ident, ast.Ex
// x [P]E
return x, &ast.ArrayType{Lbrack: lbrack, Len: args[0], Elt: elt}
}
- if !p.parseTypeParams() {
+ if !p.allowGenerics() {
p.error(rbrack, "missing element type in array type expression")
return nil, &ast.BadExpr{From: args[0].Pos(), To: args[0].End()}
}
}
- if !p.parseTypeParams() {
+ if !p.allowGenerics() {
p.error(firstComma, "expected ']', found ','")
return x, &ast.BadExpr{From: args[0].Pos(), To: args[len(args)-1].End()}
}
// x[P], x[P1, P2], ...
- return nil, &ast.IndexExpr{X: x, Lbrack: lbrack, Index: typeparams.PackExpr(args), Rbrack: rbrack}
+ return nil, typeparams.PackIndexExpr(x, lbrack, args, rbrack)
}
func (p *parser) parseFieldDecl() *ast.Field {
@@ -711,8 +714,9 @@ type field struct {
typ ast.Expr
}
-func (p *parser) parseParamDecl(name *ast.Ident) (f field) {
- // TODO(rFindley) compare with parser.paramDeclOrNil in the syntax package
+func (p *parser) parseParamDecl(name *ast.Ident, typeSetsOK bool) (f field) {
+ // TODO(rFindley) refactor to be more similar to paramDeclOrNil in the syntax
+ // package
if p.trace {
defer un(trace(p, "ParamDeclOrNil"))
}
@@ -720,10 +724,14 @@ func (p *parser) parseParamDecl(name *ast.Ident) (f field) {
ptok := p.tok
if name != nil {
p.tok = token.IDENT // force token.IDENT case in switch below
+ } else if typeSetsOK && p.tok == token.TILDE {
+ // "~" ...
+ return field{nil, p.embeddedElem(nil)}
}
switch p.tok {
case token.IDENT:
+ // name
if name != nil {
f.name = name
p.tok = ptok
@@ -736,17 +744,32 @@ func (p *parser) parseParamDecl(name *ast.Ident) (f field) {
f.typ = p.parseType()
case token.LBRACK:
- // name[type1, type2, ...] or name []type or name [len]type
+ // name "[" type1, ..., typeN "]" or name "[" n "]" type
f.name, f.typ = p.parseArrayFieldOrTypeInstance(f.name)
case token.ELLIPSIS:
- // name ...type
+ // name "..." type
f.typ = p.parseDotsType()
+ return // don't allow ...type "|" ...
case token.PERIOD:
- // qualified.typename
+ // name "." ...
f.typ = p.parseQualifiedIdent(f.name)
f.name = nil
+
+ case token.TILDE:
+ if typeSetsOK {
+ f.typ = p.embeddedElem(nil)
+ return
+ }
+
+ case token.OR:
+ if typeSetsOK {
+ // name "|" typeset
+ f.typ = p.embeddedElem(f.name)
+ f.name = nil
+ return
+ }
}
case token.MUL, token.ARROW, token.FUNC, token.LBRACK, token.CHAN, token.MAP, token.STRUCT, token.INTERFACE, token.LPAREN:
@@ -754,23 +777,36 @@ func (p *parser) parseParamDecl(name *ast.Ident) (f field) {
f.typ = p.parseType()
case token.ELLIPSIS:
- // ...type
+ // "..." type
// (always accepted)
f.typ = p.parseDotsType()
+ return // don't allow ...type "|" ...
default:
+ // TODO(rfindley): this looks incorrect in the case of type parameter
+ // lists.
p.errorExpected(p.pos, ")")
p.advance(exprEnd)
}
+ // [name] type "|"
+ if typeSetsOK && p.tok == token.OR && f.typ != nil {
+ f.typ = p.embeddedElem(f.typ)
+ }
+
return
}
-func (p *parser) parseParameterList(name0 *ast.Ident, closing token.Token, parseParamDecl func(*ast.Ident) field, tparams bool) (params []*ast.Field) {
+func (p *parser) parseParameterList(name0 *ast.Ident, closing token.Token) (params []*ast.Field) {
if p.trace {
defer un(trace(p, "ParameterList"))
}
+ // Type parameters are the only parameter list closed by ']'.
+ tparams := closing == token.RBRACK
+ // Type set notation is ok in type parameter lists.
+ typeSetsOK := tparams && p.allowTypeSets()
+
pos := p.pos
if name0 != nil {
pos = name0.Pos()
@@ -780,7 +816,7 @@ func (p *parser) parseParameterList(name0 *ast.Ident, closing token.Token, parse
var named int // number of parameters that have an explicit name and type
for name0 != nil || p.tok != closing && p.tok != token.EOF {
- par := parseParamDecl(name0)
+ par := p.parseParamDecl(name0, typeSetsOK)
name0 = nil // 1st name was consumed if present
if par.name != nil || par.typ != nil {
list = append(list, par)
@@ -818,11 +854,13 @@ func (p *parser) parseParameterList(name0 *ast.Ident, closing token.Token, parse
// some named => all must be named
ok := true
var typ ast.Expr
+ missingName := pos
for i := len(list) - 1; i >= 0; i-- {
if par := &list[i]; par.typ != nil {
typ = par.typ
if par.name == nil {
ok = false
+ missingName = par.typ.Pos()
n := ast.NewIdent("_")
n.NamePos = typ.Pos() // correct position
par.name = n
@@ -832,12 +870,13 @@ func (p *parser) parseParameterList(name0 *ast.Ident, closing token.Token, parse
} else {
// par.typ == nil && typ == nil => we only have a par.name
ok = false
+ missingName = par.name.Pos()
par.typ = &ast.BadExpr{From: par.name.Pos(), To: p.pos}
}
}
if !ok {
if tparams {
- p.error(pos, "all type parameters must be named")
+ p.error(missingName, "all type parameters must be named")
} else {
p.error(pos, "mixed named and unnamed parameters")
}
@@ -883,11 +922,11 @@ func (p *parser) parseParameters(acceptTParams bool) (tparams, params *ast.Field
defer un(trace(p, "Parameters"))
}
- if p.parseTypeParams() && acceptTParams && p.tok == token.LBRACK {
+ if p.allowGenerics() && acceptTParams && p.tok == token.LBRACK {
opening := p.pos
p.next()
// [T any](params) syntax
- list := p.parseParameterList(nil, token.RBRACK, p.parseParamDecl, true)
+ list := p.parseParameterList(nil, token.RBRACK)
rbrack := p.expect(token.RBRACK)
tparams = &ast.FieldList{Opening: opening, List: list, Closing: rbrack}
// Type parameter lists must not be empty.
@@ -901,7 +940,7 @@ func (p *parser) parseParameters(acceptTParams bool) (tparams, params *ast.Field
var fields []*ast.Field
if p.tok != token.RPAREN {
- fields = p.parseParameterList(nil, token.RPAREN, p.parseParamDecl, false)
+ fields = p.parseParameterList(nil, token.RPAREN)
}
rparen := p.expect(token.RPAREN)
@@ -956,7 +995,7 @@ func (p *parser) parseMethodSpec() *ast.Field {
x := p.parseTypeName(nil)
if ident, _ := x.(*ast.Ident); ident != nil {
switch {
- case p.tok == token.LBRACK && p.parseTypeParams():
+ case p.tok == token.LBRACK && p.allowGenerics():
// generic method or embedded instantiated type
lbrack := p.pos
p.next()
@@ -965,21 +1004,26 @@ func (p *parser) parseMethodSpec() *ast.Field {
p.exprLev--
if name0, _ := x.(*ast.Ident); name0 != nil && p.tok != token.COMMA && p.tok != token.RBRACK {
// generic method m[T any]
- list := p.parseParameterList(name0, token.RBRACK, p.parseParamDecl, true)
+ list := p.parseParameterList(name0, token.RBRACK)
rbrack := p.expect(token.RBRACK)
tparams := &ast.FieldList{Opening: lbrack, List: list, Closing: rbrack}
// TODO(rfindley) refactor to share code with parseFuncType.
_, params := p.parseParameters(false)
results := p.parseResult()
idents = []*ast.Ident{ident}
- typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results}
- typeparams.Set(typ, tparams)
+ typ = &ast.FuncType{
+ Func: token.NoPos,
+ TypeParams: tparams,
+ Params: params,
+ Results: results,
+ }
} else {
// embedded instantiated type
// TODO(rfindley) should resolve all identifiers in x.
list := []ast.Expr{x}
if p.atComma("type argument list", token.RBRACK) {
p.exprLev++
+ p.next()
for p.tok != token.RBRACK && p.tok != token.EOF {
list = append(list, p.parseType())
if !p.atComma("type argument list", token.RBRACK) {
@@ -990,7 +1034,7 @@ func (p *parser) parseMethodSpec() *ast.Field {
p.exprLev--
}
rbrack := p.expectClosing(token.RBRACK, "type argument list")
- typ = &ast.IndexExpr{X: ident, Lbrack: lbrack, Index: typeparams.PackExpr(list), Rbrack: rbrack}
+ typ = typeparams.PackIndexExpr(ident, lbrack, list, rbrack)
}
case p.tok == token.LPAREN:
// ordinary method
@@ -1006,16 +1050,60 @@ func (p *parser) parseMethodSpec() *ast.Field {
} else {
// embedded, possibly instantiated type
typ = x
- if p.tok == token.LBRACK && p.parseTypeParams() {
+ if p.tok == token.LBRACK && p.allowGenerics() {
// embedded instantiated interface
typ = p.parseTypeInstance(typ)
}
}
- p.expectSemi() // call before accessing p.linecomment
- spec := &ast.Field{Doc: doc, Names: idents, Type: typ, Comment: p.lineComment}
+ // Comment is added at the callsite: the field below may joined with
+ // additional type specs using '|'.
+ // TODO(rfindley) this should be refactored.
+ // TODO(rfindley) add more tests for comment handling.
+ return &ast.Field{Doc: doc, Names: idents, Type: typ}
+}
- return spec
+func (p *parser) embeddedElem(x ast.Expr) ast.Expr {
+ if p.trace {
+ defer un(trace(p, "EmbeddedElem"))
+ }
+ if x == nil {
+ x = p.embeddedTerm()
+ }
+ for p.tok == token.OR {
+ t := new(ast.BinaryExpr)
+ t.OpPos = p.pos
+ t.Op = token.OR
+ p.next()
+ t.X = x
+ t.Y = p.embeddedTerm()
+ x = t
+ }
+ return x
+}
+
+func (p *parser) embeddedTerm() ast.Expr {
+ if p.trace {
+ defer un(trace(p, "EmbeddedTerm"))
+ }
+ if p.tok == token.TILDE {
+ t := new(ast.UnaryExpr)
+ t.OpPos = p.pos
+ t.Op = token.TILDE
+ p.next()
+ t.X = p.parseType()
+ return t
+ }
+
+ t := p.tryIdentOrType()
+ if t == nil {
+ pos := p.pos
+ p.errorExpected(pos, "~ term or type")
+ p.advance(exprEnd)
+ return &ast.BadExpr{From: pos, To: p.pos}
+ }
+
+ return t
}
func (p *parser) parseInterfaceType() *ast.InterfaceType {
@@ -1025,22 +1113,39 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType {
pos := p.expect(token.INTERFACE)
lbrace := p.expect(token.LBRACE)
+
var list []*ast.Field
- for p.tok == token.IDENT || p.parseTypeParams() && p.tok == token.TYPE {
- if p.tok == token.IDENT {
- list = append(list, p.parseMethodSpec())
- } else {
- // all types in a type list share the same field name "type"
- // (since type is a keyword, a Go program cannot have that field name)
- name := []*ast.Ident{{NamePos: p.pos, Name: "type"}}
- p.next()
- // add each type as a field named "type"
- for _, typ := range p.parseTypeList() {
- list = append(list, &ast.Field{Names: name, Type: typ})
+
+parseElements:
+ for {
+ switch {
+ case p.tok == token.IDENT:
+ f := p.parseMethodSpec()
+ if f.Names == nil && p.allowGenerics() {
+ f.Type = p.embeddedElem(f.Type)
}
p.expectSemi()
+ f.Comment = p.lineComment
+ list = append(list, f)
+ case p.tok == token.TILDE && p.allowGenerics():
+ typ := p.embeddedElem(nil)
+ p.expectSemi()
+ comment := p.lineComment
+ list = append(list, &ast.Field{Type: typ, Comment: comment})
+ case p.allowGenerics():
+ if t := p.tryIdentOrType(); t != nil {
+ typ := p.embeddedElem(t)
+ p.expectSemi()
+ comment := p.lineComment
+ list = append(list, &ast.Field{Type: typ, Comment: comment})
+ } else {
+ break parseElements
+ }
+ default:
+ break parseElements
}
}
+
// TODO(rfindley): the error produced here could be improved, since we could
// accept a identifier, 'type', or a '}' at this point.
rbrace := p.expect(token.RBRACE)
@@ -1095,13 +1200,12 @@ func (p *parser) parseChanType() *ast.ChanType {
}
func (p *parser) parseTypeInstance(typ ast.Expr) ast.Expr {
- assert(p.parseTypeParams(), "parseTypeInstance while not parsing type params")
+ assert(p.allowGenerics(), "parseTypeInstance while not parsing type params")
if p.trace {
defer un(trace(p, "TypeInstance"))
}
opening := p.expect(token.LBRACK)
-
p.exprLev++
var list []ast.Expr
for p.tok != token.RBRACK && p.tok != token.EOF {
@@ -1115,23 +1219,30 @@ func (p *parser) parseTypeInstance(typ ast.Expr) ast.Expr {
closing := p.expectClosing(token.RBRACK, "type argument list")
- return &ast.IndexExpr{X: typ, Lbrack: opening, Index: typeparams.PackExpr(list), Rbrack: closing}
+ if len(list) == 0 {
+ p.errorExpected(closing, "type argument list")
+ return &ast.IndexExpr{
+ X: typ,
+ Lbrack: opening,
+ Index: &ast.BadExpr{From: opening + 1, To: closing},
+ Rbrack: closing,
+ }
+ }
+
+ return typeparams.PackIndexExpr(typ, opening, list, closing)
}
func (p *parser) tryIdentOrType() ast.Expr {
switch p.tok {
case token.IDENT:
typ := p.parseTypeName(nil)
- if p.tok == token.LBRACK && p.parseTypeParams() {
+ if p.tok == token.LBRACK && p.allowGenerics() {
typ = p.parseTypeInstance(typ)
}
return typ
case token.LBRACK:
lbrack := p.expect(token.LBRACK)
- alen := p.parseArrayLen()
- p.expect(token.RBRACK)
- elt := p.parseType()
- return &ast.ArrayType{Lbrack: lbrack, Len: alen, Elt: elt}
+ return p.parseArrayType(lbrack, nil)
case token.STRUCT:
return p.parseStructType()
case token.MUL:
@@ -1372,13 +1483,13 @@ func (p *parser) parseIndexOrSliceOrInstance(x ast.Expr) ast.Expr {
return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack}
}
- if !p.parseTypeParams() {
+ if !p.allowGenerics() {
p.error(firstComma, "expected ']' or ':', found ','")
return &ast.BadExpr{From: args[0].Pos(), To: args[len(args)-1].End()}
}
// instance expression
- return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: typeparams.PackExpr(args), Rbrack: rbrack}
+ return typeparams.PackIndexExpr(x, lbrack, args, rbrack)
}
func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
@@ -1480,6 +1591,7 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr {
panic("unreachable")
case *ast.SelectorExpr:
case *ast.IndexExpr:
+ case *ast.IndexListExpr:
case *ast.SliceExpr:
case *ast.TypeAssertExpr:
// If t.Type == nil we have a type assertion of the form
@@ -1525,12 +1637,14 @@ func (p *parser) checkExprOrType(x ast.Expr) ast.Expr {
return x
}
-func (p *parser) parsePrimaryExpr() (x ast.Expr) {
+func (p *parser) parsePrimaryExpr(x ast.Expr) ast.Expr {
if p.trace {
defer un(trace(p, "PrimaryExpr"))
}
- x = p.parseOperand()
+ if x == nil {
+ x = p.parseOperand()
+ }
for {
switch p.tok {
case token.PERIOD:
@@ -1566,18 +1680,18 @@ func (p *parser) parsePrimaryExpr() (x ast.Expr) {
switch t.(type) {
case *ast.BadExpr, *ast.Ident, *ast.SelectorExpr:
if p.exprLev < 0 {
- return
+ return x
}
// x is possibly a composite literal type
- case *ast.IndexExpr:
+ case *ast.IndexExpr, *ast.IndexListExpr:
if p.exprLev < 0 {
- return
+ return x
}
// x is possibly a composite literal type
case *ast.ArrayType, *ast.StructType, *ast.MapType:
// x is a composite literal type
default:
- return
+ return x
}
if t != x {
p.error(t.Pos(), "cannot parenthesize type in composite literal")
@@ -1585,7 +1699,7 @@ func (p *parser) parsePrimaryExpr() (x ast.Expr) {
}
x = p.parseLiteralValue(x)
default:
- return
+ return x
}
}
}
@@ -1656,7 +1770,7 @@ func (p *parser) parseUnaryExpr() ast.Expr {
return &ast.StarExpr{Star: pos, X: p.checkExprOrType(x)}
}
- return p.parsePrimaryExpr()
+ return p.parsePrimaryExpr(nil)
}
func (p *parser) tokPrec() (token.Token, int) {
@@ -1667,19 +1781,21 @@ func (p *parser) tokPrec() (token.Token, int) {
return tok, tok.Precedence()
}
-func (p *parser) parseBinaryExpr(prec1 int) ast.Expr {
+func (p *parser) parseBinaryExpr(x ast.Expr, prec1 int) ast.Expr {
if p.trace {
defer un(trace(p, "BinaryExpr"))
}
- x := p.parseUnaryExpr()
+ if x == nil {
+ x = p.parseUnaryExpr()
+ }
for {
op, oprec := p.tokPrec()
if oprec < prec1 {
return x
}
pos := p.expect(op)
- y := p.parseBinaryExpr(oprec + 1)
+ y := p.parseBinaryExpr(nil, oprec+1)
x = &ast.BinaryExpr{X: p.checkExpr(x), OpPos: pos, Op: op, Y: p.checkExpr(y)}
}
}
@@ -1692,7 +1808,7 @@ func (p *parser) parseExpr() ast.Expr {
defer un(trace(p, "Expression"))
}
- return p.parseBinaryExpr(token.LowestPrec + 1)
+ return p.parseBinaryExpr(nil, token.LowestPrec+1)
}
func (p *parser) parseRhs() ast.Expr {
@@ -2415,13 +2531,19 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, _ token.Pos, keyword toke
return spec
}
-func (p *parser) parseGenericType(spec *ast.TypeSpec, openPos token.Pos, name0 *ast.Ident, closeTok token.Token) {
- list := p.parseParameterList(name0, closeTok, p.parseParamDecl, true)
- closePos := p.expect(closeTok)
- typeparams.Set(spec, &ast.FieldList{Opening: openPos, List: list, Closing: closePos})
- // Type alias cannot have type parameters. Accept them for robustness but complain.
+func (p *parser) parseGenericType(spec *ast.TypeSpec, openPos token.Pos, name0 *ast.Ident) {
+ if p.trace {
+ defer un(trace(p, "parseGenericType"))
+ }
+
+ list := p.parseParameterList(name0, token.RBRACK)
+ closePos := p.expect(token.RBRACK)
+ spec.TypeParams = &ast.FieldList{Opening: openPos, List: list, Closing: closePos}
+ // Let the type checker decide whether to accept type parameters on aliases:
+ // see issue #46477.
if p.tok == token.ASSIGN {
- p.error(p.pos, "generic type cannot be alias")
+ // type alias
+ spec.Assign = p.pos
p.next()
}
spec.Type = p.parseType()
@@ -2435,34 +2557,42 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token
ident := p.parseIdent()
spec := &ast.TypeSpec{Doc: doc, Name: ident}
- switch p.tok {
- case token.LBRACK:
+ if p.tok == token.LBRACK && p.allowGenerics() {
lbrack := p.pos
p.next()
if p.tok == token.IDENT {
- // array type or generic type [T any]
- p.exprLev++
- x := p.parseExpr()
- p.exprLev--
- if name0, _ := x.(*ast.Ident); p.parseTypeParams() && name0 != nil && p.tok != token.RBRACK {
+ // array type or generic type: [name0...
+ name0 := p.parseIdent()
+
+ // Index or slice expressions are never constant and thus invalid
+ // array length expressions. Thus, if we see a "[" following name
+ // we can safely assume that "[" name starts a type parameter list.
+ var x ast.Expr // x != nil means x is the array length expression
+ if p.tok != token.LBRACK {
+ // We may still have either an array type or generic type -- check if
+ // name0 is the entire expr.
+ p.exprLev++
+ lhs := p.parsePrimaryExpr(name0)
+ x = p.parseBinaryExpr(lhs, token.LowestPrec+1)
+ p.exprLev--
+ if x == name0 && p.tok != token.RBRACK {
+ x = nil
+ }
+ }
+
+ if x == nil {
// generic type [T any];
- p.parseGenericType(spec, lbrack, name0, token.RBRACK)
+ p.parseGenericType(spec, lbrack, name0)
} else {
// array type
// TODO(rfindley) should resolve all identifiers in x.
- p.expect(token.RBRACK)
- elt := p.parseType()
- spec.Type = &ast.ArrayType{Lbrack: lbrack, Len: x, Elt: elt}
+ spec.Type = p.parseArrayType(lbrack, x)
}
} else {
// array type
- alen := p.parseArrayLen()
- p.expect(token.RBRACK)
- elt := p.parseType()
- spec.Type = &ast.ArrayType{Lbrack: lbrack, Len: alen, Elt: elt}
+ spec.Type = p.parseArrayType(lbrack, nil)
}
-
- default:
+ } else {
// no type parameters
if p.tok == token.ASSIGN {
// type alias
@@ -2528,10 +2658,11 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
results := p.parseResult()
var body *ast.BlockStmt
- if p.tok == token.LBRACE {
+ switch p.tok {
+ case token.LBRACE:
body = p.parseBody()
p.expectSemi()
- } else if p.tok == token.SEMICOLON {
+ case token.SEMICOLON:
p.next()
if p.tok == token.LBRACE {
// opening { of function declaration on next line
@@ -2539,7 +2670,7 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
body = p.parseBody()
p.expectSemi()
}
- } else {
+ default:
p.expectSemi()
}
@@ -2548,13 +2679,13 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
Recv: recv,
Name: ident,
Type: &ast.FuncType{
- Func: pos,
- Params: params,
- Results: results,
+ Func: pos,
+ TypeParams: tparams,
+ Params: params,
+ Results: results,
},
Body: body,
}
- typeparams.Set(decl.Type, tparams)
return decl
}
diff --git a/libgo/go/go/parser/resolver.go b/libgo/go/go/parser/resolver.go
index cf92c7e..910ca06 100644
--- a/libgo/go/go/parser/resolver.go
+++ b/libgo/go/go/parser/resolver.go
@@ -7,7 +7,6 @@ package parser
import (
"fmt"
"go/ast"
- "go/internal/typeparams"
"go/token"
)
@@ -68,11 +67,11 @@ type resolver struct {
targetStack [][]*ast.Ident // stack of unresolved labels
}
-func (r *resolver) dump(format string, args ...interface{}) {
+func (r *resolver) dump(format string, args ...any) {
fmt.Println(">>> " + r.sprintf(format, args...))
}
-func (r *resolver) sprintf(format string, args ...interface{}) string {
+func (r *resolver) sprintf(format string, args ...any) string {
for i, arg := range args {
switch arg := arg.(type) {
case token.Pos:
@@ -116,14 +115,8 @@ func (r *resolver) closeLabelScope() {
r.labelScope = r.labelScope.Outer
}
-func (r *resolver) declare(decl, data interface{}, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) {
+func (r *resolver) declare(decl, data any, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) {
for _, ident := range idents {
- // "type" is used for type lists in interfaces, and is otherwise an invalid
- // identifier. The 'type' identifier is also artificially duplicated in the
- // type list, so could cause panics below if we were to proceed.
- if ident.Name == "type" {
- continue
- }
assert(ident.Obj == nil, "identifier already declared or resolved")
obj := ast.NewObj(kind, ident.Name)
// remember the corresponding declaration for redeclaration
@@ -189,10 +182,9 @@ func (r *resolver) resolve(ident *ast.Ident, collectUnresolved bool) {
if ident.Obj != nil {
panic(fmt.Sprintf("%s: identifier %s already declared or resolved", r.handle.Position(ident.Pos()), ident.Name))
}
- // '_' and 'type' should never refer to existing declarations: '_' because it
- // has special handling in the spec, and 'type' because it is a keyword, and
- // only valid in an interface type list.
- if ident.Name == "_" || ident.Name == "type" {
+ // '_' should never refer to existing declarations, because it has special
+ // handling in the spec.
+ if ident.Name == "_" {
return
}
for s := r.topScope; s != nil; s = s.Outer {
@@ -455,10 +447,10 @@ func (r *resolver) Visit(node ast.Node) ast.Visitor {
// at the identifier in the TypeSpec and ends at the end of the innermost
// containing block.
r.declare(spec, nil, r.topScope, ast.Typ, spec.Name)
- if tparams := typeparams.Get(spec); tparams != nil {
+ if spec.TypeParams != nil {
r.openScope(spec.Pos())
defer r.closeScope()
- r.walkTParams(tparams)
+ r.walkTParams(spec.TypeParams)
}
ast.Walk(r, spec.Type)
}
@@ -474,8 +466,8 @@ func (r *resolver) Visit(node ast.Node) ast.Visitor {
// Type parameters are walked normally: they can reference each other, and
// can be referenced by normal parameters.
- if tparams := typeparams.Get(n.Type); tparams != nil {
- r.walkTParams(tparams)
+ if n.Type.TypeParams != nil {
+ r.walkTParams(n.Type.TypeParams)
// TODO(rFindley): need to address receiver type parameters.
}
@@ -500,7 +492,7 @@ func (r *resolver) Visit(node ast.Node) ast.Visitor {
}
func (r *resolver) walkFuncType(typ *ast.FuncType) {
- // typ.TParams must be walked separately for FuncDecls.
+ // typ.TypeParams must be walked separately for FuncDecls.
r.resolveList(typ.Params)
r.resolveList(typ.Results)
r.declareList(typ.Params, ast.Var)
@@ -539,9 +531,6 @@ func (r *resolver) walkFieldList(list *ast.FieldList, kind ast.ObjKind) {
// that they may be resolved in the constraint expressions held in the field
// Type.
func (r *resolver) walkTParams(list *ast.FieldList) {
- if list == nil {
- return
- }
r.declareList(list, ast.Typ)
r.resolveList(list)
}
diff --git a/libgo/go/go/parser/resolver_test.go b/libgo/go/go/parser/resolver_test.go
index 625c009..0c06c59 100644
--- a/libgo/go/go/parser/resolver_test.go
+++ b/libgo/go/go/parser/resolver_test.go
@@ -41,11 +41,7 @@ func TestResolution(t *testing.T) {
path := filepath.Join(dir, fi.Name())
src := readFile(path) // panics on failure
var mode Mode
- if strings.HasSuffix(path, ".go2") {
- if !typeparams.Enabled {
- t.Skip("type params are not enabled")
- }
- } else {
+ if !strings.HasSuffix(path, ".go2") {
mode |= typeparams.DisallowParsing
}
file, err := ParseFile(fset, path, src, mode)
diff --git a/libgo/go/go/parser/short_test.go b/libgo/go/go/parser/short_test.go
index 67fef15..90a4ec9 100644
--- a/libgo/go/go/parser/short_test.go
+++ b/libgo/go/go/parser/short_test.go
@@ -119,11 +119,11 @@ var validWithTParamsOnly = []string{
`package p; func _(T[P] /* ERROR "missing element type" */ ) T[P]`,
`package p; type _ struct{ T[P] /* ERROR "missing element type" */ }`,
`package p; type _ struct{ T[struct /* ERROR "expected expression" */ {a, b, c int}] }`,
- `package p; type _ interface{type /* ERROR "expected '}', found 'type'" */ int}`,
- `package p; type _ interface{type /* ERROR "expected '}', found 'type'" */ int, float32; type bool; m(); type string;}`,
+ `package p; type _ interface{int| /* ERROR "expected ';'" */ float32; bool; m(); string;}`,
`package p; type I1[T any /* ERROR "expected ']', found any" */ ] interface{}; type I2 interface{ I1[int] }`,
`package p; type I1[T any /* ERROR "expected ']', found any" */ ] interface{}; type I2[T any] interface{ I1[T] }`,
`package p; type _ interface { f[ /* ERROR "expected ';', found '\['" */ T any]() }`,
+ `package p; type T[P any /* ERROR "expected ']'" */ ] = T0`,
}
func TestValid(t *testing.T) {
@@ -133,9 +133,6 @@ func TestValid(t *testing.T) {
}
})
t.Run("tparams", func(t *testing.T) {
- if !typeparams.Enabled {
- t.Skip("type params are not enabled")
- }
for _, src := range valids {
checkErrors(t, src, src, DeclarationErrors|AllErrors, false)
}
@@ -200,10 +197,12 @@ var invalids = []string{
`package p; func (type /* ERROR "found 'type'" */ T)(T) _()`,
`package p; type _[A+B, /* ERROR "expected ']'" */ ] int`,
- // TODO: this error should be positioned on the ':'
+ // TODO(rfindley): this error should be positioned on the ':'
`package p; var a = a[[]int:[ /* ERROR "expected expression" */ ]int];`,
- // TODO: the compiler error is better here: "cannot parenthesize embedded type"
- `package p; type I1 interface{}; type I2 interface{ (/* ERROR "expected '}', found '\('" */ I1) }`,
+
+ // TODO(rfindley): the compiler error is better here: "cannot parenthesize embedded type"
+ // TODO(rfindley): confirm that parenthesized types should now be accepted.
+ // `package p; type I1 interface{}; type I2 interface{ (/* ERROR "expected '}', found '\('" */ I1) }`,
// issue 8656
`package p; func f() (a b string /* ERROR "missing ','" */ , ok bool)`,
@@ -242,7 +241,6 @@ var invalidNoTParamErrs = []string{
// error messages produced when ParseTypeParams is set.
var invalidTParamErrs = []string{
`package p; type _[_ any] int; var _ = T[] /* ERROR "expected operand" */ {}`,
- `package p; type T[P any] = /* ERROR "cannot be alias" */ T0`,
`package p; var _ func[ /* ERROR "cannot have type parameters" */ T any](T)`,
`package p; func _[]/* ERROR "empty type parameter list" */()`,
@@ -266,9 +264,6 @@ func TestInvalid(t *testing.T) {
}
})
t.Run("tparams", func(t *testing.T) {
- if !typeparams.Enabled {
- t.Skip("type params are not enabled")
- }
for _, src := range invalids {
checkErrors(t, src, src, DeclarationErrors|AllErrors, true)
}
diff --git a/libgo/go/go/parser/testdata/linalg.go2 b/libgo/go/go/parser/testdata/linalg.go2
index fba0d02..7ccb19c 100644
--- a/libgo/go/go/parser/testdata/linalg.go2
+++ b/libgo/go/go/parser/testdata/linalg.go2
@@ -9,10 +9,10 @@ import "math"
// Numeric is type bound that matches any numeric type.
// It would likely be in a constraints package in the standard library.
type Numeric interface {
- type int, int8, int16, int32, int64,
- uint, uint8, uint16, uint32, uint64, uintptr,
- float32, float64,
- complex64, complex128
+ ~int|~int8|~int16|~int32|~int64|
+ ~uint|~uint8|~uint16|~uint32|~uint64|~uintptr|
+ ~float32|~float64|
+ ~complex64|~complex128
}
func DotProduct[T Numeric](s1, s2 []T) T {
@@ -42,14 +42,14 @@ func AbsDifference[T NumericAbs](a, b T) T {
// OrderedNumeric is a type bound that matches numeric types that support the < operator.
type OrderedNumeric interface {
- type int, int8, int16, int32, int64,
- uint, uint8, uint16, uint32, uint64, uintptr,
- float32, float64
+ ~int|~int8|~int16|~int32|~int64|
+ ~uint|~uint8|~uint16|~uint32|~uint64|~uintptr|
+ ~float32|~float64
}
// Complex is a type bound that matches the two complex types, which do not have a < operator.
type Complex interface {
- type complex64, complex128
+ ~complex64|~complex128
}
// OrderedAbs is a helper type that defines an Abs method for
diff --git a/libgo/go/go/parser/testdata/resolution/typeparams.go2 b/libgo/go/go/parser/testdata/resolution/typeparams.go2
index 0ffecd6..8c243af 100644
--- a/libgo/go/go/parser/testdata/resolution/typeparams.go2
+++ b/libgo/go/go/parser/testdata/resolution/typeparams.go2
@@ -15,7 +15,7 @@ type Pair /* =@Pair */ [L /* =@L */, R /* =@R */ any] struct {
var _ = Pair /* @Pair */ [int, string]{}
type Addable /* =@Addable */ interface {
- type int64, float64
+ ~int64|~float64
}
func Add /* =@AddDecl */[T /* =@T */ Addable /* @Addable */](l /* =@l */, r /* =@r */ T /* @T */) T /* @T */ {
@@ -30,7 +30,7 @@ type Receiver /* =@Receiver */[P /* =@P */ any] struct {}
// parameter below.
func (r /* =@recv */ Receiver /* @Receiver */ [P]) m() P {}
-func f /* =@f */[T1 /* =@T1 */ interface{type []T2 /* @T2 */}, T2 /* =@T2 */ any](
+func f /* =@f */[T1 /* =@T1 */ interface{~[]T2 /* @T2 */}, T2 /* =@T2 */ any](
x /* =@x */ T1 /* @T1 */, T1 /* =@T1_duplicate */ y, // Note that this is a bug:
// the duplicate T1 should
// not be allowed.
diff --git a/libgo/go/go/printer/nodes.go b/libgo/go/go/printer/nodes.go
index 913281e..19d4ab6 100644
--- a/libgo/go/go/printer/nodes.go
+++ b/libgo/go/go/printer/nodes.go
@@ -11,7 +11,6 @@ package printer
import (
"bytes"
"go/ast"
- "go/internal/typeparams"
"go/token"
"math"
"strconv"
@@ -383,8 +382,8 @@ func (p *printer) parameters(fields *ast.FieldList, isTypeParam bool) {
}
func (p *printer) signature(sig *ast.FuncType) {
- if tparams := typeparams.Get(sig); tparams != nil {
- p.parameters(tparams, true)
+ if sig.TypeParams != nil {
+ p.parameters(sig.TypeParams, true)
}
if sig.Params != nil {
p.parameters(sig.Params, false)
@@ -472,17 +471,9 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
p.expr(f.Type)
} else { // interface
if len(f.Names) > 0 {
- // type list type or method
- name := f.Names[0] // "type" or method name
+ name := f.Names[0] // method name
p.expr(name)
- if name.Name == "type" {
- // type list type
- p.print(blank)
- p.expr(f.Type)
- } else {
- // method
- p.signature(f.Type.(*ast.FuncType)) // don't print "func"
- }
+ p.signature(f.Type.(*ast.FuncType)) // don't print "func"
} else {
// embedded interface
p.expr(f.Type)
@@ -569,24 +560,10 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
p.setComment(f.Doc)
p.recordLine(&line)
if name != nil {
- // type list type or method
- if name.Name == "type" {
- // type list type
- if name == prev {
- // type is part of a list of types
- p.print(token.COMMA, blank)
- } else {
- // type starts a new list of types
- p.print(name, blank)
- }
- p.expr(f.Type)
- prev = name
- } else {
- // method
- p.expr(name)
- p.signature(f.Type.(*ast.FuncType)) // don't print "func"
- prev = nil
- }
+ // method
+ p.expr(name)
+ p.signature(f.Type.(*ast.FuncType)) // don't print "func"
+ prev = nil
} else {
// embedded interface
p.expr(f.Type)
@@ -871,17 +848,15 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
// TODO(gri): should treat[] like parentheses and undo one level of depth
p.expr1(x.X, token.HighestPrec, 1)
p.print(x.Lbrack, token.LBRACK)
- // Note: we're a bit defensive here to handle the case of a ListExpr of
- // length 1.
- if list := typeparams.UnpackExpr(x.Index); len(list) > 0 {
- if len(list) > 1 {
- p.exprList(x.Lbrack, list, depth+1, commaTerm, x.Rbrack, false)
- } else {
- p.expr0(list[0], depth+1)
- }
- } else {
- p.expr0(x.Index, depth+1)
- }
+ p.expr0(x.Index, depth+1)
+ p.print(x.Rbrack, token.RBRACK)
+
+ case *ast.IndexListExpr:
+ // TODO(gri): as for IndexExpr, should treat [] like parentheses and undo
+ // one level of depth
+ p.expr1(x.X, token.HighestPrec, 1)
+ p.print(x.Lbrack, token.LBRACK)
+ p.exprList(x.Lbrack, x.Indices, depth+1, commaTerm, x.Rbrack, false)
p.print(x.Rbrack, token.RBRACK)
case *ast.SliceExpr:
@@ -1052,7 +1027,7 @@ func normalizedNumber(lit *ast.BasicLit) *ast.BasicLit {
break
}
// remove leading 0's from integer (but not floating-point) imaginary literals
- if x[len(x)-1] == 'i' && strings.IndexByte(x, '.') < 0 && strings.IndexByte(x, 'e') < 0 {
+ if x[len(x)-1] == 'i' && !strings.ContainsAny(x, ".e") {
x = strings.TrimLeft(x, "0_")
if x == "i" {
x = "0i"
@@ -1635,8 +1610,8 @@ func (p *printer) spec(spec ast.Spec, n int, doIndent bool) {
case *ast.TypeSpec:
p.setComment(s.Doc)
p.expr(s.Name)
- if tparams := typeparams.Get(s); tparams != nil {
- p.parameters(tparams, true)
+ if s.TypeParams != nil {
+ p.parameters(s.TypeParams, true)
}
if n == 1 {
p.print(blank)
diff --git a/libgo/go/go/printer/printer.go b/libgo/go/go/printer/printer.go
index ba61f78..e4679b0 100644
--- a/libgo/go/go/printer/printer.go
+++ b/libgo/go/go/printer/printer.go
@@ -104,7 +104,7 @@ func (p *printer) init(cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]
p.cachedPos = -1
}
-func (p *printer) internalError(msg ...interface{}) {
+func (p *printer) internalError(msg ...any) {
if debug {
fmt.Print(p.pos.String() + ": ")
fmt.Println(msg...)
@@ -559,12 +559,9 @@ func stripCommonPrefix(lines []string) {
* Check for vertical "line of stars" and correct prefix accordingly.
*/
lineOfStars := false
- if i := strings.Index(prefix, "*"); i >= 0 {
- // Line of stars present.
- if i > 0 && prefix[i-1] == ' ' {
- i-- // remove trailing blank from prefix so stars remain aligned
- }
- prefix = prefix[0:i]
+ if p, _, ok := strings.Cut(prefix, "*"); ok {
+ // remove trailing blank from prefix so stars remain aligned
+ prefix = strings.TrimSuffix(p, " ")
lineOfStars = true
} else {
// No line of stars present.
@@ -616,8 +613,8 @@ func stripCommonPrefix(lines []string) {
// lines.
last := lines[len(lines)-1]
closing := "*/"
- i := strings.Index(last, closing) // i >= 0 (closing is always present)
- if isBlank(last[0:i]) {
+ before, _, _ := strings.Cut(last, closing) // closing always present
+ if isBlank(before) {
// last line only contains closing */
if lineOfStars {
closing = " */" // add blank to align final star
@@ -881,7 +878,7 @@ func mayCombine(prev token.Token, next byte) (b bool) {
// space for best comment placement. Then, any leftover whitespace is
// printed, followed by the actual token.
//
-func (p *printer) print(args ...interface{}) {
+func (p *printer) print(args ...any) {
for _, arg := range args {
// information about the current arg
var data string
@@ -1078,7 +1075,7 @@ func getLastComment(n ast.Node) *ast.CommentGroup {
return nil
}
-func (p *printer) printNode(node interface{}) error {
+func (p *printer) printNode(node any) error {
// unpack *CommentedNode, if any
var comments []*ast.CommentGroup
if cnode, ok := node.(*CommentedNode); ok {
@@ -1312,7 +1309,7 @@ type Config struct {
}
// fprint implements Fprint and takes a nodesSizes map for setting up the printer state.
-func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{}, nodeSizes map[ast.Node]int) (err error) {
+func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node any, nodeSizes map[ast.Node]int) (err error) {
// print node
var p printer
p.init(cfg, fset, nodeSizes)
@@ -1368,7 +1365,7 @@ func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{
// It may be provided as argument to any of the Fprint functions.
//
type CommentedNode struct {
- Node interface{} // *ast.File, or ast.Expr, ast.Decl, ast.Spec, or ast.Stmt
+ Node any // *ast.File, or ast.Expr, ast.Decl, ast.Spec, or ast.Stmt
Comments []*ast.CommentGroup
}
@@ -1377,7 +1374,7 @@ type CommentedNode struct {
// The node type must be *ast.File, *CommentedNode, []ast.Decl, []ast.Stmt,
// or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt.
//
-func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) error {
+func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node any) error {
return cfg.fprint(output, fset, node, make(map[ast.Node]int))
}
@@ -1386,6 +1383,6 @@ func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{
// Note that gofmt uses tabs for indentation but spaces for alignment;
// use format.Node (package go/format) for output that matches gofmt.
//
-func Fprint(output io.Writer, fset *token.FileSet, node interface{}) error {
+func Fprint(output io.Writer, fset *token.FileSet, node any) error {
return (&Config{Tabwidth: 8}).Fprint(output, fset, node)
}
diff --git a/libgo/go/go/printer/printer_test.go b/libgo/go/go/printer/printer_test.go
index 20c97b8..ff8be4a 100644
--- a/libgo/go/go/printer/printer_test.go
+++ b/libgo/go/go/printer/printer_test.go
@@ -10,7 +10,6 @@ import (
"flag"
"fmt"
"go/ast"
- "go/internal/typeparams"
"go/parser"
"go/token"
"io"
@@ -222,9 +221,6 @@ var data = []entry{
func TestFiles(t *testing.T) {
t.Parallel()
for _, e := range data {
- if !typeparams.Enabled && e.mode&allowTypeParams != 0 {
- continue
- }
source := filepath.Join(dataDir, e.source)
golden := filepath.Join(dataDir, e.golden)
mode := e.mode
diff --git a/libgo/go/go/printer/testdata/generics.golden b/libgo/go/go/printer/testdata/generics.golden
index cc7fbbe..3d95eda 100644
--- a/libgo/go/go/printer/testdata/generics.golden
+++ b/libgo/go/go/printer/testdata/generics.golden
@@ -22,7 +22,7 @@ func f[P1, P2, P3 any](x1 P1, x2 P2, x3 P3) struct{}
func f[P interface{}](x P)
func f[P1, P2, P3 interface {
m1(P1)
- type P2, P3
+ ~P2 | ~P3
}](x1 P1, x2 P2, x3 P3) struct{}
func f[P any](T1[P], T2[P]) T3[P]
@@ -35,9 +35,6 @@ func _() {
_ = []T[P]{}
}
-// properly format one-line type lists
-type _ interface{ type a }
-
-type _ interface {
- type a, b, c
-}
+// type constraint literals with elided interfaces
+func _[P ~int, Q int | string]() {}
+func _[P struct{ f int }, Q *P]() {}
diff --git a/libgo/go/go/printer/testdata/generics.input b/libgo/go/go/printer/testdata/generics.input
index f4571ad..746dfdd 100644
--- a/libgo/go/go/printer/testdata/generics.input
+++ b/libgo/go/go/printer/testdata/generics.input
@@ -20,7 +20,7 @@ func f[P any](x P)
func f[P1, P2, P3 any](x1 P1, x2 P2, x3 P3) struct{}
func f[P interface{}](x P)
-func f[P1, P2, P3 interface{ m1(P1); type P2, P3 }](x1 P1, x2 P2, x3 P3) struct{}
+func f[P1, P2, P3 interface{ m1(P1); ~P2|~P3 }](x1 P1, x2 P2, x3 P3) struct{}
func f[P any](T1[P], T2[P]) T3[P]
func (x T[P]) m()
@@ -32,7 +32,6 @@ func _() {
_ = []T[P]{}
}
-// properly format one-line type lists
-type _ interface { type a }
-
-type _ interface { type a,b,c }
+// type constraint literals with elided interfaces
+func _[P ~int, Q int | string]() {}
+func _[P struct{f int}, Q *P]() {}
diff --git a/libgo/go/go/printer/testdata/parser.go b/libgo/go/go/printer/testdata/parser.go
index fc2812a..7e83797 100644
--- a/libgo/go/go/printer/testdata/parser.go
+++ b/libgo/go/go/printer/testdata/parser.go
@@ -122,7 +122,7 @@ func (p *parser) closeLabelScope() {
p.labelScope = p.labelScope.Outer
}
-func (p *parser) declare(decl interface{}, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) {
+func (p *parser) declare(decl any, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) {
for _, ident := range idents {
assert(ident.Obj == nil, "identifier already declared or resolved")
if ident.Name != "_" {
@@ -200,7 +200,7 @@ func (p *parser) resolve(x ast.Expr) {
// ----------------------------------------------------------------------------
// Parsing support
-func (p *parser) printTrace(a ...interface{}) {
+func (p *parser) printTrace(a ...any) {
const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " +
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "
const n = uint(len(dots))
diff --git a/libgo/go/go/scanner/scanner.go b/libgo/go/go/scanner/scanner.go
index f08e28c..23d8db9 100644
--- a/libgo/go/go/scanner/scanner.go
+++ b/libgo/go/go/scanner/scanner.go
@@ -155,7 +155,7 @@ func (s *Scanner) error(offs int, msg string) {
s.ErrorCount++
}
-func (s *Scanner) errorf(offs int, format string, args ...interface{}) {
+func (s *Scanner) errorf(offs int, format string, args ...any) {
s.error(offs, fmt.Sprintf(format, args...))
}
@@ -969,6 +969,8 @@ scanAgain:
}
case '|':
tok = s.switch3(token.OR, token.OR_ASSIGN, '|', token.LOR)
+ case '~':
+ tok = token.TILDE
default:
// next reports unexpected BOMs - don't repeat
if ch != bom {
diff --git a/libgo/go/go/scanner/scanner_test.go b/libgo/go/go/scanner/scanner_test.go
index db123c3..de45e16 100644
--- a/libgo/go/go/scanner/scanner_test.go
+++ b/libgo/go/go/scanner/scanner_test.go
@@ -40,7 +40,7 @@ type elt struct {
class int
}
-var tokens = [...]elt{
+var tokens = []elt{
// Special tokens
{token.COMMENT, "/* a comment */", special},
{token.COMMENT, "// a comment \n", special},
@@ -149,6 +149,7 @@ var tokens = [...]elt{
{token.RBRACE, "}", operator},
{token.SEMICOLON, ";", operator},
{token.COLON, ":", operator},
+ {token.TILDE, "~", operator},
// Keywords
{token.BREAK, "break", keyword},
diff --git a/libgo/go/go/token/position.go b/libgo/go/go/token/position.go
index 0d7982c..ce4af03 100644
--- a/libgo/go/go/token/position.go
+++ b/libgo/go/go/token/position.go
@@ -540,7 +540,7 @@ func searchInts(a []int, x int) int {
// TODO(gri): Remove this when compilers have caught up.
i, j := 0, len(a)
for i < j {
- h := i + (j-i)>>1 // avoid overflow when computing h
+ h := int(uint(i+j) >> 1) // avoid overflow when computing h
// i ≤ h < j
if a[h] <= x {
i = h + 1
diff --git a/libgo/go/go/token/serialize.go b/libgo/go/go/token/serialize.go
index d0ea345..ffb6990 100644
--- a/libgo/go/go/token/serialize.go
+++ b/libgo/go/go/token/serialize.go
@@ -19,7 +19,7 @@ type serializedFileSet struct {
}
// Read calls decode to deserialize a file set into s; s must not be nil.
-func (s *FileSet) Read(decode func(interface{}) error) error {
+func (s *FileSet) Read(decode func(any) error) error {
var ss serializedFileSet
if err := decode(&ss); err != nil {
return err
@@ -47,7 +47,7 @@ func (s *FileSet) Read(decode func(interface{}) error) error {
}
// Write calls encode to serialize the file set s.
-func (s *FileSet) Write(encode func(interface{}) error) error {
+func (s *FileSet) Write(encode func(any) error) error {
var ss serializedFileSet
s.mutex.Lock()
diff --git a/libgo/go/go/token/serialize_test.go b/libgo/go/go/token/serialize_test.go
index 4e925adb..4aa0b0d 100644
--- a/libgo/go/go/token/serialize_test.go
+++ b/libgo/go/go/token/serialize_test.go
@@ -70,7 +70,7 @@ func equal(p, q *FileSet) error {
func checkSerialize(t *testing.T, p *FileSet) {
var buf bytes.Buffer
- encode := func(x interface{}) error {
+ encode := func(x any) error {
return gob.NewEncoder(&buf).Encode(x)
}
if err := p.Write(encode); err != nil {
@@ -78,7 +78,7 @@ func checkSerialize(t *testing.T, p *FileSet) {
return
}
q := NewFileSet()
- decode := func(x interface{}) error {
+ decode := func(x any) error {
return gob.NewDecoder(&buf).Decode(x)
}
if err := q.Read(decode); err != nil {
diff --git a/libgo/go/go/token/token.go b/libgo/go/go/token/token.go
index 96a1079..d22e575 100644
--- a/libgo/go/go/token/token.go
+++ b/libgo/go/go/token/token.go
@@ -125,6 +125,11 @@ const (
TYPE
VAR
keyword_end
+
+ additional_beg
+ // additional tokens, handled in an ad-hoc manner
+ TILDE
+ additional_end
)
var tokens = [...]string{
@@ -225,6 +230,8 @@ var tokens = [...]string{
SWITCH: "switch",
TYPE: "type",
VAR: "var",
+
+ TILDE: "~",
}
// String returns the string corresponding to the token tok.
@@ -304,7 +311,9 @@ func (tok Token) IsLiteral() bool { return literal_beg < tok && tok < literal_en
// IsOperator returns true for tokens corresponding to operators and
// delimiters; it returns false otherwise.
//
-func (tok Token) IsOperator() bool { return operator_beg < tok && tok < operator_end }
+func (tok Token) IsOperator() bool {
+ return (operator_beg < tok && tok < operator_end) || tok == TILDE
+}
// IsKeyword returns true for tokens corresponding to keywords;
// it returns false otherwise.
diff --git a/libgo/go/go/types/api.go b/libgo/go/go/types/api.go
index d645b48..f0a2280 100644
--- a/libgo/go/go/types/api.go
+++ b/libgo/go/go/types/api.go
@@ -35,6 +35,8 @@ import (
_ "unsafe" // for go:linkname
)
+const allowTypeLists = false
+
// An Error describes a type-checking error; it implements the error interface.
// A "soft" error is an error that still permits a valid interpretation of a
// package (such as "unused variable"); "hard" errors may lead to unpredictable
@@ -61,6 +63,15 @@ func (err Error) Error() string {
return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg)
}
+// An ArgumentError holds an error associated with an argument index.
+type ArgumentError struct {
+ Index int
+ Err error
+}
+
+func (e *ArgumentError) Error() string { return e.Err.Error() }
+func (e *ArgumentError) Unwrap() error { return e.Err }
+
// An Importer resolves import paths to Packages.
//
// CAUTION: This interface does not support the import of locally
@@ -102,12 +113,16 @@ type ImporterFrom interface {
// A Config specifies the configuration for type checking.
// The zero value for Config is a ready-to-use default configuration.
type Config struct {
- // goVersion describes the accepted Go language version. The string
+ // Context is the context used for resolving global identifiers. If nil, the
+ // type checker will initialize this field with a newly created context.
+ Context *Context
+
+ // GoVersion describes the accepted Go language version. The string
// must follow the format "go%d.%d" (e.g. "go1.12") or it must be
// empty; an empty string indicates the latest language version.
// If the format is invalid, invoking the type checker will cause a
// panic.
- goVersion string
+ GoVersion string
// If IgnoreFuncBodies is set, function bodies are not
// type-checked.
@@ -162,7 +177,110 @@ func srcimporter_setUsesCgo(conf *Config) {
conf.go115UsesCgo = true
}
-// The Info struct is found in api_notypeparams.go and api_typeparams.go.
+// Info holds result type information for a type-checked package.
+// Only the information for which a map is provided is collected.
+// If the package has type errors, the collected information may
+// be incomplete.
+type Info struct {
+ // Types maps expressions to their types, and for constant
+ // expressions, also their values. Invalid expressions are
+ // omitted.
+ //
+ // For (possibly parenthesized) identifiers denoting built-in
+ // functions, the recorded signatures are call-site specific:
+ // if the call result is not a constant, the recorded type is
+ // an argument-specific signature. Otherwise, the recorded type
+ // is invalid.
+ //
+ // The Types map does not record the type of every identifier,
+ // only those that appear where an arbitrary expression is
+ // permitted. For instance, the identifier f in a selector
+ // expression x.f is found only in the Selections map, the
+ // identifier z in a variable declaration 'var z int' is found
+ // only in the Defs map, and identifiers denoting packages in
+ // qualified identifiers are collected in the Uses map.
+ Types map[ast.Expr]TypeAndValue
+
+ // Instances maps identifiers denoting parameterized types or functions to
+ // their type arguments and instantiated type.
+ //
+ // For example, Instances will map the identifier for 'T' in the type
+ // instantiation T[int, string] to the type arguments [int, string] and
+ // resulting instantiated *Named type. Given a parameterized function
+ // func F[A any](A), Instances will map the identifier for 'F' in the call
+ // expression F(int(1)) to the inferred type arguments [int], and resulting
+ // instantiated *Signature.
+ //
+ // Invariant: Instantiating Uses[id].Type() with Instances[id].TypeArgs
+ // results in an equivalent of Instances[id].Type.
+ Instances map[*ast.Ident]Instance
+
+ // Defs maps identifiers to the objects they define (including
+ // package names, dots "." of dot-imports, and blank "_" identifiers).
+ // For identifiers that do not denote objects (e.g., the package name
+ // in package clauses, or symbolic variables t in t := x.(type) of
+ // type switch headers), the corresponding objects are nil.
+ //
+ // For an embedded field, Defs returns the field *Var it defines.
+ //
+ // Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()
+ Defs map[*ast.Ident]Object
+
+ // Uses maps identifiers to the objects they denote.
+ //
+ // For an embedded field, Uses returns the *TypeName it denotes.
+ //
+ // Invariant: Uses[id].Pos() != id.Pos()
+ Uses map[*ast.Ident]Object
+
+ // Implicits maps nodes to their implicitly declared objects, if any.
+ // The following node and object types may appear:
+ //
+ // node declared object
+ //
+ // *ast.ImportSpec *PkgName for imports without renames
+ // *ast.CaseClause type-specific *Var for each type switch case clause (incl. default)
+ // *ast.Field anonymous parameter *Var (incl. unnamed results)
+ //
+ Implicits map[ast.Node]Object
+
+ // Selections maps selector expressions (excluding qualified identifiers)
+ // to their corresponding selections.
+ Selections map[*ast.SelectorExpr]*Selection
+
+ // Scopes maps ast.Nodes to the scopes they define. Package scopes are not
+ // associated with a specific node but with all files belonging to a package.
+ // Thus, the package scope can be found in the type-checked Package object.
+ // Scopes nest, with the Universe scope being the outermost scope, enclosing
+ // the package scope, which contains (one or more) files scopes, which enclose
+ // function scopes which in turn enclose statement and function literal scopes.
+ // Note that even though package-level functions are declared in the package
+ // scope, the function scopes are embedded in the file scope of the file
+ // containing the function declaration.
+ //
+ // The following node types may appear in Scopes:
+ //
+ // *ast.File
+ // *ast.FuncType
+ // *ast.TypeSpec
+ // *ast.BlockStmt
+ // *ast.IfStmt
+ // *ast.SwitchStmt
+ // *ast.TypeSwitchStmt
+ // *ast.CaseClause
+ // *ast.CommClause
+ // *ast.ForStmt
+ // *ast.RangeStmt
+ //
+ Scopes map[ast.Node]*Scope
+
+ // InitOrder is the list of package-level initializers in the order in which
+ // they must be executed. Initializers referring to variables related by an
+ // initialization dependency appear in topological order, the others appear
+ // in source order. Variables without an initialization expression do not
+ // appear in this list.
+ InitOrder []*Initializer
+}
// TypeOf returns the type of expression e, or nil if not found.
// Precondition: the Types, Uses and Defs maps are populated.
@@ -254,11 +372,13 @@ func (tv TypeAndValue) HasOk() bool {
return tv.mode == commaok || tv.mode == mapindex
}
-// _Inferred reports the _Inferred type arguments and signature
-// for a parameterized function call that uses type inference.
-type _Inferred struct {
- Targs []Type
- Sig *Signature
+// Instance reports the type arguments and instantiated type for type and
+// function instantiations. For type instantiations, Type will be of dynamic
+// type *Named. For function instantiations, Type will be of dynamic type
+// *Signature.
+type Instance struct {
+ TypeArgs *TypeList
+ Type Type
}
// An Initializer describes a package-level variable, or a list of variables in case
@@ -319,18 +439,26 @@ func ConvertibleTo(V, T Type) bool {
// Implements reports whether type V implements interface T.
func Implements(V Type, T *Interface) bool {
- f, _ := MissingMethod(V, T, true)
- return f == nil
+ if T.Empty() {
+ // All types (even Typ[Invalid]) implement the empty interface.
+ return true
+ }
+ // Checker.implements suppresses errors for invalid types, so we need special
+ // handling here.
+ if V.Underlying() == Typ[Invalid] {
+ return false
+ }
+ return (*Checker)(nil).implements(V, T) == nil
}
// Identical reports whether x and y are identical types.
// Receivers of Signature types are ignored.
func Identical(x, y Type) bool {
- return (*Checker)(nil).identical(x, y)
+ return identical(x, y, true, nil)
}
// IdenticalIgnoreTags reports whether x and y are identical types if tags are ignored.
// Receivers of Signature types are ignored.
func IdenticalIgnoreTags(x, y Type) bool {
- return (*Checker)(nil).identicalIgnoreTags(x, y)
+ return identical(x, y, false, nil)
}
diff --git a/libgo/go/go/types/api_notypeparams.go b/libgo/go/go/types/api_notypeparams.go
deleted file mode 100644
index 9f7cb7e..0000000
--- a/libgo/go/go/types/api_notypeparams.go
+++ /dev/null
@@ -1,104 +0,0 @@
-// 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.
-
-//go:build !typeparams
-// +build !typeparams
-
-package types
-
-import "go/ast"
-
-// Info holds result type information for a type-checked package.
-// Only the information for which a map is provided is collected.
-// If the package has type errors, the collected information may
-// be incomplete.
-type Info struct {
- // Types maps expressions to their types, and for constant
- // expressions, also their values. Invalid expressions are
- // omitted.
- //
- // For (possibly parenthesized) identifiers denoting built-in
- // functions, the recorded signatures are call-site specific:
- // if the call result is not a constant, the recorded type is
- // an argument-specific signature. Otherwise, the recorded type
- // is invalid.
- //
- // The Types map does not record the type of every identifier,
- // only those that appear where an arbitrary expression is
- // permitted. For instance, the identifier f in a selector
- // expression x.f is found only in the Selections map, the
- // identifier z in a variable declaration 'var z int' is found
- // only in the Defs map, and identifiers denoting packages in
- // qualified identifiers are collected in the Uses map.
- Types map[ast.Expr]TypeAndValue
-
- // Defs maps identifiers to the objects they define (including
- // package names, dots "." of dot-imports, and blank "_" identifiers).
- // For identifiers that do not denote objects (e.g., the package name
- // in package clauses, or symbolic variables t in t := x.(type) of
- // type switch headers), the corresponding objects are nil.
- //
- // For an embedded field, Defs returns the field *Var it defines.
- //
- // Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()
- Defs map[*ast.Ident]Object
-
- // Uses maps identifiers to the objects they denote.
- //
- // For an embedded field, Uses returns the *TypeName it denotes.
- //
- // Invariant: Uses[id].Pos() != id.Pos()
- Uses map[*ast.Ident]Object
-
- // Implicits maps nodes to their implicitly declared objects, if any.
- // The following node and object types may appear:
- //
- // node declared object
- //
- // *ast.ImportSpec *PkgName for imports without renames
- // *ast.CaseClause type-specific *Var for each type switch case clause (incl. default)
- // *ast.Field anonymous parameter *Var (incl. unnamed results)
- //
- Implicits map[ast.Node]Object
-
- // Selections maps selector expressions (excluding qualified identifiers)
- // to their corresponding selections.
- Selections map[*ast.SelectorExpr]*Selection
-
- // Scopes maps ast.Nodes to the scopes they define. Package scopes are not
- // associated with a specific node but with all files belonging to a package.
- // Thus, the package scope can be found in the type-checked Package object.
- // Scopes nest, with the Universe scope being the outermost scope, enclosing
- // the package scope, which contains (one or more) files scopes, which enclose
- // function scopes which in turn enclose statement and function literal scopes.
- // Note that even though package-level functions are declared in the package
- // scope, the function scopes are embedded in the file scope of the file
- // containing the function declaration.
- //
- // The following node types may appear in Scopes:
- //
- // *ast.File
- // *ast.FuncType
- // *ast.BlockStmt
- // *ast.IfStmt
- // *ast.SwitchStmt
- // *ast.TypeSwitchStmt
- // *ast.CaseClause
- // *ast.CommClause
- // *ast.ForStmt
- // *ast.RangeStmt
- //
- Scopes map[ast.Node]*Scope
-
- // InitOrder is the list of package-level initializers in the order in which
- // they must be executed. Initializers referring to variables related by an
- // initialization dependency appear in topological order, the others appear
- // in source order. Variables without an initialization expression do not
- // appear in this list.
- InitOrder []*Initializer
-}
-
-func getInferred(info *Info) map[ast.Expr]_Inferred {
- return nil
-}
diff --git a/libgo/go/go/types/api_test.go b/libgo/go/go/types/api_test.go
index 0ff8da6..09d2861 100644
--- a/libgo/go/go/types/api_test.go
+++ b/libgo/go/go/types/api_test.go
@@ -6,6 +6,7 @@ package types_test
import (
"bytes"
+ "errors"
"fmt"
"go/ast"
"go/importer"
@@ -27,8 +28,12 @@ import (
// If source begins with "package generic_" and type parameters are enabled,
// generic code is permitted.
func pkgFor(path, source string, info *Info) (*Package, error) {
- fset := token.NewFileSet()
mode := modeForSource(source)
+ return pkgForMode(path, source, info, mode)
+}
+
+func pkgForMode(path, source string, info *Info, mode parser.Mode) (*Package, error) {
+ fset := token.NewFileSet()
f, err := parser.ParseFile(fset, path, source, mode)
if err != nil {
return nil, err
@@ -123,7 +128,6 @@ func TestValuesInfo(t *testing.T) {
{`package c5d; var _ = string(65)`, `65`, `untyped int`, `65`},
{`package c5e; var _ = string('A')`, `'A'`, `untyped rune`, `65`},
{`package c5f; type T string; var _ = T('A')`, `'A'`, `untyped rune`, `65`},
- {`package c5g; var s uint; var _ = string(1 << s)`, `1 << s`, `untyped int`, ``},
{`package d0; var _ = []byte("foo")`, `"foo"`, `string`, `"foo"`},
{`package d1; var _ = []byte(string("foo"))`, `"foo"`, `string`, `"foo"`},
@@ -153,6 +157,7 @@ func TestValuesInfo(t *testing.T) {
{`package f7b; var _ = -1e-2000i`, `-1e-2000i`, `complex128`, `(0 + 0i)`},
{`package g0; const (a = len([iota]int{}); b; c); const _ = c`, `c`, `int`, `2`}, // issue #22341
+ {`package g1; var(j int32; s int; n = 1.0<<s == j)`, `1.0`, `int32`, `1`}, // issue #48422
}
for _, test := range tests {
@@ -341,19 +346,21 @@ func TestTypesInfo(t *testing.T) {
{broken + `x2; func _() { var a, b string; type x struct {f string}; z := &x{f: a; f: b;}}`, `b`, `string`},
{broken + `x3; var x = panic("");`, `panic`, `func(interface{})`},
{`package x4; func _() { panic("") }`, `panic`, `func(interface{})`},
- {broken + `x5; func _() { var x map[string][...]int; x = map[string][...]int{"": {1,2,3}} }`, `x`, `map[string][-1]int`},
+ {broken + `x5; func _() { var x map[string][...]int; x = map[string][...]int{"": {1,2,3}} }`, `x`, `map[string]invalid type`},
// parameterized functions
- {genericPkg + `p0; func f[T any](T); var _ = f[int]`, `f`, `func[T₁ interface{}](T₁)`},
- {genericPkg + `p1; func f[T any](T); var _ = f[int]`, `f[int]`, `func(int)`},
- {genericPkg + `p2; func f[T any](T); func _() { f(42) }`, `f`, `func[T₁ interface{}](T₁)`},
- {genericPkg + `p3; func f[T any](T); func _() { f(42) }`, `f(42)`, `()`},
+ {genericPkg + `p0; func f[T any](T) {}; var _ = f[int]`, `f`, `func[T any](T)`},
+ {genericPkg + `p1; func f[T any](T) {}; var _ = f[int]`, `f[int]`, `func(int)`},
+ {genericPkg + `p2; func f[T any](T) {}; func _() { f(42) }`, `f`, `func(int)`},
+ {genericPkg + `p3; func f[T any](T) {}; func _() { f[int](42) }`, `f[int]`, `func(int)`},
+ {genericPkg + `p4; func f[T any](T) {}; func _() { f[int](42) }`, `f`, `func[T any](T)`},
+ {genericPkg + `p5; func f[T any](T) {}; func _() { f(42) }`, `f(42)`, `()`},
// type parameters
{genericPkg + `t0; type t[] int; var _ t`, `t`, `generic_t0.t`}, // t[] is a syntax error that is ignored in this test in favor of t
- {genericPkg + `t1; type t[P any] int; var _ t[int]`, `t`, `generic_t1.t[P₁ interface{}]`},
- {genericPkg + `t2; type t[P interface{}] int; var _ t[int]`, `t`, `generic_t2.t[P₁ interface{}]`},
- {genericPkg + `t3; type t[P, Q interface{}] int; var _ t[int, int]`, `t`, `generic_t3.t[P₁, Q₂ interface{}]`},
+ {genericPkg + `t1; type t[P any] int; var _ t[int]`, `t`, `generic_t1.t[P any]`},
+ {genericPkg + `t2; type t[P interface{}] int; var _ t[int]`, `t`, `generic_t2.t[P interface{}]`},
+ {genericPkg + `t3; type t[P, Q interface{}] int; var _ t[int, int]`, `t`, `generic_t3.t[P, Q interface{}]`},
// TODO (rFindley): compare with types2, which resolves the type broken_t4.t[P₁, Q₂ interface{m()}] here
{broken + `t4; type t[P, Q interface{ m() }] int; var _ t[int, int]`, `t`, `broken_t4.t`},
@@ -362,14 +369,38 @@ func TestTypesInfo(t *testing.T) {
{genericPkg + `g0; type t[P any] int; var x struct{ f t[int] }; var _ = x.f`, `x.f`, `generic_g0.t[int]`},
// issue 45096
- {genericPkg + `issue45096; func _[T interface{ type int8, int16, int32 }](x T) { _ = x < 0 }`, `0`, `T₁`},
+ {genericPkg + `issue45096; func _[T interface{ ~int8 | ~int16 | ~int32 }](x T) { _ = x < 0 }`, `0`, `T`},
+
+ // issue 47895
+ {`package p; import "unsafe"; type S struct { f int }; var s S; var _ = unsafe.Offsetof(s.f)`, `s.f`, `int`},
+
+ // issue 50093
+ {genericPkg + `u0a; func _[_ interface{int}]() {}`, `int`, `int`},
+ {genericPkg + `u1a; func _[_ interface{~int}]() {}`, `~int`, `~int`},
+ {genericPkg + `u2a; func _[_ interface{int|string}]() {}`, `int | string`, `int|string`},
+ {genericPkg + `u3a; func _[_ interface{int|string|~bool}]() {}`, `int | string | ~bool`, `int|string|~bool`},
+ {genericPkg + `u3a; func _[_ interface{int|string|~bool}]() {}`, `int | string`, `int|string`},
+ {genericPkg + `u3a; func _[_ interface{int|string|~bool}]() {}`, `~bool`, `~bool`},
+ {genericPkg + `u3a; func _[_ interface{int|string|~float64|~bool}]() {}`, `int | string | ~float64`, `int|string|~float64`},
+
+ {genericPkg + `u0b; func _[_ int]() {}`, `int`, `int`},
+ {genericPkg + `u1b; func _[_ ~int]() {}`, `~int`, `~int`},
+ {genericPkg + `u2b; func _[_ int|string]() {}`, `int | string`, `int|string`},
+ {genericPkg + `u3b; func _[_ int|string|~bool]() {}`, `int | string | ~bool`, `int|string|~bool`},
+ {genericPkg + `u3b; func _[_ int|string|~bool]() {}`, `int | string`, `int|string`},
+ {genericPkg + `u3b; func _[_ int|string|~bool]() {}`, `~bool`, `~bool`},
+ {genericPkg + `u3b; func _[_ int|string|~float64|~bool]() {}`, `int | string | ~float64`, `int|string|~float64`},
+
+ {genericPkg + `u0c; type _ interface{int}`, `int`, `int`},
+ {genericPkg + `u1c; type _ interface{~int}`, `~int`, `~int`},
+ {genericPkg + `u2c; type _ interface{int|string}`, `int | string`, `int|string`},
+ {genericPkg + `u3c; type _ interface{int|string|~bool}`, `int | string | ~bool`, `int|string|~bool`},
+ {genericPkg + `u3c; type _ interface{int|string|~bool}`, `int | string`, `int|string`},
+ {genericPkg + `u3c; type _ interface{int|string|~bool}`, `~bool`, `~bool`},
+ {genericPkg + `u3c; type _ interface{int|string|~float64|~bool}`, `int | string | ~float64`, `int|string|~float64`},
}
for _, test := range tests {
- ResetId() // avoid renumbering of type parameter ids when adding tests
- if strings.HasPrefix(test.src, genericPkg) && !typeparams.Enabled {
- continue
- }
info := Info{Types: make(map[ast.Expr]TypeAndValue)}
var name string
if strings.HasPrefix(test.src, broken) {
@@ -403,6 +434,193 @@ func TestTypesInfo(t *testing.T) {
}
}
+func TestInstanceInfo(t *testing.T) {
+ var tests = []struct {
+ src string
+ name string
+ targs []string
+ typ string
+ }{
+ {`package p0; func f[T any](T) {}; func _() { f(42) }`,
+ `f`,
+ []string{`int`},
+ `func(int)`,
+ },
+ {`package p1; func f[T any](T) T { panic(0) }; func _() { f('@') }`,
+ `f`,
+ []string{`rune`},
+ `func(rune) rune`,
+ },
+ {`package p2; func f[T any](...T) T { panic(0) }; func _() { f(0i) }`,
+ `f`,
+ []string{`complex128`},
+ `func(...complex128) complex128`,
+ },
+ {`package p3; func f[A, B, C any](A, *B, []C) {}; func _() { f(1.2, new(string), []byte{}) }`,
+ `f`,
+ []string{`float64`, `string`, `byte`},
+ `func(float64, *string, []byte)`,
+ },
+ {`package p4; func f[A, B any](A, *B, ...[]B) {}; func _() { f(1.2, new(byte)) }`,
+ `f`,
+ []string{`float64`, `byte`},
+ `func(float64, *byte, ...[]byte)`,
+ },
+
+ {`package s1; func f[T any, P interface{~*T}](x T) {}; func _(x string) { f(x) }`,
+ `f`,
+ []string{`string`, `*string`},
+ `func(x string)`,
+ },
+ {`package s2; func f[T any, P interface{~*T}](x []T) {}; func _(x []int) { f(x) }`,
+ `f`,
+ []string{`int`, `*int`},
+ `func(x []int)`,
+ },
+ {`package s3; type C[T any] interface{~chan<- T}; func f[T any, P C[T]](x []T) {}; func _(x []int) { f(x) }`,
+ `f`,
+ []string{`int`, `chan<- int`},
+ `func(x []int)`,
+ },
+ {`package s4; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T) {}; func _(x []int) { f(x) }`,
+ `f`,
+ []string{`int`, `chan<- int`, `chan<- []*chan<- int`},
+ `func(x []int)`,
+ },
+
+ {`package t1; func f[T any, P interface{~*T}]() T { panic(0) }; func _() { _ = f[string] }`,
+ `f`,
+ []string{`string`, `*string`},
+ `func() string`,
+ },
+ {`package t2; func f[T any, P interface{~*T}]() T { panic(0) }; func _() { _ = (f[string]) }`,
+ `f`,
+ []string{`string`, `*string`},
+ `func() string`,
+ },
+ {`package t3; type C[T any] interface{~chan<- T}; func f[T any, P C[T]]() []T { return nil }; func _() { _ = f[int] }`,
+ `f`,
+ []string{`int`, `chan<- int`},
+ `func() []int`,
+ },
+ {`package t4; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = f[int] }`,
+ `f`,
+ []string{`int`, `chan<- int`, `chan<- []*chan<- int`},
+ `func() []int`,
+ },
+ {`package i0; import "lib"; func _() { lib.F(42) }`,
+ `F`,
+ []string{`int`},
+ `func(int)`,
+ },
+ {`package type0; type T[P interface{~int}] struct{ x P }; var _ T[int]`,
+ `T`,
+ []string{`int`},
+ `struct{x int}`,
+ },
+ {`package type1; type T[P interface{~int}] struct{ x P }; var _ (T[int])`,
+ `T`,
+ []string{`int`},
+ `struct{x int}`,
+ },
+ {`package type2; type T[P interface{~int}] struct{ x P }; var _ T[(int)]`,
+ `T`,
+ []string{`int`},
+ `struct{x int}`,
+ },
+ {`package type3; type T[P1 interface{~[]P2}, P2 any] struct{ x P1; y P2 }; var _ T[[]int, int]`,
+ `T`,
+ []string{`[]int`, `int`},
+ `struct{x []int; y int}`,
+ },
+ {`package type4; import "lib"; var _ lib.T[int]`,
+ `T`,
+ []string{`int`},
+ `[]int`,
+ },
+ }
+
+ for _, test := range tests {
+ const lib = `package lib
+
+func F[P any](P) {}
+
+type T[P any] []P
+`
+
+ imports := make(testImporter)
+ conf := Config{Importer: imports}
+ instances := make(map[*ast.Ident]Instance)
+ uses := make(map[*ast.Ident]Object)
+ makePkg := func(src string) *Package {
+ f, err := parser.ParseFile(fset, "p.go", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ pkg, err := conf.Check("", fset, []*ast.File{f}, &Info{Instances: instances, Uses: uses})
+ if err != nil {
+ t.Fatal(err)
+ }
+ imports[pkg.Name()] = pkg
+ return pkg
+ }
+ makePkg(lib)
+ pkg := makePkg(test.src)
+
+ // look for instance information
+ var targs []Type
+ var typ Type
+ for ident, inst := range instances {
+ if ExprString(ident) == test.name {
+ for i := 0; i < inst.TypeArgs.Len(); i++ {
+ targs = append(targs, inst.TypeArgs.At(i))
+ }
+ typ = inst.Type
+
+ // Check that we can find the corresponding parameterized type.
+ ptype := uses[ident].Type()
+ lister, _ := ptype.(interface{ TypeParams() *TypeParamList })
+ if lister == nil || lister.TypeParams().Len() == 0 {
+ t.Errorf("package %s: info.Types[%v] = %v, want parameterized type", pkg.Name(), ident, ptype)
+ continue
+ }
+
+ // Verify the invariant that re-instantiating the generic type with
+ // TypeArgs results in an equivalent type.
+ inst2, err := Instantiate(nil, ptype, targs, true)
+ if err != nil {
+ t.Errorf("Instantiate(%v, %v) failed: %v", ptype, targs, err)
+ }
+ if !Identical(inst.Type, inst2) {
+ t.Errorf("%v and %v are not identical", inst.Type, inst2)
+ }
+ break
+ }
+ }
+ if targs == nil {
+ t.Errorf("package %s: no instance information found for %s", pkg.Name(), test.name)
+ continue
+ }
+
+ // check that type arguments are correct
+ if len(targs) != len(test.targs) {
+ t.Errorf("package %s: got %d type arguments; want %d", pkg.Name(), len(targs), len(test.targs))
+ continue
+ }
+ for i, targ := range targs {
+ if got := targ.String(); got != test.targs[i] {
+ t.Errorf("package %s, %d. type argument: got %s; want %s", pkg.Name(), i, got, test.targs[i])
+ continue
+ }
+ }
+
+ // check that the types match
+ if got := typ.Underlying().String(); got != test.typ {
+ t.Errorf("package %s: got %s; want %s", pkg.Name(), got, test.typ)
+ }
+ }
+}
+
func TestDefsInfo(t *testing.T) {
var tests = []struct {
src string
@@ -416,18 +634,13 @@ func TestDefsInfo(t *testing.T) {
{`package p4; func f()`, `f`, `func p4.f()`},
{`package p5; func f() int { x, _ := 1, 2; return x }`, `_`, `var _ int`},
- // generic types must be sanitized
- // (need to use sufficiently nested types to provoke unexpanded types)
- {genericPkg + `g0; type t[P any] P; const x = t[int](42)`, `x`, `const generic_g0.x generic_g0.t[int]`},
- {genericPkg + `g1; type t[P any] P; var x = t[int](42)`, `x`, `var generic_g1.x generic_g1.t[int]`},
- {genericPkg + `g2; type t[P any] P; type x struct{ f t[int] }`, `x`, `type generic_g2.x struct{f generic_g2.t[int]}`},
- {genericPkg + `g3; type t[P any] P; func f(x struct{ f t[string] }); var g = f`, `g`, `var generic_g3.g func(x struct{f generic_g3.t[string]})`},
+ // Tests using generics.
+ {`package generic_g0; type x[T any] int`, `x`, `type generic_g0.x[T any] int`},
+ {`package generic_g1; func f[T any]() {}`, `f`, `func generic_g1.f[T any]()`},
+ {`package generic_g2; type x[T any] int; func (*x[_]) m() {}`, `m`, `func (*generic_g2.x[_]).m()`},
}
for _, test := range tests {
- if strings.HasPrefix(test.src, genericPkg) && !typeparams.Enabled {
- continue
- }
info := Info{
Defs: make(map[*ast.Ident]Object),
}
@@ -464,18 +677,35 @@ func TestUsesInfo(t *testing.T) {
{`package p3; func _() { type _ x }; type x int`, `x`, `type p3.x int`},
{`package p4; func _() { _ = f }; func f()`, `f`, `func p4.f()`},
- // generic types must be sanitized
- // (need to use sufficiently nested types to provoke unexpanded types)
- {genericPkg + `g0; func _() { _ = x }; type t[P any] P; const x = t[int](42)`, `x`, `const generic_g0.x generic_g0.t[int]`},
- {genericPkg + `g1; func _() { _ = x }; type t[P any] P; var x = t[int](42)`, `x`, `var generic_g1.x generic_g1.t[int]`},
- {genericPkg + `g2; func _() { type _ x }; type t[P any] P; type x struct{ f t[int] }`, `x`, `type generic_g2.x struct{f generic_g2.t[int]}`},
- {genericPkg + `g3; func _() { _ = f }; type t[P any] P; func f(x struct{ f t[string] })`, `f`, `func generic_g3.f(x struct{f generic_g3.t[string]})`},
+ // Tests using generics.
+ {`package generic_g0; func _[T any]() { _ = x }; const x = 42`, `x`, `const generic_g0.x untyped int`},
+ {`package generic_g1; func _[T any](x T) { }`, `T`, `type parameter T any`},
+ {`package generic_g2; type N[A any] int; var _ N[int]`, `N`, `type generic_g2.N[A any] int`},
+ {`package generic_g3; type N[A any] int; func (N[_]) m() {}`, `N`, `type generic_g3.N[A any] int`},
+
+ // Uses of fields are instantiated.
+ {`package generic_s1; type N[A any] struct{ a A }; var f = N[int]{}.a`, `a`, `field a int`},
+ {`package generic_s1; type N[A any] struct{ a A }; func (r N[B]) m(b B) { r.a = b }`, `a`, `field a B`},
+
+ // Uses of methods are uses of the instantiated method.
+ {`package generic_m0; type N[A any] int; func (r N[B]) m() { r.n() }; func (N[C]) n() {}`, `n`, `func (generic_m0.N[B]).n()`},
+ {`package generic_m1; type N[A any] int; func (r N[B]) m() { }; var f = N[int].m`, `m`, `func (generic_m1.N[int]).m()`},
+ {`package generic_m2; func _[A any](v interface{ m() A }) { v.m() }`, `m`, `func (interface).m() A`},
+ {`package generic_m3; func f[A any]() interface{ m() A } { return nil }; var _ = f[int]().m()`, `m`, `func (interface).m() int`},
+ {`package generic_m4; type T[A any] func() interface{ m() A }; var x T[int]; var y = x().m`, `m`, `func (interface).m() int`},
+ {`package generic_m5; type T[A any] interface{ m() A }; func _[B any](t T[B]) { t.m() }`, `m`, `func (generic_m5.T[B]).m() B`},
+ {`package generic_m6; type T[A any] interface{ m() }; func _[B any](t T[B]) { t.m() }`, `m`, `func (generic_m6.T[B]).m()`},
+ {`package generic_m7; type T[A any] interface{ m() A }; func _(t T[int]) { t.m() }`, `m`, `func (generic_m7.T[int]).m() int`},
+ {`package generic_m8; type T[A any] interface{ m() }; func _(t T[int]) { t.m() }`, `m`, `func (generic_m8.T[int]).m()`},
+ {`package generic_m9; type T[A any] interface{ m() }; func _(t T[int]) { _ = t.m }`, `m`, `func (generic_m9.T[int]).m()`},
+ {
+ `package generic_m10; type E[A any] interface{ m() }; type T[B any] interface{ E[B]; n() }; func _(t T[int]) { t.m() }`,
+ `m`,
+ `func (generic_m10.E[int]).m()`,
+ },
}
for _, test := range tests {
- if strings.HasPrefix(test.src, genericPkg) && !typeparams.Enabled {
- continue
- }
info := Info{
Uses: make(map[*ast.Ident]Object),
}
@@ -485,8 +715,10 @@ func TestUsesInfo(t *testing.T) {
var use Object
for id, obj := range info.Uses {
if id.Name == test.obj {
+ if use != nil {
+ panic(fmt.Sprintf("multiple uses of %q", id.Name))
+ }
use = obj
- break
}
}
if use == nil {
@@ -500,6 +732,90 @@ func TestUsesInfo(t *testing.T) {
}
}
+func TestGenericMethodInfo(t *testing.T) {
+ src := `package p
+
+type N[A any] int
+
+func (r N[B]) m() { r.m(); r.n() }
+
+func (r *N[C]) n() { }
+`
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "p.go", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ info := Info{
+ Defs: make(map[*ast.Ident]Object),
+ Uses: make(map[*ast.Ident]Object),
+ Selections: make(map[*ast.SelectorExpr]*Selection),
+ }
+ var conf Config
+ pkg, err := conf.Check("p", fset, []*ast.File{f}, &info)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ N := pkg.Scope().Lookup("N").Type().(*Named)
+
+ // Find the generic methods stored on N.
+ gm, gn := N.Method(0), N.Method(1)
+ if gm.Name() == "n" {
+ gm, gn = gn, gm
+ }
+
+ // Collect objects from info.
+ var dm, dn *Func // the declared methods
+ var dmm, dmn *Func // the methods used in the body of m
+ for _, decl := range f.Decls {
+ fdecl, ok := decl.(*ast.FuncDecl)
+ if !ok {
+ continue
+ }
+ def := info.Defs[fdecl.Name].(*Func)
+ switch fdecl.Name.Name {
+ case "m":
+ dm = def
+ ast.Inspect(fdecl.Body, func(n ast.Node) bool {
+ if call, ok := n.(*ast.CallExpr); ok {
+ sel := call.Fun.(*ast.SelectorExpr)
+ use := info.Uses[sel.Sel].(*Func)
+ selection := info.Selections[sel]
+ if selection.Kind() != MethodVal {
+ t.Errorf("Selection kind = %v, want %v", selection.Kind(), MethodVal)
+ }
+ if selection.Obj() != use {
+ t.Errorf("info.Selections contains %v, want %v", selection.Obj(), use)
+ }
+ switch sel.Sel.Name {
+ case "m":
+ dmm = use
+ case "n":
+ dmn = use
+ }
+ }
+ return true
+ })
+ case "n":
+ dn = def
+ }
+ }
+
+ if gm != dm {
+ t.Errorf(`N.Method(...) returns %v for "m", but Info.Defs has %v`, gm, dm)
+ }
+ if gn != dn {
+ t.Errorf(`N.Method(...) returns %v for "m", but Info.Defs has %v`, gm, dm)
+ }
+ if dmm != dm {
+ t.Errorf(`Inside "m", r.m uses %v, want the defined func %v`, dmm, dm)
+ }
+ if dmn == dn {
+ t.Errorf(`Inside "m", r.n uses %v, want a func distinct from %v`, dmm, dm)
+ }
+}
+
func TestImplicitsInfo(t *testing.T) {
testenv.MustHaveGoBuild(t)
@@ -520,6 +836,17 @@ func TestImplicitsInfo(t *testing.T) {
{`package p8; func f(int) {}`, "field: var int"},
{`package p9; func f() (complex64) { return 0 }`, "field: var complex64"},
{`package p10; type T struct{}; func (*T) f() {}`, "field: var *p10.T"},
+
+ // Tests using generics.
+ {`package generic_f0; func f[T any](x int) {}`, ""}, // no Implicits entry
+ {`package generic_f1; func f[T any](int) {}`, "field: var int"},
+ {`package generic_f2; func f[T any](T) {}`, "field: var T"},
+ {`package generic_f3; func f[T any]() (complex64) { return 0 }`, "field: var complex64"},
+ {`package generic_f4; func f[T any](t T) (T) { return t }`, "field: var T"},
+ {`package generic_t0; type T[A any] struct{}; func (*T[_]) f() {}`, "field: var *generic_t0.T[_]"},
+ {`package generic_t1; type T[A any] struct{}; func _(x interface{}) { switch t := x.(type) { case T[int]: _ = t } }`, "caseClause: var t generic_t1.T[int]"},
+ {`package generic_t2; type T[A any] struct{}; func _[P any](x interface{}) { switch t := x.(type) { case T[P]: _ = t } }`, "caseClause: var t generic_t2.T[P]"},
+ {`package generic_t3; func _[P any](x interface{}) { switch t := x.(type) { case P: _ = t } }`, "caseClause: var t P"},
}
for _, test := range tests {
@@ -1229,6 +1556,18 @@ var _ = a.C2
makePkg("main", mainSrc) // don't crash when type-checking this package
}
+func TestLookupFieldOrMethodOnNil(t *testing.T) {
+ // LookupFieldOrMethod on a nil type is expected to produce a run-time panic.
+ defer func() {
+ const want = "LookupFieldOrMethod on nil type"
+ p := recover()
+ if s, ok := p.(string); !ok || s != want {
+ t.Fatalf("got %v, want %s", p, want)
+ }
+ }()
+ LookupFieldOrMethod(nil, false, nil, "")
+}
+
func TestLookupFieldOrMethod(t *testing.T) {
t.Skip("skipping for gccgo--no importer")
// Test cases assume a lookup of the form a.f or x.f, where a stands for an
@@ -1435,6 +1774,13 @@ func F(){
}
}
+// newDefined creates a new defined type named T with the given underlying type.
+// Helper function for use with TestIncompleteInterfaces only.
+func newDefined(underlying Type) *Named {
+ tname := NewTypeName(token.NoPos, nil, "T", nil)
+ return NewNamed(tname, underlying, nil)
+}
+
func TestConvertibleTo(t *testing.T) {
for _, test := range []struct {
v, t Type
@@ -1442,6 +1788,7 @@ func TestConvertibleTo(t *testing.T) {
}{
{Typ[Int], Typ[Int], true},
{Typ[Int], Typ[Float32], true},
+ {Typ[Int], Typ[String], true},
{newDefined(Typ[Int]), Typ[Int], true},
{newDefined(new(Struct)), new(Struct), true},
{newDefined(Typ[Int]), new(Struct), false},
@@ -1449,8 +1796,7 @@ func TestConvertibleTo(t *testing.T) {
{NewSlice(Typ[Int]), NewPointer(NewArray(Typ[Int], 10)), true},
{NewSlice(Typ[Int]), NewArray(Typ[Int], 10), false},
{NewSlice(Typ[Int]), NewPointer(NewArray(Typ[Uint], 10)), false},
- // Untyped string values are not permitted by the spec, so the below
- // behavior is undefined.
+ // Untyped string values are not permitted by the spec, so the behavior below is undefined.
{Typ[UntypedString], Typ[String], true},
} {
if got := ConvertibleTo(test.v, test.t); got != test.want {
@@ -1482,6 +1828,56 @@ func TestAssignableTo(t *testing.T) {
}
}
+func TestIdentical(t *testing.T) {
+ // For each test, we compare the types of objects X and Y in the source.
+ tests := []struct {
+ src string
+ want bool
+ }{
+ // Basic types.
+ {"var X int; var Y int", true},
+ {"var X int; var Y string", false},
+
+ // TODO: add more tests for complex types.
+
+ // Named types.
+ {"type X int; type Y int", false},
+
+ // Aliases.
+ {"type X = int; type Y = int", true},
+
+ // Functions.
+ {`func X(int) string { return "" }; func Y(int) string { return "" }`, true},
+ {`func X() string { return "" }; func Y(int) string { return "" }`, false},
+ {`func X(int) string { return "" }; func Y(int) {}`, false},
+
+ // Generic functions. Type parameters should be considered identical modulo
+ // renaming. See also issue #49722.
+ {`func X[P ~int](){}; func Y[Q ~int]() {}`, true},
+ {`func X[P1 any, P2 ~*P1](){}; func Y[Q1 any, Q2 ~*Q1]() {}`, true},
+ {`func X[P1 any, P2 ~[]P1](){}; func Y[Q1 any, Q2 ~*Q1]() {}`, false},
+ {`func X[P ~int](P){}; func Y[Q ~int](Q) {}`, true},
+ {`func X[P ~string](P){}; func Y[Q ~int](Q) {}`, false},
+ {`func X[P ~int]([]P){}; func Y[Q ~int]([]Q) {}`, true},
+ }
+
+ for _, test := range tests {
+ pkg, err := pkgForMode("test", "package p;"+test.src, nil, 0)
+ if err != nil {
+ t.Errorf("%s: incorrect test case: %s", test.src, err)
+ continue
+ }
+ X := pkg.Scope().Lookup("X")
+ Y := pkg.Scope().Lookup("Y")
+ if X == nil || Y == nil {
+ t.Fatal("test must declare both X and Y")
+ }
+ if got := Identical(X.Type(), Y.Type()); got != test.want {
+ t.Errorf("Identical(%s, %s) = %t, want %t", X.Type(), Y.Type(), got, test.want)
+ }
+ }
+}
+
func TestIdentical_issue15173(t *testing.T) {
// Identical should allow nil arguments and be symmetric.
for _, test := range []struct {
@@ -1499,6 +1895,48 @@ func TestIdentical_issue15173(t *testing.T) {
}
}
+func TestIdenticalUnions(t *testing.T) {
+ tname := NewTypeName(token.NoPos, nil, "myInt", nil)
+ myInt := NewNamed(tname, Typ[Int], nil)
+ tmap := map[string]*Term{
+ "int": NewTerm(false, Typ[Int]),
+ "~int": NewTerm(true, Typ[Int]),
+ "string": NewTerm(false, Typ[String]),
+ "~string": NewTerm(true, Typ[String]),
+ "myInt": NewTerm(false, myInt),
+ }
+ makeUnion := func(s string) *Union {
+ parts := strings.Split(s, "|")
+ var terms []*Term
+ for _, p := range parts {
+ term := tmap[p]
+ if term == nil {
+ t.Fatalf("missing term %q", p)
+ }
+ terms = append(terms, term)
+ }
+ return NewUnion(terms)
+ }
+ for _, test := range []struct {
+ x, y string
+ want bool
+ }{
+ // These tests are just sanity checks. The tests for type sets and
+ // interfaces provide much more test coverage.
+ {"int|~int", "~int", true},
+ {"myInt|~int", "~int", true},
+ {"int|string", "string|int", true},
+ {"int|int|string", "string|int", true},
+ {"myInt|string", "int|string", false},
+ } {
+ x := makeUnion(test.x)
+ y := makeUnion(test.y)
+ if got := Identical(x, y); got != test.want {
+ t.Errorf("Identical(%v, %v) = %t", test.x, test.y, got)
+ }
+ }
+}
+
func TestIssue15305(t *testing.T) {
const src = "package p; func f() int16; var _ = f(undef)"
fset := token.NewFileSet()
@@ -1718,3 +2156,211 @@ func f(x T) T { return foo.F(x) }
}
}
}
+
+func TestInstantiate(t *testing.T) {
+ // eventually we like more tests but this is a start
+ const src = "package p; type T[P any] *T[P]"
+ pkg, err := pkgForMode(".", src, nil, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // type T should have one type parameter
+ T := pkg.Scope().Lookup("T").Type().(*Named)
+ if n := T.TypeParams().Len(); n != 1 {
+ t.Fatalf("expected 1 type parameter; found %d", n)
+ }
+
+ // instantiation should succeed (no endless recursion)
+ // even with a nil *Checker
+ res, err := Instantiate(nil, T, []Type{Typ[Int]}, false)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // instantiated type should point to itself
+ if p := res.Underlying().(*Pointer).Elem(); p != res {
+ t.Fatalf("unexpected result type: %s points to %s", res, p)
+ }
+}
+
+func TestInstantiateErrors(t *testing.T) {
+ tests := []struct {
+ src string // by convention, T must be the type being instantiated
+ targs []Type
+ wantAt int // -1 indicates no error
+ }{
+ {"type T[P interface{~string}] int", []Type{Typ[Int]}, 0},
+ {"type T[P1 interface{int}, P2 interface{~string}] int", []Type{Typ[Int], Typ[Int]}, 1},
+ {"type T[P1 any, P2 interface{~[]P1}] int", []Type{Typ[Int], NewSlice(Typ[String])}, 1},
+ {"type T[P1 interface{~[]P2}, P2 any] int", []Type{NewSlice(Typ[String]), Typ[Int]}, 0},
+ }
+
+ for _, test := range tests {
+ src := "package p; " + test.src
+ pkg, err := pkgForMode(".", src, nil, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ T := pkg.Scope().Lookup("T").Type().(*Named)
+
+ _, err = Instantiate(nil, T, test.targs, true)
+ if err == nil {
+ t.Fatalf("Instantiate(%v, %v) returned nil error, want non-nil", T, test.targs)
+ }
+
+ var argErr *ArgumentError
+ if !errors.As(err, &argErr) {
+ t.Fatalf("Instantiate(%v, %v): error is not an *ArgumentError", T, test.targs)
+ }
+
+ if argErr.Index != test.wantAt {
+ t.Errorf("Instantate(%v, %v): error at index %d, want index %d", T, test.targs, argErr.Index, test.wantAt)
+ }
+ }
+}
+
+func TestArgumentErrorUnwrapping(t *testing.T) {
+ var err error = &ArgumentError{
+ Index: 1,
+ Err: Error{Msg: "test"},
+ }
+ var e Error
+ if !errors.As(err, &e) {
+ t.Fatalf("error %v does not wrap types.Error", err)
+ }
+ if e.Msg != "test" {
+ t.Errorf("e.Msg = %q, want %q", e.Msg, "test")
+ }
+}
+
+func TestInstanceIdentity(t *testing.T) {
+ imports := make(testImporter)
+ conf := Config{Importer: imports}
+ makePkg := func(src string) {
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ name := f.Name.Name
+ pkg, err := conf.Check(name, fset, []*ast.File{f}, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ imports[name] = pkg
+ }
+ makePkg(genericPkg + `lib; type T[P any] struct{}`)
+ makePkg(genericPkg + `a; import "generic_lib"; var A generic_lib.T[int]`)
+ makePkg(genericPkg + `b; import "generic_lib"; var B generic_lib.T[int]`)
+ a := imports["generic_a"].Scope().Lookup("A")
+ b := imports["generic_b"].Scope().Lookup("B")
+ if !Identical(a.Type(), b.Type()) {
+ t.Errorf("mismatching types: a.A: %s, b.B: %s", a.Type(), b.Type())
+ }
+}
+
+func TestImplements(t *testing.T) {
+ const src = `
+package p
+
+type EmptyIface interface{}
+
+type I interface {
+ m()
+}
+
+type C interface {
+ m()
+ ~int
+}
+
+type Integer interface{
+ int8 | int16 | int32 | int64
+}
+
+type EmptyTypeSet interface{
+ Integer
+ ~string
+}
+
+type N1 int
+func (N1) m() {}
+
+type N2 int
+func (*N2) m() {}
+
+type N3 int
+func (N3) m(int) {}
+
+type N4 string
+func (N4) m()
+
+type Bad Bad // invalid type
+`
+
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "p.go", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ conf := Config{Error: func(error) {}}
+ pkg, _ := conf.Check(f.Name.Name, fset, []*ast.File{f}, nil)
+
+ scope := pkg.Scope()
+ var (
+ EmptyIface = scope.Lookup("EmptyIface").Type().Underlying().(*Interface)
+ I = scope.Lookup("I").Type().(*Named)
+ II = I.Underlying().(*Interface)
+ C = scope.Lookup("C").Type().(*Named)
+ CI = C.Underlying().(*Interface)
+ Integer = scope.Lookup("Integer").Type().Underlying().(*Interface)
+ EmptyTypeSet = scope.Lookup("EmptyTypeSet").Type().Underlying().(*Interface)
+ N1 = scope.Lookup("N1").Type()
+ N1p = NewPointer(N1)
+ N2 = scope.Lookup("N2").Type()
+ N2p = NewPointer(N2)
+ N3 = scope.Lookup("N3").Type()
+ N4 = scope.Lookup("N4").Type()
+ Bad = scope.Lookup("Bad").Type()
+ )
+
+ tests := []struct {
+ t Type
+ i *Interface
+ want bool
+ }{
+ {I, II, true},
+ {I, CI, false},
+ {C, II, true},
+ {C, CI, true},
+ {Typ[Int8], Integer, true},
+ {Typ[Int64], Integer, true},
+ {Typ[String], Integer, false},
+ {EmptyTypeSet, II, true},
+ {EmptyTypeSet, EmptyTypeSet, true},
+ {Typ[Int], EmptyTypeSet, false},
+ {N1, II, true},
+ {N1, CI, true},
+ {N1p, II, true},
+ {N1p, CI, false},
+ {N2, II, false},
+ {N2, CI, false},
+ {N2p, II, true},
+ {N2p, CI, false},
+ {N3, II, false},
+ {N3, CI, false},
+ {N4, II, true},
+ {N4, CI, false},
+ {Bad, II, false},
+ {Bad, CI, false},
+ {Bad, EmptyIface, true},
+ }
+
+ for _, test := range tests {
+ if got := Implements(test.t, test.i); got != test.want {
+ t.Errorf("Implements(%s, %s) = %t, want %t", test.t, test.i, got, test.want)
+ }
+ }
+}
diff --git a/libgo/go/go/types/api_typeparams.go b/libgo/go/go/types/api_typeparams.go
deleted file mode 100644
index ed744c4..0000000
--- a/libgo/go/go/types/api_typeparams.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// 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.
-
-//go:build typeparams
-// +build typeparams
-
-package types
-
-import (
- "go/ast"
-)
-
-type (
- Inferred = _Inferred
- Sum = _Sum
- TypeParam = _TypeParam
-)
-
-func NewSum(types []Type) Type { return _NewSum(types) }
-
-func (s *Signature) TParams() []*TypeName { return s._TParams() }
-func (s *Signature) SetTParams(tparams []*TypeName) { s._SetTParams(tparams) }
-
-func (t *Interface) HasTypeList() bool { return t._HasTypeList() }
-func (t *Interface) IsComparable() bool { return t._IsComparable() }
-func (t *Interface) IsConstraint() bool { return t._IsConstraint() }
-
-func (t *Named) TParams() []*TypeName { return t._TParams() }
-func (t *Named) TArgs() []Type { return t._TArgs() }
-func (t *Named) SetTArgs(args []Type) { t._SetTArgs(args) }
-
-// Info is documented in api_notypeparams.go.
-type Info struct {
- Types map[ast.Expr]TypeAndValue
-
- // Inferred maps calls of parameterized functions that use type inference to
- // the Inferred type arguments and signature of the function called. The
- // recorded "call" expression may be an *ast.CallExpr (as in f(x)), or an
- // *ast.IndexExpr (s in f[T]).
- Inferred map[ast.Expr]_Inferred
-
- Defs map[*ast.Ident]Object
- Uses map[*ast.Ident]Object
- Implicits map[ast.Node]Object
- Selections map[*ast.SelectorExpr]*Selection
- Scopes map[ast.Node]*Scope
- InitOrder []*Initializer
-}
-
-func getInferred(info *Info) map[ast.Expr]_Inferred {
- return info.Inferred
-}
diff --git a/libgo/go/go/types/api_typeparams_test.go b/libgo/go/go/types/api_typeparams_test.go
deleted file mode 100644
index 15c9bf0..0000000
--- a/libgo/go/go/types/api_typeparams_test.go
+++ /dev/null
@@ -1,139 +0,0 @@
-// 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.
-
-//go:build typeparams
-// +build typeparams
-
-package types_test
-
-import (
- "fmt"
- "go/ast"
- "testing"
-
- . "go/types"
-)
-
-func TestInferredInfo(t *testing.T) {
- var tests = []struct {
- src string
- fun string
- targs []string
- sig string
- }{
- {genericPkg + `p0; func f[T any](T); func _() { f(42) }`,
- `f`,
- []string{`int`},
- `func(int)`,
- },
- {genericPkg + `p1; func f[T any](T) T; func _() { f('@') }`,
- `f`,
- []string{`rune`},
- `func(rune) rune`,
- },
- {genericPkg + `p2; func f[T any](...T) T; func _() { f(0i) }`,
- `f`,
- []string{`complex128`},
- `func(...complex128) complex128`,
- },
- {genericPkg + `p3; func f[A, B, C any](A, *B, []C); func _() { f(1.2, new(string), []byte{}) }`,
- `f`,
- []string{`float64`, `string`, `byte`},
- `func(float64, *string, []byte)`,
- },
- {genericPkg + `p4; func f[A, B any](A, *B, ...[]B); func _() { f(1.2, new(byte)) }`,
- `f`,
- []string{`float64`, `byte`},
- `func(float64, *byte, ...[]byte)`,
- },
-
- {genericPkg + `s1; func f[T any, P interface{type *T}](x T); func _(x string) { f(x) }`,
- `f`,
- []string{`string`, `*string`},
- `func(x string)`,
- },
- {genericPkg + `s2; func f[T any, P interface{type *T}](x []T); func _(x []int) { f(x) }`,
- `f`,
- []string{`int`, `*int`},
- `func(x []int)`,
- },
- {genericPkg + `s3; type C[T any] interface{type chan<- T}; func f[T any, P C[T]](x []T); func _(x []int) { f(x) }`,
- `f`,
- []string{`int`, `chan<- int`},
- `func(x []int)`,
- },
- {genericPkg + `s4; type C[T any] interface{type chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T); func _(x []int) { f(x) }`,
- `f`,
- []string{`int`, `chan<- int`, `chan<- []*chan<- int`},
- `func(x []int)`,
- },
-
- {genericPkg + `t1; func f[T any, P interface{type *T}]() T; func _() { _ = f[string] }`,
- `f`,
- []string{`string`, `*string`},
- `func() string`,
- },
- {genericPkg + `t2; type C[T any] interface{type chan<- T}; func f[T any, P C[T]]() []T; func _() { _ = f[int] }`,
- `f`,
- []string{`int`, `chan<- int`},
- `func() []int`,
- },
- {genericPkg + `t3; type C[T any] interface{type chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T; func _() { _ = f[int] }`,
- `f`,
- []string{`int`, `chan<- int`, `chan<- []*chan<- int`},
- `func() []int`,
- },
- }
-
- for _, test := range tests {
- info := Info{}
- info.Inferred = make(map[ast.Expr]Inferred)
- name, err := mayTypecheck(t, "InferredInfo", test.src, &info)
- if err != nil {
- t.Errorf("package %s: %v", name, err)
- continue
- }
-
- // look for inferred type arguments and signature
- var targs []Type
- var sig *Signature
- for call, inf := range info.Inferred {
- var fun ast.Expr
- switch x := call.(type) {
- case *ast.CallExpr:
- fun = x.Fun
- case *ast.IndexExpr:
- fun = x.X
- default:
- panic(fmt.Sprintf("unexpected call expression type %T", call))
- }
- if ExprString(fun) == test.fun {
- targs = inf.Targs
- sig = inf.Sig
- break
- }
- }
- if targs == nil {
- t.Errorf("package %s: no inferred information found for %s", name, test.fun)
- continue
- }
-
- // check that type arguments are correct
- if len(targs) != len(test.targs) {
- t.Errorf("package %s: got %d type arguments; want %d", name, len(targs), len(test.targs))
- continue
- }
- for i, targ := range targs {
- if got := targ.String(); got != test.targs[i] {
- t.Errorf("package %s, %d. type argument: got %s; want %s", name, i, got, test.targs[i])
- continue
- }
- }
-
- // check that signature is correct
- if got := sig.String(); got != test.sig {
- t.Errorf("package %s: got %s; want %s", name, got, test.sig)
- }
- }
-}
diff --git a/libgo/go/go/types/array.go b/libgo/go/go/types/array.go
new file mode 100644
index 0000000..5b28474
--- /dev/null
+++ b/libgo/go/go/types/array.go
@@ -0,0 +1,25 @@
+// Copyright 2011 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.
+
+package types
+
+// An Array represents an array type.
+type Array struct {
+ len int64
+ elem Type
+}
+
+// NewArray returns a new array type for the given element type and length.
+// A negative length indicates an unknown length.
+func NewArray(elem Type, len int64) *Array { return &Array{len: len, elem: elem} }
+
+// Len returns the length of array a.
+// A negative result indicates an unknown length.
+func (a *Array) Len() int64 { return a.len }
+
+// Elem returns element type of array a.
+func (a *Array) Elem() Type { return a.elem }
+
+func (t *Array) Underlying() Type { return t }
+func (t *Array) String() string { return TypeString(t, nil) }
diff --git a/libgo/go/go/types/assignments.go b/libgo/go/go/types/assignments.go
index 18eae62..f75b8b6 100644
--- a/libgo/go/go/types/assignments.go
+++ b/libgo/go/go/types/assignments.go
@@ -7,8 +7,9 @@
package types
import (
+ "fmt"
"go/ast"
- "go/token"
+ "strings"
)
// assignment reports whether x can be assigned to a variable of type T,
@@ -37,7 +38,7 @@ func (check *Checker) assignment(x *operand, T Type, context string) {
// bool, rune, int, float64, complex128 or string respectively, depending
// on whether the value is a boolean, rune, integer, floating-point,
// complex, or string constant."
- if T == nil || IsInterface(T) {
+ if T == nil || IsInterface(T) && !isTypeParam(T) {
if T == nil && x.typ == Typ[UntypedNil] {
check.errorf(x, _UntypedNil, "use of untyped nil in %s", context)
x.mode = invalid
@@ -71,8 +72,8 @@ func (check *Checker) assignment(x *operand, T Type, context string) {
}
// A generic (non-instantiated) function value cannot be assigned to a variable.
- if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 {
- check.errorf(x, _Todo, "cannot use generic function %s without instantiation in %s", x, context)
+ if sig, _ := under(x.typ).(*Signature); sig != nil && sig.TypeParams().Len() > 0 {
+ check.errorf(x, _WrongTypeArgCount, "cannot use generic function %s without instantiation in %s", x, context)
}
// spec: "If a left-hand side is the blank identifier, any typed or
@@ -84,10 +85,18 @@ func (check *Checker) assignment(x *operand, T Type, context string) {
reason := ""
if ok, code := x.assignableTo(check, T, &reason); !ok {
- if reason != "" {
- check.errorf(x, code, "cannot use %s as %s value in %s: %s", x, T, context, reason)
+ if compilerErrorMessages {
+ if reason != "" {
+ check.errorf(x, code, "cannot use %s as type %s in %s:\n\t%s", x, T, context, reason)
+ } else {
+ check.errorf(x, code, "cannot use %s as type %s in %s", x, T, context)
+ }
} else {
- check.errorf(x, code, "cannot use %s as %s value in %s", x, T, context)
+ if reason != "" {
+ check.errorf(x, code, "cannot use %s as %s value in %s: %s", x, T, context, reason)
+ } else {
+ check.errorf(x, code, "cannot use %s as %s value in %s", x, T, context)
+ }
}
x.mode = invalid
}
@@ -229,14 +238,89 @@ func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type {
return x.typ
}
-// If returnPos is valid, initVars is called to type-check the assignment of
-// return expressions, and returnPos is the position of the return statement.
-func (check *Checker) initVars(lhs []*Var, origRHS []ast.Expr, returnPos token.Pos) {
- rhs, commaOk := check.exprList(origRHS, len(lhs) == 2 && !returnPos.IsValid())
+// operandTypes returns the list of types for the given operands.
+func operandTypes(list []*operand) (res []Type) {
+ for _, x := range list {
+ res = append(res, x.typ)
+ }
+ return res
+}
+
+// varTypes returns the list of types for the given variables.
+func varTypes(list []*Var) (res []Type) {
+ for _, x := range list {
+ res = append(res, x.typ)
+ }
+ return res
+}
+
+// typesSummary returns a string of the form "(t1, t2, ...)" where the
+// ti's are user-friendly string representations for the given types.
+// If variadic is set and the last type is a slice, its string is of
+// the form "...E" where E is the slice's element type.
+func (check *Checker) typesSummary(list []Type, variadic bool) string {
+ var res []string
+ for i, t := range list {
+ var s string
+ switch {
+ case t == nil:
+ fallthrough // should not happen but be cautious
+ case t == Typ[Invalid]:
+ s = "<T>"
+ case isUntyped(t):
+ if isNumeric(t) {
+ // Do not imply a specific type requirement:
+ // "have number, want float64" is better than
+ // "have untyped int, want float64" or
+ // "have int, want float64".
+ s = "number"
+ } else {
+ // If we don't have a number, omit the "untyped" qualifier
+ // for compactness.
+ s = strings.Replace(t.(*Basic).name, "untyped ", "", -1)
+ }
+ case variadic && i == len(list)-1:
+ s = check.sprintf("...%s", t.(*Slice).elem)
+ }
+ if s == "" {
+ s = check.sprintf("%s", t)
+ }
+ res = append(res, s)
+ }
+ return "(" + strings.Join(res, ", ") + ")"
+}
+
+func (check *Checker) assignError(rhs []ast.Expr, nvars, nvals int) {
+ measure := func(x int, unit string) string {
+ s := fmt.Sprintf("%d %s", x, unit)
+ if x != 1 {
+ s += "s"
+ }
+ return s
+ }
+
+ vars := measure(nvars, "variable")
+ vals := measure(nvals, "value")
+ rhs0 := rhs[0]
+
+ if len(rhs) == 1 {
+ if call, _ := unparen(rhs0).(*ast.CallExpr); call != nil {
+ check.errorf(rhs0, _WrongAssignCount, "assignment mismatch: %s but %s returns %s", vars, call.Fun, vals)
+ return
+ }
+ }
+ check.errorf(rhs0, _WrongAssignCount, "assignment mismatch: %s but %s", vars, vals)
+}
+
+// If returnStmt != nil, initVars is called to type-check the assignment
+// of return expressions, and returnStmt is the return statement.
+func (check *Checker) initVars(lhs []*Var, origRHS []ast.Expr, returnStmt ast.Stmt) {
+ rhs, commaOk := check.exprList(origRHS, len(lhs) == 2 && returnStmt == nil)
if len(lhs) != len(rhs) {
// invalidate lhs
for _, obj := range lhs {
+ obj.used = true // avoid declared but not used errors
if obj.typ == nil {
obj.typ = Typ[Invalid]
}
@@ -247,16 +331,32 @@ func (check *Checker) initVars(lhs []*Var, origRHS []ast.Expr, returnPos token.P
return
}
}
- if returnPos.IsValid() {
- check.errorf(atPos(returnPos), _WrongResultCount, "wrong number of return values (want %d, got %d)", len(lhs), len(rhs))
+ if returnStmt != nil {
+ var at positioner = returnStmt
+ qualifier := "not enough"
+ if len(rhs) > len(lhs) {
+ at = rhs[len(lhs)].expr // report at first extra value
+ qualifier = "too many"
+ } else if len(rhs) > 0 {
+ at = rhs[len(rhs)-1].expr // report at last value
+ }
+ check.errorf(at, _WrongResultCount, "%s return values\n\thave %s\n\twant %s",
+ qualifier,
+ check.typesSummary(operandTypes(rhs), false),
+ check.typesSummary(varTypes(lhs), false),
+ )
return
}
- check.errorf(rhs[0], _WrongAssignCount, "cannot initialize %d variables with %d values", len(lhs), len(rhs))
+ if compilerErrorMessages {
+ check.assignError(origRHS, len(lhs), len(rhs))
+ } else {
+ check.errorf(rhs[0], _WrongAssignCount, "cannot initialize %d variables with %d values", len(lhs), len(rhs))
+ }
return
}
context := "assignment"
- if returnPos.IsValid() {
+ if returnStmt != nil {
context = "return statement"
}
@@ -285,7 +385,11 @@ func (check *Checker) assignVars(lhs, origRHS []ast.Expr) {
return
}
}
- check.errorf(rhs[0], _WrongAssignCount, "cannot assign %d values to %d variables", len(rhs), len(lhs))
+ if compilerErrorMessages {
+ check.assignError(origRHS, len(lhs), len(rhs))
+ } else {
+ check.errorf(rhs[0], _WrongAssignCount, "cannot assign %d values to %d variables", len(rhs), len(lhs))
+ }
return
}
@@ -364,7 +468,7 @@ func (check *Checker) shortVarDecl(pos positioner, lhs, rhs []ast.Expr) {
}
}
- check.initVars(lhsVars, rhs, token.NoPos)
+ check.initVars(lhsVars, rhs, nil)
// process function literals in rhs expressions before scope changes
check.processDelayed(top)
diff --git a/libgo/go/go/types/basic.go b/libgo/go/go/types/basic.go
new file mode 100644
index 0000000..215923f
--- /dev/null
+++ b/libgo/go/go/types/basic.go
@@ -0,0 +1,82 @@
+// Copyright 2011 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.
+
+package types
+
+// BasicKind describes the kind of basic type.
+type BasicKind int
+
+const (
+ Invalid BasicKind = iota // type is invalid
+
+ // predeclared types
+ Bool
+ Int
+ Int8
+ Int16
+ Int32
+ Int64
+ Uint
+ Uint8
+ Uint16
+ Uint32
+ Uint64
+ Uintptr
+ Float32
+ Float64
+ Complex64
+ Complex128
+ String
+ UnsafePointer
+
+ // types for untyped values
+ UntypedBool
+ UntypedInt
+ UntypedRune
+ UntypedFloat
+ UntypedComplex
+ UntypedString
+ UntypedNil
+
+ // aliases
+ Byte = Uint8
+ Rune = Int32
+)
+
+// BasicInfo is a set of flags describing properties of a basic type.
+type BasicInfo int
+
+// Properties of basic types.
+const (
+ IsBoolean BasicInfo = 1 << iota
+ IsInteger
+ IsUnsigned
+ IsFloat
+ IsComplex
+ IsString
+ IsUntyped
+
+ IsOrdered = IsInteger | IsFloat | IsString
+ IsNumeric = IsInteger | IsFloat | IsComplex
+ IsConstType = IsBoolean | IsNumeric | IsString
+)
+
+// A Basic represents a basic type.
+type Basic struct {
+ kind BasicKind
+ info BasicInfo
+ name string
+}
+
+// Kind returns the kind of basic type b.
+func (b *Basic) Kind() BasicKind { return b.kind }
+
+// Info returns information about properties of basic type b.
+func (b *Basic) Info() BasicInfo { return b.info }
+
+// Name returns the name of basic type b.
+func (b *Basic) Name() string { return b.name }
+
+func (t *Basic) Underlying() Type { return t }
+func (t *Basic) String() string { return TypeString(t, nil) }
diff --git a/libgo/go/go/types/builtins.go b/libgo/go/go/types/builtins.go
index 2a2d54d..35a2d1a 100644
--- a/libgo/go/go/types/builtins.go
+++ b/libgo/go/go/types/builtins.go
@@ -47,7 +47,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
default:
// make argument getter
xlist, _ := check.exprList(call.Args, false)
- arg = func(x *operand, i int) { *x = *xlist[i]; x.typ = expand(x.typ) }
+ arg = func(x *operand, i int) { *x = *xlist[i] }
nargs = len(xlist)
// evaluate first argument, if present
if nargs > 0 {
@@ -83,7 +83,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
// of S and the respective parameter passing rules apply."
S := x.typ
var T Type
- if s := asSlice(S); s != nil {
+ if s, _ := structuralType(S).(*Slice); s != nil {
T = s.elem
} else {
check.invalidArg(x, _InvalidAppend, "%s is not a slice", x)
@@ -102,7 +102,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
if x.mode == invalid {
return
}
- if isString(x.typ) {
+ if t := structuralString(x.typ); t != nil && isString(t) {
if check.Types != nil {
sig := makeSig(S, S, x.typ)
sig.variadic = true
@@ -130,7 +130,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
arg(&x, i)
xlist = append(xlist, &x)
}
- check.arguments(call, sig, nil, xlist) // discard result (we know the result type)
+ check.arguments(call, sig, nil, xlist, nil) // discard result (we know the result type)
// ok to continue even if check.arguments reported errors
x.mode = value
@@ -145,7 +145,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
mode := invalid
var typ Type
var val constant.Value
- switch typ = implicitArrayDeref(optype(x.typ)); t := typ.(type) {
+ switch typ = arrayPtrDeref(under(x.typ)); t := typ.(type) {
case *Basic:
if isString(t) && id == _Len {
if x.mode == constant_ {
@@ -179,9 +179,12 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
mode = value
}
- case *_Sum:
- if t.is(func(t Type) bool {
- switch t := under(t).(type) {
+ case *Interface:
+ if !isTypeParam(x.typ) {
+ break
+ }
+ if t.typeSet().underIs(func(t Type) bool {
+ switch t := arrayPtrDeref(t).(type) {
case *Basic:
if isString(t) && id == _Len {
return true
@@ -217,19 +220,23 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
case _Close:
// close(c)
- c := asChan(x.typ)
- if c == nil {
- check.invalidArg(x, _InvalidClose, "%s is not a channel", x)
- return
- }
- if c.dir == RecvOnly {
- check.invalidArg(x, _InvalidClose, "%s must not be a receive-only channel", x)
+ if !underIs(x.typ, func(u Type) bool {
+ uch, _ := u.(*Chan)
+ if uch == nil {
+ check.invalidOp(x, _InvalidClose, "cannot close non-channel %s", x)
+ return false
+ }
+ if uch.dir == RecvOnly {
+ check.invalidOp(x, _InvalidClose, "cannot close receive-only channel %s", x)
+ return false
+ }
+ return true
+ }) {
return
}
-
x.mode = novalue
if check.Types != nil {
- check.recordBuiltinType(call.Fun, makeSig(nil, c))
+ check.recordBuiltinType(call.Fun, makeSig(nil, x.typ))
}
case _Complex:
@@ -286,14 +293,16 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
}
// both argument types must be identical
- if !check.identical(x.typ, y.typ) {
+ if !Identical(x.typ, y.typ) {
check.invalidArg(x, _InvalidComplex, "mismatched types %s and %s", x.typ, y.typ)
return
}
// the argument types must be of floating-point type
- f := func(x Type) Type {
- if t := asBasic(x); t != nil {
+ // (applyTypeFunc never calls f with a type parameter)
+ f := func(typ Type) Type {
+ assert(!isTypeParam(typ))
+ if t, _ := under(typ).(*Basic); t != nil {
switch t.kind {
case Float32:
return Typ[Complex64]
@@ -326,33 +335,26 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
case _Copy:
// copy(x, y []T) int
- var dst Type
- if t := asSlice(x.typ); t != nil {
- dst = t.elem
- }
+ dst, _ := structuralType(x.typ).(*Slice)
var y operand
arg(&y, 1)
if y.mode == invalid {
return
}
- var src Type
- switch t := optype(y.typ).(type) {
- case *Basic:
- if isString(y.typ) {
- src = universeByte
- }
- case *Slice:
- src = t.elem
+ src0 := structuralString(y.typ)
+ if src0 != nil && isString(src0) {
+ src0 = NewSlice(universeByte)
}
+ src, _ := src0.(*Slice)
if dst == nil || src == nil {
check.invalidArg(x, _InvalidCopy, "copy expects slice arguments; found %s and %s", x, &y)
return
}
- if !check.identical(dst, src) {
- check.invalidArg(x, _InvalidCopy, "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src)
+ if !Identical(dst.elem, src.elem) {
+ check.errorf(x, _InvalidCopy, "arguments to copy %s and %s have different element types %s and %s", x, &y, dst.elem, src.elem)
return
}
@@ -363,25 +365,40 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
x.typ = Typ[Int]
case _Delete:
- // delete(m, k)
- m := asMap(x.typ)
- if m == nil {
- check.invalidArg(x, _InvalidDelete, "%s is not a map", x)
+ // delete(map_, key)
+ // map_ must be a map type or a type parameter describing map types.
+ // The key cannot be a type parameter for now.
+ map_ := x.typ
+ var key Type
+ if !underIs(map_, func(u Type) bool {
+ map_, _ := u.(*Map)
+ if map_ == nil {
+ check.invalidArg(x, _InvalidDelete, "%s is not a map", x)
+ return false
+ }
+ if key != nil && !Identical(map_.key, key) {
+ check.invalidArg(x, _InvalidDelete, "maps of %s must have identical key types", x)
+ return false
+ }
+ key = map_.key
+ return true
+ }) {
return
}
+
arg(x, 1) // k
if x.mode == invalid {
return
}
- check.assignment(x, m.key, "argument to delete")
+ check.assignment(x, key, "argument to delete")
if x.mode == invalid {
return
}
x.mode = novalue
if check.Types != nil {
- check.recordBuiltinType(call.Fun, makeSig(nil, m, m.key))
+ check.recordBuiltinType(call.Fun, makeSig(nil, map_, key))
}
case _Imag, _Real:
@@ -410,8 +427,10 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
}
// the argument must be of complex type
- f := func(x Type) Type {
- if t := asBasic(x); t != nil {
+ // (applyTypeFunc never calls f with a type parameter)
+ f := func(typ Type) Type {
+ assert(!isTypeParam(typ))
+ if t, _ := under(typ).(*Basic); t != nil {
switch t.kind {
case Complex64:
return Typ[Float32]
@@ -460,39 +479,21 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
return
}
- min, max := -1, 10
- var valid func(t Type) bool
- valid = func(t Type) bool {
- var m int
- switch t := optype(t).(type) {
- case *Slice:
- m = 2
- case *Map, *Chan:
- m = 1
- case *_Sum:
- return t.is(valid)
- default:
- return false
- }
- if m > min {
- min = m
- }
- if m+1 < max {
- max = m + 1
- }
- return true
- }
-
- if !valid(T) {
+ var min int // minimum number of arguments
+ switch structuralType(T).(type) {
+ case *Slice:
+ min = 2
+ case *Map, *Chan:
+ min = 1
+ case nil:
+ check.errorf(arg0, _InvalidMake, "cannot make %s: no structural type", arg0)
+ return
+ default:
check.invalidArg(arg0, _InvalidMake, "cannot make %s; type must be slice, map, or channel", arg0)
return
}
- if nargs < min || max < nargs {
- if min == max {
- check.errorf(call, _WrongArgCount, "%v expects %d arguments; found %d", call, min, nargs)
- } else {
- check.errorf(call, _WrongArgCount, "%v expects %d or %d arguments; found %d", call, min, max, nargs)
- }
+ if nargs < min || min+1 < nargs {
+ check.invalidOp(call, _WrongArgCount, "%v expects %d or %d arguments; found %d", call, min, min+1, nargs)
return
}
@@ -612,19 +613,22 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
case _Alignof:
// unsafe.Alignof(x T) uintptr
- if asTypeParam(x.typ) != nil {
- check.invalidOp(call, _Todo, "unsafe.Alignof undefined for %s", x)
- return
- }
check.assignment(x, nil, "argument to unsafe.Alignof")
if x.mode == invalid {
return
}
- x.mode = constant_
- x.val = constant.MakeInt64(check.conf.alignof(x.typ))
+ if hasVarSize(x.typ) {
+ x.mode = value
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], x.typ))
+ }
+ } else {
+ x.mode = constant_
+ x.val = constant.MakeInt64(check.conf.alignof(x.typ))
+ // result is constant - no need to record signature
+ }
x.typ = Typ[Uintptr]
- // result is constant - no need to record signature
case _Offsetof:
// unsafe.Offsetof(x T) uintptr, where x must be a selector
@@ -644,7 +648,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
base := derefStructPtr(x.typ)
sel := selx.Sel.Name
- obj, index, indirect := check.lookupFieldOrMethod(base, false, check.pkg, sel)
+ obj, index, indirect := LookupFieldOrMethod(base, false, check.pkg, sel)
switch obj.(type) {
case nil:
check.invalidArg(x, _MissingFieldOrMethod, "%s has no single field %s", base, sel)
@@ -662,30 +666,52 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
return
}
- // TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)?
+ // TODO(gri) Should we pass x.typ instead of base (and have indirect report if derefStructPtr indirected)?
check.recordSelection(selx, FieldVal, base, obj, index, false)
- offs := check.conf.offsetof(base, index)
- x.mode = constant_
- x.val = constant.MakeInt64(offs)
+ // record the selector expression (was bug - issue #47895)
+ {
+ mode := value
+ if x.mode == variable || indirect {
+ mode = variable
+ }
+ check.record(&operand{mode, selx, obj.Type(), nil, 0})
+ }
+
+ // The field offset is considered a variable even if the field is declared before
+ // the part of the struct which is variable-sized. This makes both the rules
+ // simpler and also permits (or at least doesn't prevent) a compiler from re-
+ // arranging struct fields if it wanted to.
+ if hasVarSize(base) {
+ x.mode = value
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], obj.Type()))
+ }
+ } else {
+ x.mode = constant_
+ x.val = constant.MakeInt64(check.conf.offsetof(base, index))
+ // result is constant - no need to record signature
+ }
x.typ = Typ[Uintptr]
- // result is constant - no need to record signature
case _Sizeof:
// unsafe.Sizeof(x T) uintptr
- if asTypeParam(x.typ) != nil {
- check.invalidOp(call, _Todo, "unsafe.Sizeof undefined for %s", x)
- return
- }
check.assignment(x, nil, "argument to unsafe.Sizeof")
if x.mode == invalid {
return
}
- x.mode = constant_
- x.val = constant.MakeInt64(check.conf.sizeof(x.typ))
+ if hasVarSize(x.typ) {
+ x.mode = value
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], x.typ))
+ }
+ } else {
+ x.mode = constant_
+ x.val = constant.MakeInt64(check.conf.sizeof(x.typ))
+ // result is constant - no need to record signature
+ }
x.typ = Typ[Uintptr]
- // result is constant - no need to record signature
case _Slice:
// unsafe.Slice(ptr *T, len IntegerType) []T
@@ -694,7 +720,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
return
}
- typ := asPointer(x.typ)
+ typ, _ := under(x.typ).(*Pointer)
if typ == nil {
check.invalidArg(x, _InvalidUnsafeSlice, "%s is not a pointer", x)
return
@@ -744,7 +770,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
var t operand
x1 := x
for _, arg := range call.Args {
- check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T))
+ check.rawExpr(x1, arg, nil, false) // permit trace for types, e.g.: new(trace(T))
check.dump("%v: %s", x1.Pos(), x1)
x1 = &t // use incoming x only for first argument
}
@@ -757,6 +783,25 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
return true
}
+// hasVarSize reports if the size of type t is variable due to type parameters.
+func hasVarSize(t Type) bool {
+ switch u := under(t).(type) {
+ case *Array:
+ return hasVarSize(u.elem)
+ case *Struct:
+ for _, f := range u.fields {
+ if hasVarSize(f.typ) {
+ return true
+ }
+ }
+ case *Interface:
+ return isTypeParam(t)
+ case *Named, *Union:
+ unreachable()
+ }
+ return false
+}
+
// applyTypeFunc applies f to x. If x is a type parameter,
// the result is a type parameter constrained by an new
// interface bound. The type bounds for that interface
@@ -765,13 +810,16 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
// applyTypeFunc returns nil.
// If x is not a type parameter, the result is f(x).
func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
- if tp := asTypeParam(x); tp != nil {
+ if tp, _ := x.(*TypeParam); tp != nil {
// Test if t satisfies the requirements for the argument
// type and collect possible result types at the same time.
- var rtypes []Type
- if !tp.Bound().is(func(x Type) bool {
- if r := f(x); r != nil {
- rtypes = append(rtypes, r)
+ var terms []*Term
+ if !tp.is(func(t *term) bool {
+ if t == nil {
+ return false
+ }
+ if r := f(t.typ); r != nil {
+ terms = append(terms, NewTerm(t.tilde, r))
return true
}
return false
@@ -779,11 +827,12 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
return nil
}
- // construct a suitable new type parameter
- tpar := NewTypeName(token.NoPos, nil /* = Universe pkg */, "<type parameter>", nil)
- ptyp := check.newTypeParam(tpar, 0, &emptyInterface) // assigns type to tpar as a side-effect
- tsum := _NewSum(rtypes)
- ptyp.bound = &Interface{types: tsum, allMethods: markComplete, allTypes: tsum}
+ // Construct a suitable new type parameter for the result type.
+ // The type parameter is placed in the current package so export/import
+ // works as expected.
+ tpar := NewTypeName(token.NoPos, check.pkg, "<type parameter>", nil)
+ ptyp := check.newTypeParam(tpar, NewInterfaceType(nil, []Type{NewUnion(terms)})) // assigns type to tpar as a side-effect
+ ptyp.index = tp.index
return ptyp
}
@@ -807,12 +856,11 @@ func makeSig(res Type, args ...Type) *Signature {
return &Signature{params: params, results: result}
}
-// implicitArrayDeref returns A if typ is of the form *A and A is an array;
+// arrayPtrDeref returns A if typ is of the form *A and A is an array;
// otherwise it returns typ.
-//
-func implicitArrayDeref(typ Type) Type {
+func arrayPtrDeref(typ Type) Type {
if p, ok := typ.(*Pointer); ok {
- if a := asArray(p.base); a != nil {
+ if a, _ := under(p.base).(*Array); a != nil {
return a
}
}
diff --git a/libgo/go/go/types/builtins_test.go b/libgo/go/go/types/builtins_test.go
index c4830d9..cfe075c 100644
--- a/libgo/go/go/types/builtins_test.go
+++ b/libgo/go/go/types/builtins_test.go
@@ -113,12 +113,15 @@ var builtinCalls = []struct {
{"Alignof", `_ = unsafe.Alignof(0)`, `invalid type`}, // constant
{"Alignof", `var x struct{}; _ = unsafe.Alignof(x)`, `invalid type`}, // constant
+ {"Alignof", `var x P; _ = unsafe.Alignof(x)`, `func(P) uintptr`},
{"Offsetof", `var x struct{f bool}; _ = unsafe.Offsetof(x.f)`, `invalid type`}, // constant
{"Offsetof", `var x struct{_ int; f bool}; _ = unsafe.Offsetof((&x).f)`, `invalid type`}, // constant
+ {"Offsetof", `var x struct{_ int; f P}; _ = unsafe.Offsetof((&x).f)`, `func(P) uintptr`},
{"Sizeof", `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant
{"Sizeof", `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant
+ {"Sizeof", `var x P; _ = unsafe.Sizeof(x)`, `func(P) uintptr`},
{"Slice", `var p *int; _ = unsafe.Slice(p, 1)`, `func(*int, int) []int`},
{"Slice", `var p *byte; var n uintptr; _ = unsafe.Slice(p, n)`, `func(*byte, uintptr) []byte`},
@@ -151,10 +154,12 @@ func TestBuiltinSignatures(t *testing.T) {
}
}
+// parseGenericSrc in types2 is not necessary. We can just parse in testBuiltinSignature below.
+
func testBuiltinSignature(t *testing.T, name, src0, want string) {
t.Skip("skipping for gccgo--no default importer")
- src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _() { %s }`, src0)
+ src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _[P any]() { %s }`, src0)
f, err := parser.ParseFile(fset, "", src, 0)
if err != nil {
t.Errorf("%s: %s", src0, err)
diff --git a/libgo/go/go/types/call.go b/libgo/go/go/types/call.go
index 336b526..aa87c48 100644
--- a/libgo/go/go/types/call.go
+++ b/libgo/go/go/types/call.go
@@ -16,76 +16,102 @@ 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 *ast.IndexExpr) {
- xlist := typeparams.UnpackExpr(inst.Index)
- targs := check.typeList(xlist)
+func (check *Checker) funcInst(x *operand, ix *typeparams.IndexExpr) {
+ if !check.allowVersion(check.pkg, 1, 18) {
+ check.softErrorf(inNode(ix.Orig, ix.Lbrack), _UnsupportedFeature, "function instantiation requires go1.18 or later")
+ }
+
+ targs := check.typeList(ix.Indices)
if targs == nil {
x.mode = invalid
- x.expr = inst
+ x.expr = ix.Orig
return
}
- assert(len(targs) == len(xlist))
+ assert(len(targs) == len(ix.Indices))
// check number of type arguments (got) vs number of type parameters (want)
sig := x.typ.(*Signature)
- got, want := len(targs), len(sig.tparams)
+ got, want := len(targs), sig.TypeParams().Len()
if got > want {
- check.errorf(xlist[got-1], _Todo, "got %d type arguments but want %d", got, want)
+ check.errorf(ix.Indices[got-1], _WrongTypeArgCount, "got %d type arguments but want %d", got, want)
x.mode = invalid
- x.expr = inst
+ x.expr = ix.Orig
return
}
- // if we don't have enough type arguments, try type inference
- inferred := false
-
if got < want {
- targs = check.infer(inst, sig.tparams, targs, nil, nil, true)
+ targs = check.infer(ix.Orig, sig.TypeParams().list(), targs, nil, nil)
if targs == nil {
// error was already reported
x.mode = invalid
- x.expr = inst
+ x.expr = ix.Orig
return
}
got = len(targs)
- inferred = true
}
assert(got == want)
- // determine argument positions (for error reporting)
- // TODO(rFindley) use a positioner here? instantiate would need to be
- // updated accordingly.
- poslist := make([]token.Pos, len(xlist))
- for i, x := range xlist {
- poslist[i] = x.Pos()
- }
-
// instantiate function signature
- res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature)
- assert(res.tparams == nil) // signature is not generic anymore
- if inferred {
- check.recordInferred(inst, targs, res)
- }
+ res := check.instantiateSignature(x.Pos(), sig, targs, ix.Indices)
+ assert(res.TypeParams().Len() == 0) // signature is not generic anymore
+ check.recordInstance(ix.Orig, targs, res)
x.typ = res
x.mode = value
- x.expr = inst
+ x.expr = ix.Orig
+}
+
+func (check *Checker) instantiateSignature(pos token.Pos, typ *Signature, targs []Type, xlist []ast.Expr) (res *Signature) {
+ assert(check != nil)
+ assert(len(targs) == typ.TypeParams().Len())
+
+ if trace {
+ check.trace(pos, "-- instantiating %s with %s", typ, targs)
+ check.indent++
+ defer func() {
+ check.indent--
+ check.trace(pos, "=> %s (under = %s)", res, res.Underlying())
+ }()
+ }
+
+ inst := check.instance(pos, typ, targs, check.bestContext(nil)).(*Signature)
+ assert(len(xlist) <= len(targs))
+
+ // verify instantiation lazily (was issue #50450)
+ check.later(func() {
+ tparams := typ.TypeParams().list()
+ if i, err := check.verify(pos, tparams, targs); err != nil {
+ // best position for error reporting
+ pos := pos
+ if i < len(xlist) {
+ pos = xlist[i].Pos()
+ }
+ check.softErrorf(atPos(pos), _InvalidTypeArg, "%s", err)
+ } else {
+ check.mono.recordInstance(check.pkg, pos, tparams, targs, xlist)
+ }
+ })
+
+ return inst
}
func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
- var inst *ast.IndexExpr
- if iexpr, _ := call.Fun.(*ast.IndexExpr); iexpr != nil {
- if check.indexExpr(x, iexpr) {
+ ix := typeparams.UnpackIndexExpr(call.Fun)
+ if ix != nil {
+ if check.indexExpr(x, ix) {
// Delay function instantiation to argument checking,
// where we combine type and value arguments for type
// inference.
assert(x.mode == value)
- inst = iexpr
+ } else {
+ ix = nil
}
- x.expr = iexpr
+ x.expr = call.Fun
check.record(x)
+
} else {
- check.exprOrType(x, call.Fun)
+ check.exprOrType(x, call.Fun, true)
}
+ // x.typ may be generic
switch x.mode {
case invalid:
@@ -95,6 +121,10 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
case typexpr:
// conversion
+ check.nonGeneric(x)
+ if x.mode == invalid {
+ return conversion
+ }
T := x.typ
x.mode = invalid
switch n := len(call.Args); n {
@@ -107,10 +137,9 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
check.errorf(call.Args[0], _BadDotDotDotSyntax, "invalid use of ... in conversion to %s", T)
break
}
- if t := asInterface(T); t != nil {
- check.completeInterface(token.NoPos, t)
- if t._IsConstraint() {
- check.errorf(call, _Todo, "cannot use interface %s in conversion (contains type list or is comparable)", T)
+ if t, _ := under(T).(*Interface); t != nil && !isTypeParam(T) {
+ if !t.IsMethodSet() {
+ check.errorf(call, _MisplacedConstraintIface, "cannot use interface %s in conversion (contains specific type constraints or is comparable)", T)
break
}
}
@@ -124,6 +153,7 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
return conversion
case builtin:
+ // no need to check for non-genericity here
id := x.id
if !check.builtin(x, call, id) {
x.mode = invalid
@@ -137,9 +167,11 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
}
// ordinary function/method call
+ // signature may be generic
cgocall := x.mode == cgofunc
- sig := asSignature(x.typ)
+ // a type parameter may be "called" if all types have the same signature
+ sig, _ := structuralType(x.typ).(*Signature)
if sig == nil {
check.invalidOp(x, _InvalidCall, "cannot call non-function %s", x)
x.mode = invalid
@@ -148,9 +180,10 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
}
// evaluate type arguments, if any
+ var xlist []ast.Expr
var targs []Type
- if inst != nil {
- xlist := typeparams.UnpackExpr(inst.Index)
+ if ix != nil {
+ xlist = ix.Indices
targs = check.typeList(xlist)
if targs == nil {
check.use(call.Args...)
@@ -161,9 +194,9 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
assert(len(targs) == len(xlist))
// check number of type arguments (got) vs number of type parameters (want)
- got, want := len(targs), len(sig.tparams)
+ got, want := len(targs), sig.TypeParams().Len()
if got > want {
- check.errorf(xlist[want], _Todo, "got %d type arguments but want %d", got, want)
+ check.errorf(xlist[want], _WrongTypeArgCount, "got %d type arguments but want %d", got, want)
check.use(call.Args...)
x.mode = invalid
x.expr = call
@@ -173,7 +206,13 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
// evaluate arguments
args, _ := check.exprList(call.Args, false)
- sig = check.arguments(call, sig, targs, args)
+ isGeneric := sig.TypeParams().Len() > 0
+ sig = check.arguments(call, sig, targs, args, xlist)
+
+ if isGeneric && sig.TypeParams().Len() == 0 {
+ // Update the recorded type of call.Fun to its instantiated type.
+ check.recordTypeAndValue(call.Fun, value, sig, nil)
+ }
// determine result
switch sig.results.Len() {
@@ -195,7 +234,7 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
// if type inference failed, a parametrized result must be invalidated
// (operands cannot have a parametrized type)
- if x.mode == value && len(sig.tparams) > 0 && isParameterized(sig.tparams, x.typ) {
+ if x.mode == value && sig.TypeParams().Len() > 0 && isParameterized(sig.TypeParams().list(), x.typ) {
x.mode = invalid
}
@@ -245,7 +284,8 @@ func (check *Checker) exprList(elist []ast.Expr, allowCommaOk bool) (xlist []*op
return
}
-func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type, args []*operand) (rsig *Signature) {
+// xlist is the list of type argument expressions supplied in the source code.
+func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type, args []*operand, xlist []ast.Expr) (rsig *Signature) {
rsig = sig
// TODO(gri) try to eliminate this extra verification loop
@@ -314,42 +354,65 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type
}
// check argument count
- switch {
- case nargs < npars:
- check.errorf(inNode(call, call.Rparen), _WrongArgCount, "not enough arguments in call to %s", call.Fun)
- return
- case nargs > npars:
- check.errorf(args[npars], _WrongArgCount, "too many arguments in call to %s", call.Fun) // report at first extra argument
+ if nargs != npars {
+ var at positioner = call
+ qualifier := "not enough"
+ if nargs > npars {
+ at = args[npars].expr // report at first extra argument
+ qualifier = "too many"
+ } else {
+ at = atPos(call.Rparen) // report at closing )
+ }
+ // take care of empty parameter lists represented by nil tuples
+ var params []*Var
+ if sig.params != nil {
+ params = sig.params.vars
+ }
+ check.errorf(at, _WrongArgCount, "%s arguments in call to %s\n\thave %s\n\twant %s",
+ qualifier, call.Fun,
+ check.typesSummary(operandTypes(args), false),
+ check.typesSummary(varTypes(params), sig.variadic),
+ )
return
}
// infer type arguments and instantiate signature if necessary
- if len(sig.tparams) > 0 {
- // TODO(gri) provide position information for targs so we can feed
- // it to the instantiate call for better error reporting
- targs := check.infer(call, sig.tparams, targs, sigParams, args, true)
+ if sig.TypeParams().Len() > 0 {
+ if !check.allowVersion(check.pkg, 1, 18) {
+ switch call.Fun.(type) {
+ case *ast.IndexExpr, *ast.IndexListExpr:
+ ix := typeparams.UnpackIndexExpr(call.Fun)
+ check.softErrorf(inNode(call.Fun, ix.Lbrack), _UnsupportedFeature, "function instantiation requires go1.18 or later")
+ default:
+ check.softErrorf(inNode(call, call.Lparen), _UnsupportedFeature, "implicit function instantiation requires go1.18 or later")
+ }
+ }
+ targs := check.infer(call, sig.TypeParams().list(), targs, sigParams, args)
if targs == nil {
return // error already reported
}
// compute result signature
- rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature)
- assert(rsig.tparams == nil) // signature is not generic anymore
- check.recordInferred(call, targs, rsig)
+ rsig = check.instantiateSignature(call.Pos(), sig, targs, xlist)
+ assert(rsig.TypeParams().Len() == 0) // signature is not generic anymore
+ check.recordInstance(call.Fun, targs, rsig)
// Optimization: Only if the parameter list was adjusted do we
// need to compute it from the adjusted list; otherwise we can
// simply use the result signature's parameter list.
if adjusted {
- sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.tparams, targs)).(*Tuple)
+ sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.TypeParams().list(), targs), nil).(*Tuple)
} else {
sigParams = rsig.params
}
}
// 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
@@ -463,36 +526,38 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
}
}
- check.exprOrType(x, e.X)
+ check.exprOrType(x, e.X, false)
if x.mode == invalid {
goto Error
}
- check.instantiatedOperand(x)
-
- obj, index, indirect = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
+ obj, index, indirect = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
if obj == nil {
- switch {
- case index != nil:
+ // Don't report another error if the underlying type was invalid (issue #49541).
+ if under(x.typ) == Typ[Invalid] {
+ goto Error
+ }
+
+ if index != nil {
// TODO(gri) should provide actual type where the conflict happens
check.errorf(e.Sel, _AmbiguousSelector, "ambiguous selector %s.%s", x.expr, sel)
- case indirect:
+ goto Error
+ }
+
+ if indirect {
check.errorf(e.Sel, _InvalidMethodExpr, "cannot call pointer method %s on %s", sel, x.typ)
- default:
- var why string
- if tpar := asTypeParam(x.typ); tpar != nil {
- // Type parameter bounds don't specify fields, so don't mention "field".
- switch obj := tpar.Bound().obj.(type) {
- case nil:
- why = check.sprintf("type bound for %s has no method %s", x.typ, sel)
- case *TypeName:
- why = check.sprintf("interface %s has no method %s", obj.name, sel)
- }
- } else {
- why = check.sprintf("type %s has no field or method %s", x.typ, sel)
- }
+ goto Error
+ }
+ var why string
+ if isInterfacePtr(x.typ) {
+ why = check.interfacePtrError(x.typ)
+ } else {
+ why = check.sprintf("type %s has no field or method %s", x.typ, sel)
// Check if capitalization of sel matters and provide better error message in that case.
+ // TODO(gri) This code only looks at the first character but LookupFieldOrMethod should
+ // have an (internal) mechanism for case-insensitive lookup that we should use
+ // instead (see types2).
if len(sel) > 0 {
var changeCase string
if r := rune(sel[0]); unicode.IsUpper(r) {
@@ -500,67 +565,18 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
} else {
changeCase = string(unicode.ToUpper(r)) + sel[1:]
}
- if obj, _, _ = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil {
+ if obj, _, _ = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil {
why += ", but does have " + changeCase
}
}
-
- check.errorf(e.Sel, _MissingFieldOrMethod, "%s.%s undefined (%s)", x.expr, sel, why)
}
+ check.errorf(e.Sel, _MissingFieldOrMethod, "%s.%s undefined (%s)", x.expr, sel, why)
goto Error
}
// methods may not have a fully set up signature yet
if m, _ := obj.(*Func); m != nil {
check.objDecl(m, nil)
- // If m has a parameterized receiver type, infer the type arguments from
- // the actual receiver provided and then substitute the type parameters in
- // the signature accordingly.
- // TODO(gri) factor this code out
- sig := m.typ.(*Signature)
- if len(sig.rparams) > 0 {
- // For inference to work, we must use the receiver type
- // matching the receiver in the actual method declaration.
- // If the method is embedded, the matching receiver is the
- // embedded struct or interface that declared the method.
- // Traverse the embedding to find that type (issue #44688).
- recv := x.typ
- for i := 0; i < len(index)-1; i++ {
- // The embedded type is either a struct or a pointer to
- // a struct except for the last one (which we don't need).
- recv = asStruct(derefStructPtr(recv)).Field(index[i]).typ
- }
-
- // The method may have a pointer receiver, but the actually provided receiver
- // may be a (hopefully addressable) non-pointer value, or vice versa. Here we
- // only care about inferring receiver type parameters; to make the inference
- // work, match up pointer-ness of receiver and argument.
- if ptrRecv := isPointer(sig.recv.typ); ptrRecv != isPointer(recv) {
- if ptrRecv {
- recv = NewPointer(recv)
- } else {
- recv = recv.(*Pointer).base
- }
- }
- // Disable reporting of errors during inference below. If we're unable to infer
- // the receiver type arguments here, the receiver must be be otherwise invalid
- // and an error has been reported elsewhere.
- arg := operand{mode: variable, expr: x.expr, typ: recv}
- targs := check.infer(m, sig.rparams, nil, NewTuple(sig.recv), []*operand{&arg}, false /* no error reporting */)
- if targs == nil {
- // We may reach here if there were other errors (see issue #40056).
- goto Error
- }
- // Don't modify m. Instead - for now - make a copy of m and use that instead.
- // (If we modify m, some tests will fail; possibly because the m is in use.)
- // TODO(gri) investigate and provide a correct explanation here
- copy := *m
- copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.rparams, targs))
- obj = &copy
- }
- // TODO(gri) we also need to do substitution for parameterized interface methods
- // (this breaks code in testdata/linalg.go2 at the moment)
- // 12/20/2019: Is this TODO still correct?
}
if x.mode == typexpr {
@@ -574,17 +590,37 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
check.recordSelection(e, MethodExpr, x.typ, m, index, indirect)
+ sig := m.typ.(*Signature)
+ if sig.recv == nil {
+ check.error(e, _InvalidDeclCycle, "illegal cycle in method declaration")
+ goto Error
+ }
+
// the receiver type becomes the type of the first function
// argument of the method expression's function type
var params []*Var
- sig := m.typ.(*Signature)
if sig.params != nil {
params = sig.params.vars
}
+ // Be consistent about named/unnamed parameters. This is not needed
+ // for type-checking, but the newly constructed signature may appear
+ // in an error message and then have mixed named/unnamed parameters.
+ // (An alternative would be to not print parameter names in errors,
+ // but it's useful to see them; this is cheap and method expressions
+ // are rare.)
+ name := ""
+ if len(params) > 0 && params[0].name != "" {
+ // name needed
+ name = sig.recv.name
+ if name == "" {
+ name = "_"
+ }
+ }
+ params = append([]*Var{NewVar(sig.recv.pos, sig.recv.pkg, name, x.typ)}, params...)
x.mode = value
x.typ = &Signature{
tparams: sig.tparams,
- params: NewTuple(append([]*Var{NewVar(token.NoPos, check.pkg, "_", x.typ)}, params...)...),
+ params: NewTuple(params...),
results: sig.results,
variadic: sig.variadic,
}
@@ -686,7 +722,7 @@ func (check *Checker) use(arg ...ast.Expr) {
// The nil check below is necessary since certain AST fields
// may legally be nil (e.g., the ast.SliceExpr.High field).
if e != nil {
- check.rawExpr(&x, e, nil)
+ check.rawExpr(&x, e, nil, false)
}
}
}
@@ -718,17 +754,9 @@ func (check *Checker) useLHS(arg ...ast.Expr) {
}
}
}
- check.rawExpr(&x, e, nil)
+ check.rawExpr(&x, e, nil, false)
if v != nil {
v.used = v_used // restore v.used
}
}
}
-
-// instantiatedOperand reports an error of x is an uninstantiated (generic) type and sets x.typ to Typ[Invalid].
-func (check *Checker) instantiatedOperand(x *operand) {
- if x.mode == typexpr && isGeneric(x.typ) {
- check.errorf(x, _Todo, "cannot use generic type %s without instantiation", x.typ)
- x.typ = Typ[Invalid]
- }
-}
diff --git a/libgo/go/go/types/chan.go b/libgo/go/go/types/chan.go
new file mode 100644
index 0000000..1f7b72b
--- /dev/null
+++ b/libgo/go/go/types/chan.go
@@ -0,0 +1,35 @@
+// Copyright 2011 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.
+
+package types
+
+// A Chan represents a channel type.
+type Chan struct {
+ dir ChanDir
+ elem Type
+}
+
+// A ChanDir value indicates a channel direction.
+type ChanDir int
+
+// The direction of a channel is indicated by one of these constants.
+const (
+ SendRecv ChanDir = iota
+ SendOnly
+ RecvOnly
+)
+
+// NewChan returns a new channel type for the given direction and element type.
+func NewChan(dir ChanDir, elem Type) *Chan {
+ return &Chan{dir: dir, elem: elem}
+}
+
+// Dir returns the direction of channel c.
+func (c *Chan) Dir() ChanDir { return c.dir }
+
+// Elem returns the element type of channel c.
+func (c *Chan) Elem() Type { return c.elem }
+
+func (t *Chan) Underlying() Type { return t }
+func (t *Chan) String() string { return TypeString(t, nil) }
diff --git a/libgo/go/go/types/check.go b/libgo/go/go/types/check.go
index a923c3c..a0c3700 100644
--- a/libgo/go/go/types/check.go
+++ b/libgo/go/go/types/check.go
@@ -18,6 +18,10 @@ import (
const (
debug = false // leave on during development
trace = false // turn on for detailed type resolution traces
+
+ // TODO(rfindley): add compiler error message handling from types2, guarded
+ // behind this flag, so that we can keep the code in sync.
+ compilerErrorMessages = false // match compiler error messages
)
// If forceStrict is set, the type-checker enforces additional
@@ -41,22 +45,24 @@ type exprInfo struct {
val constant.Value // constant value; or nil (if not a constant)
}
-// A context represents the context within which an object is type-checked.
-type context struct {
+// An environment represents the environment within which an object is
+// type-checked.
+type environment struct {
decl *declInfo // package-level declaration whose init expression/function body is checked
scope *Scope // top-most scope for lookups
pos token.Pos // if valid, identifiers are looked up as if at position pos (used by Eval)
iota constant.Value // value of iota in a constant declaration; nil otherwise
errpos positioner // if set, identifier position of a constant with inherited initializer
+ inTParamList bool // set if inside a type parameter list
sig *Signature // function signature if inside a function; nil otherwise
isPanic map[*ast.CallExpr]bool // set of panic call expressions (used for termination check)
hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions
hasCallOrRecv bool // set if an expression contains a function call or channel receive operation
}
-// lookup looks up name in the current context and returns the matching object, or nil.
-func (ctxt *context) lookup(name string) Object {
- _, obj := ctxt.scope.LookupParent(name, ctxt.pos)
+// lookup looks up name in the current environment and returns the matching object, or nil.
+func (env *environment) lookup(name string) Object {
+ _, obj := env.scope.LookupParent(name, env.pos)
return obj
}
@@ -73,7 +79,29 @@ type importKey struct {
// A dotImportKey describes a dot-imported object in the given scope.
type dotImportKey struct {
scope *Scope
- obj Object
+ name string
+}
+
+// An action describes a (delayed) action.
+type action struct {
+ f func() // action to be executed
+ desc *actionDesc // action description; may be nil, requires debug to be set
+}
+
+// If debug is set, describef sets a printf-formatted description for action a.
+// Otherwise, it is a no-op.
+func (a *action) describef(pos positioner, format string, args ...any) {
+ if debug {
+ a.desc = &actionDesc{pos, format, args}
+ }
+}
+
+// An actionDesc provides information on an action.
+// For debugging only.
+type actionDesc struct {
+ pos positioner
+ format string
+ args []any
}
// A Checker maintains the state of the type checker.
@@ -82,14 +110,15 @@ type Checker struct {
// package information
// (initialized by NewChecker, valid for the life-time of checker)
conf *Config
+ ctxt *Context // context for de-duplicating instances
fset *token.FileSet
pkg *Package
*Info
- version version // accepted language version
- objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
- impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
- posMap map[*Interface][]token.Pos // maps interface types to lists of embedded interface positions
- typMap map[string]*Named // maps an instantiated named type hash to a *Named type
+ version version // accepted language version
+ nextID uint64 // unique Id for type parameters (first valid Id is 1)
+ objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
+ impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
+ infoMap map[*Named]typeInfo // maps named types to their associated type info (for cycle detection)
// pkgPathMap maps package names to the set of distinct import paths we've
// seen for that name, anywhere in the import graph. It is used for
@@ -104,19 +133,24 @@ type Checker struct {
// information collected during type-checking of a set of package files
// (initialized by Files, valid only for the duration of check.Files;
// maps and lists are allocated on demand)
- files []*ast.File // package files
- imports []*PkgName // list of imported packages
- dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through
+ files []*ast.File // package files
+ imports []*PkgName // list of imported packages
+ dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through
+ recvTParamMap map[*ast.Ident]*TypeParam // maps blank receiver type parameters to their type
+ brokenAliases map[*TypeName]bool // set of aliases with broken (not yet determined) types
+ unionTypeSets map[*Union]*_TypeSet // computed type sets for union types
+ mono monoGraph // graph for detecting non-monomorphizable instantiation loops
firstErr error // first error encountered
methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods
untyped map[ast.Expr]exprInfo // map of expressions without final type
- delayed []func() // stack of delayed action segments; segments are processed in FIFO order
+ delayed []action // stack of delayed action segments; segments are processed in FIFO order
objPath []Object // path of object dependencies during type inference (for cycle reporting)
+ defTypes []*Named // defined types created during type checking, for final validation.
- // context within which the current object is type-checked
- // (valid only for the duration of type-checking a specific object)
- context
+ // environment within which the current object is type-checked (valid only
+ // for the duration of type-checking a specific object)
+ environment
// debugging
indent int // indentation for tracing
@@ -134,6 +168,27 @@ func (check *Checker) addDeclDep(to Object) {
from.addDep(to)
}
+// brokenAlias records that alias doesn't have a determined type yet.
+// It also sets alias.typ to Typ[Invalid].
+func (check *Checker) brokenAlias(alias *TypeName) {
+ if check.brokenAliases == nil {
+ check.brokenAliases = make(map[*TypeName]bool)
+ }
+ check.brokenAliases[alias] = true
+ alias.typ = Typ[Invalid]
+}
+
+// validAlias records that alias has the valid type typ (possibly Typ[Invalid]).
+func (check *Checker) validAlias(alias *TypeName, typ Type) {
+ delete(check.brokenAliases, alias)
+ alias.typ = typ
+}
+
+// isBrokenAlias reports whether alias doesn't have a determined type yet.
+func (check *Checker) isBrokenAlias(alias *TypeName) bool {
+ return alias.typ == Typ[Invalid] && check.brokenAliases[alias]
+}
+
func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) {
m := check.untyped
if m == nil {
@@ -147,8 +202,12 @@ func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, ty
// either at the end of the current statement, or in case of a local constant
// or variable declaration, before the constant or variable is in scope
// (so that f still sees the scope before any new declarations).
-func (check *Checker) later(f func()) {
- check.delayed = append(check.delayed, f)
+// later returns the pushed action so one can provide a description
+// via action.describef for debugging, if desired.
+func (check *Checker) later(f func()) *action {
+ i := len(check.delayed)
+ check.delayed = append(check.delayed, action{f: f})
+ return &check.delayed[i]
}
// push pushes obj onto the object path and returns its index in the path.
@@ -179,21 +238,21 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch
info = new(Info)
}
- version, err := parseGoVersion(conf.goVersion)
+ version, err := parseGoVersion(conf.GoVersion)
if err != nil {
- panic(fmt.Sprintf("invalid Go version %q (%v)", conf.goVersion, err))
+ panic(fmt.Sprintf("invalid Go version %q (%v)", conf.GoVersion, err))
}
return &Checker{
conf: conf,
+ ctxt: conf.Context,
fset: fset,
pkg: pkg,
Info: info,
version: version,
objMap: make(map[Object]*declInfo),
impMap: make(map[importKey]*Package),
- posMap: make(map[*Interface][]token.Pos),
- typMap: make(map[string]*Named),
+ infoMap: make(map[*Named]typeInfo),
}
}
@@ -266,6 +325,8 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {
check.processDelayed(0) // incl. all functions
+ check.expandDefTypes()
+
check.initOrder()
if !check.conf.DisableUnusedImportCheck {
@@ -274,8 +335,9 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {
check.recordUntyped()
- if check.Info != nil {
- sanitizeInfo(check.Info)
+ if check.firstErr == nil {
+ // TODO(mdempsky): Ensure monomorph is safe when errors exist.
+ check.monomorph()
}
check.pkg.complete = true
@@ -285,6 +347,11 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {
check.dotImportMap = nil
check.pkgPathMap = nil
check.seenPkgMap = nil
+ check.recvTParamMap = nil
+ check.brokenAliases = nil
+ check.unionTypeSets = nil
+ check.defTypes = nil
+ check.ctxt = nil
// TODO(rFindley) There's more memory we should release at this point.
@@ -300,12 +367,40 @@ func (check *Checker) processDelayed(top int) {
// add more actions (such as nested functions), so
// this is a sufficiently bounded process.
for i := top; i < len(check.delayed); i++ {
- check.delayed[i]() // may append to check.delayed
+ a := &check.delayed[i]
+ if trace && a.desc != nil {
+ fmt.Println()
+ check.trace(a.desc.pos.Pos(), "-- "+a.desc.format, a.desc.args...)
+ }
+ a.f() // may append to check.delayed
}
assert(top <= len(check.delayed)) // stack must not have shrunk
check.delayed = check.delayed[:top]
}
+func (check *Checker) expandDefTypes() {
+ // Ensure that every defined type created in the course of type-checking has
+ // either non-*Named underlying, or is unresolved.
+ //
+ // This guarantees that we don't leak any types whose underlying is *Named,
+ // because any unresolved instances will lazily compute their underlying by
+ // substituting in the underlying of their origin. The origin must have
+ // either been imported or type-checked and expanded here, and in either case
+ // its underlying will be fully expanded.
+ for i := 0; i < len(check.defTypes); i++ {
+ n := check.defTypes[i]
+ switch n.underlying.(type) {
+ case nil:
+ if n.resolver == nil {
+ panic("nil underlying")
+ }
+ case *Named:
+ n.under() // n.under may add entries to check.defTypes
+ }
+ n.check = nil
+ }
+}
+
func (check *Checker) record(x *operand) {
// convert x into a user-friendly set of values
// TODO(gri) this code can be simplified
@@ -355,9 +450,9 @@ func (check *Checker) recordTypeAndValue(x ast.Expr, mode operandMode, typ Type,
}
if mode == constant_ {
assert(val != nil)
- // We check is(typ, IsConstType) here as constant expressions may be
+ // We check allBasic(typ, IsConstType) here as constant expressions may be
// recorded as type parameters.
- assert(typ == Typ[Invalid] || is(typ, IsConstType))
+ assert(typ == Typ[Invalid] || allBasic(typ, IsConstType))
}
if m := check.Types; m != nil {
m[x] = TypeAndValue{mode, typ, val}
@@ -408,12 +503,38 @@ func (check *Checker) recordCommaOkTypes(x ast.Expr, a [2]Type) {
}
}
-func (check *Checker) recordInferred(call ast.Expr, targs []Type, sig *Signature) {
- assert(call != nil)
- assert(sig != nil)
- if m := getInferred(check.Info); m != nil {
- m[call] = _Inferred{targs, sig}
+// recordInstance records instantiation information into check.Info, if the
+// Instances map is non-nil. The given expr must be an ident, selector, or
+// index (list) expr with ident or selector operand.
+//
+// TODO(rfindley): the expr parameter is fragile. See if we can access the
+// instantiated identifier in some other way.
+func (check *Checker) recordInstance(expr ast.Expr, targs []Type, typ Type) {
+ ident := instantiatedIdent(expr)
+ assert(ident != nil)
+ assert(typ != nil)
+ if m := check.Instances; m != nil {
+ m[ident] = Instance{newTypeList(targs), typ}
+ }
+}
+
+func instantiatedIdent(expr ast.Expr) *ast.Ident {
+ var selOrIdent ast.Expr
+ switch e := expr.(type) {
+ case *ast.IndexExpr:
+ selOrIdent = e.X
+ case *ast.IndexListExpr:
+ selOrIdent = e.X
+ case *ast.SelectorExpr, *ast.Ident:
+ selOrIdent = e
+ }
+ switch x := selOrIdent.(type) {
+ case *ast.Ident:
+ return x
+ case *ast.SelectorExpr:
+ return x.Sel
}
+ panic("instantiated ident not found")
}
func (check *Checker) recordDef(id *ast.Ident, obj Object) {
diff --git a/libgo/go/go/types/check_test.go b/libgo/go/go/types/check_test.go
index f83abf1..81ea81c 100644
--- a/libgo/go/go/types/check_test.go
+++ b/libgo/go/go/types/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 types_test
import (
@@ -34,6 +31,7 @@ import (
"go/parser"
"go/scanner"
"go/token"
+ "internal/buildcfg"
"internal/testenv"
"os"
"path/filepath"
@@ -53,7 +51,7 @@ var (
var fset = token.NewFileSet()
// Positioned errors are of the form filename:line:column: message .
-var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(.*)`)
+var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(?s)(.*)`)
// splitError splits an error's error message into a position string
// and the actual error message. If there's no position information,
@@ -202,20 +200,35 @@ func asGoVersion(s string) string {
return ""
}
-func checkFiles(t *testing.T, sizes Sizes, goVersion string, filenames []string, srcs [][]byte, manual bool, imp Importer) {
+// excludedForUnifiedBuild lists files that cannot be tested
+// when using the unified build's export data.
+// TODO(gri) enable as soon as the unified build supports this.
+var excludedForUnifiedBuild = map[string]bool{
+ "issue47818.go2": true,
+ "issue49705.go2": true,
+}
+
+func testFiles(t *testing.T, sizes Sizes, filenames []string, srcs [][]byte, manual bool, imp Importer) {
if len(filenames) == 0 {
t.Fatal("no source files")
}
- if strings.HasSuffix(filenames[0], ".go2") && !typeparams.Enabled {
- t.Skip("type params are not enabled")
+ if buildcfg.Experiment.Unified {
+ for _, f := range filenames {
+ if excludedForUnifiedBuild[filepath.Base(f)] {
+ t.Logf("%s cannot be tested with unified build - skipped", f)
+ return
+ }
+ }
}
- if strings.HasSuffix(filenames[0], ".go1") && typeparams.Enabled {
+
+ if strings.HasSuffix(filenames[0], ".go1") {
+ // TODO(rfindley): re-enable this test by using GoVersion.
t.Skip("type params are enabled")
}
mode := parser.AllErrors
- if !strings.HasSuffix(filenames[0], ".go2") {
+ if !strings.HasSuffix(filenames[0], ".go2") && !manual {
mode |= typeparams.DisallowParsing
}
@@ -228,6 +241,7 @@ func checkFiles(t *testing.T, sizes Sizes, goVersion string, filenames []string,
}
// if no Go version is given, consider the package name
+ goVersion := *goVersion
if goVersion == "" {
goVersion = asGoVersion(pkgName)
}
@@ -243,7 +257,7 @@ func checkFiles(t *testing.T, sizes Sizes, goVersion string, filenames []string,
// typecheck and collect typechecker errors
var conf Config
conf.Sizes = sizes
- SetGoVersion(&conf, goVersion)
+ conf.GoVersion = goVersion
// special case for importC.src
if len(filenames) == 1 {
@@ -303,29 +317,48 @@ func checkFiles(t *testing.T, sizes Sizes, goVersion string, filenames []string,
}
}
-// TestManual is for manual testing of input files, provided as a list
-// of arguments after the test arguments (and a separating "--"). For
-// instance, to check the files foo.go and bar.go, use:
+// TestManual is for manual testing of a package - either provided
+// as a list of filenames belonging to the package, or a directory
+// name containing the package files - after the test arguments
+// (and a separating "--"). For instance, to test the package made
+// of the files foo.go and bar.go, use:
//
// go test -run Manual -- foo.go bar.go
//
-// Provide the -verify flag to verify errors against ERROR comments in
-// the input files rather than having a list of errors reported.
-// The accepted Go language version can be controlled with the -lang flag.
+// If no source arguments are provided, the file testdata/manual.go2
+// is used instead.
+// Provide the -verify flag to verify errors against ERROR comments
+// in the input files rather than having a list of errors reported.
+// The accepted Go language version can be controlled with the -lang
+// flag.
func TestManual(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
filenames := flag.Args()
if len(filenames) == 0 {
- return
+ filenames = []string{filepath.FromSlash("testdata/manual.go2")}
}
- testenv.MustHaveGoBuild(t)
+
+ info, err := os.Stat(filenames[0])
+ if err != nil {
+ t.Fatalf("TestManual: %v", err)
+ }
+
DefPredeclaredTestFuncs()
- testPkg(t, filenames, *goVersion, true)
+ if info.IsDir() {
+ if len(filenames) > 1 {
+ t.Fatal("TestManual: must have only one directory argument")
+ }
+ testDir(t, filenames[0], true)
+ } else {
+ testPkg(t, filenames, true)
+ }
}
func TestLongConstants(t *testing.T) {
format := "package longconst\n\nconst _ = %s\nconst _ = %s // ERROR excessively long constant"
src := fmt.Sprintf(format, strings.Repeat("1", 9999), strings.Repeat("1", 10001))
- checkFiles(t, nil, "", []string{"longconst.go"}, [][]byte{[]byte(src)}, false, nil)
+ testFiles(t, nil, []string{"longconst.go"}, [][]byte{[]byte(src)}, false, nil)
}
// TestIndexRepresentability tests that constant index operands must
@@ -333,32 +366,28 @@ func TestLongConstants(t *testing.T) {
// represent larger values.
func TestIndexRepresentability(t *testing.T) {
const src = "package index\n\nvar s []byte\nvar _ = s[int64 /* ERROR \"int64\\(1\\) << 40 \\(.*\\) overflows int\" */ (1) << 40]"
- checkFiles(t, &StdSizes{4, 4}, "", []string{"index.go"}, [][]byte{[]byte(src)}, false, nil)
-}
-
-func TestIssue46453(t *testing.T) {
- if typeparams.Enabled {
- t.Skip("type params are enabled")
- }
- const src = "package p\ntype _ comparable // ERROR \"undeclared name: comparable\""
- checkFiles(t, nil, "", []string{"issue46453.go"}, [][]byte{[]byte(src)}, false, nil)
+ testFiles(t, &StdSizes{4, 4}, []string{"index.go"}, [][]byte{[]byte(src)}, false, nil)
}
func TestIssue47243_TypedRHS(t *testing.T) {
// The RHS of the shift expression below overflows uint on 32bit platforms,
// but this is OK as it is explicitly typed.
const src = "package issue47243\n\nvar a uint64; var _ = a << uint64(4294967296)" // uint64(1<<32)
- checkFiles(t, &StdSizes{4, 4}, "", []string{"p.go"}, [][]byte{[]byte(src)}, false, nil)
+ testFiles(t, &StdSizes{4, 4}, []string{"p.go"}, [][]byte{[]byte(src)}, false, nil)
}
-func TestCheck(t *testing.T) { DefPredeclaredTestFuncs(); testDir(t, "check") }
-func TestExamples(t *testing.T) { testDir(t, "examples") }
-func TestFixedbugs(t *testing.T) { testDir(t, "fixedbugs") }
+func TestCheck(t *testing.T) { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/check", false) }
+func TestSpec(t *testing.T) { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/spec", false) }
+func TestExamples(t *testing.T) { testDirFiles(t, "testdata/examples", false) }
+func TestFixedbugs(t *testing.T) {
+ DefPredeclaredTestFuncs()
+ testDirFiles(t, "testdata/fixedbugs", false)
+}
-func testDir(t *testing.T, dir string) {
+func testDirFiles(t *testing.T, dir string, manual bool) {
testenv.MustHaveGoBuild(t)
+ dir = filepath.FromSlash(dir)
- dir = filepath.Join("testdata", dir)
fis, err := os.ReadDir(dir)
if err != nil {
t.Error(err)
@@ -368,28 +397,38 @@ func testDir(t *testing.T, dir string) {
for _, fi := range fis {
path := filepath.Join(dir, fi.Name())
- // if fi is a directory, its files make up a single package
- var filenames []string
+ // If fi is a directory, its files make up a single package.
if fi.IsDir() {
- fis, err := os.ReadDir(path)
- if err != nil {
- t.Error(err)
- continue
- }
- for _, fi := range fis {
- filenames = append(filenames, filepath.Join(path, fi.Name()))
- }
+ testDir(t, path, manual)
} else {
- filenames = []string{path}
+ t.Run(filepath.Base(path), func(t *testing.T) {
+ testPkg(t, []string{path}, manual)
+ })
}
- t.Run(filepath.Base(path), func(t *testing.T) {
- testPkg(t, filenames, "", false)
- })
}
}
+func testDir(t *testing.T, dir string, manual bool) {
+ testenv.MustHaveGoBuild(t)
+
+ fis, err := os.ReadDir(dir)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ var filenames []string
+ for _, fi := range fis {
+ filenames = append(filenames, filepath.Join(dir, fi.Name()))
+ }
+
+ t.Run(filepath.Base(dir), func(t *testing.T) {
+ testPkg(t, filenames, manual)
+ })
+}
+
// TODO(rFindley) reconcile the different test setup in go/types with types2.
-func testPkg(t *testing.T, filenames []string, goVersion string, manual bool) {
+func testPkg(t *testing.T, filenames []string, manual bool) {
srcs := make([][]byte, len(filenames))
for i, filename := range filenames {
src, err := os.ReadFile(filename)
@@ -398,5 +437,5 @@ func testPkg(t *testing.T, filenames []string, goVersion string, manual bool) {
}
srcs[i] = src
}
- checkFiles(t, nil, goVersion, filenames, srcs, manual, nil)
+ testFiles(t, nil, filenames, srcs, manual, nil)
}
diff --git a/libgo/go/go/types/context.go b/libgo/go/go/types/context.go
new file mode 100644
index 0000000..ff4bf89
--- /dev/null
+++ b/libgo/go/go/types/context.go
@@ -0,0 +1,123 @@
+// 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.
+
+package types
+
+import (
+ "bytes"
+ "fmt"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+// An Context is an opaque type checking context. It may be used to share
+// identical type instances across type-checked packages or calls to
+// Instantiate.
+//
+// It is safe for concurrent use.
+type Context struct {
+ mu sync.Mutex
+ typeMap map[string][]ctxtEntry // type hash -> instances entries
+ nextID int // next unique ID
+ originIDs map[Type]int // origin type -> unique ID
+}
+
+type ctxtEntry struct {
+ orig Type
+ targs []Type
+ instance Type // = orig[targs]
+}
+
+// NewContext creates a new Context.
+func NewContext() *Context {
+ return &Context{
+ typeMap: make(map[string][]ctxtEntry),
+ originIDs: make(map[Type]int),
+ }
+}
+
+// instanceHash returns a string representation of typ instantiated with targs.
+// The hash should be a perfect hash, though out of caution the type checker
+// does not assume this. The result is guaranteed to not contain blanks.
+func (ctxt *Context) instanceHash(orig Type, targs []Type) string {
+ assert(ctxt != nil)
+ assert(orig != nil)
+ var buf bytes.Buffer
+
+ h := newTypeHasher(&buf, ctxt)
+ h.string(strconv.Itoa(ctxt.getID(orig)))
+ // Because we've already written the unique origin ID this call to h.typ is
+ // unnecessary, but we leave it for hash readability. It can be removed later
+ // if performance is an issue.
+ h.typ(orig)
+ if len(targs) > 0 {
+ // TODO(rfindley): consider asserting on isGeneric(typ) here, if and when
+ // isGeneric handles *Signature types.
+ h.typeList(targs)
+ }
+
+ return strings.Replace(buf.String(), " ", "#", -1) // ReplaceAll is not available in Go1.4
+}
+
+// lookup returns an existing instantiation of orig with targs, if it exists.
+// Otherwise, it returns nil.
+func (ctxt *Context) lookup(h string, orig Type, targs []Type) Type {
+ ctxt.mu.Lock()
+ defer ctxt.mu.Unlock()
+
+ for _, e := range ctxt.typeMap[h] {
+ if identicalInstance(orig, targs, e.orig, e.targs) {
+ return e.instance
+ }
+ if debug {
+ // Panic during development to surface any imperfections in our hash.
+ panic(fmt.Sprintf("non-identical instances: (orig: %s, targs: %v) and %s", orig, targs, e.instance))
+ }
+ }
+
+ return nil
+}
+
+// update de-duplicates n against previously seen types with the hash h. If an
+// identical type is found with the type hash h, the previously seen type is
+// returned. Otherwise, n is returned, and recorded in the Context for the hash
+// h.
+func (ctxt *Context) update(h string, orig Type, targs []Type, inst Type) Type {
+ assert(inst != nil)
+
+ ctxt.mu.Lock()
+ defer ctxt.mu.Unlock()
+
+ for _, e := range ctxt.typeMap[h] {
+ if inst == nil || Identical(inst, e.instance) {
+ return e.instance
+ }
+ if debug {
+ // Panic during development to surface any imperfections in our hash.
+ panic(fmt.Sprintf("%s and %s are not identical", inst, e.instance))
+ }
+ }
+
+ ctxt.typeMap[h] = append(ctxt.typeMap[h], ctxtEntry{
+ orig: orig,
+ targs: targs,
+ instance: inst,
+ })
+
+ return inst
+}
+
+// getID returns a unique ID for the type t.
+func (ctxt *Context) getID(t Type) int {
+ ctxt.mu.Lock()
+ defer ctxt.mu.Unlock()
+ id, ok := ctxt.originIDs[t]
+ if !ok {
+ id = ctxt.nextID
+ ctxt.originIDs[t] = id
+ ctxt.nextID++
+ }
+ return id
+}
diff --git a/libgo/go/go/types/context_test.go b/libgo/go/go/types/context_test.go
new file mode 100644
index 0000000..ec300502
--- /dev/null
+++ b/libgo/go/go/types/context_test.go
@@ -0,0 +1,70 @@
+// 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.
+
+package types
+
+import (
+ "go/token"
+ "testing"
+)
+
+func TestContextHashCollisions(t *testing.T) {
+ if debug {
+ t.Skip("hash collisions are expected, and would fail debug assertions")
+ }
+ // Unit test the de-duplication fall-back logic in Context.
+ //
+ // We can't test this via Instantiate because this is only a fall-back in
+ // case our hash is imperfect.
+ //
+ // These lookups and updates use reasonable looking types in an attempt to
+ // make them robust to internal type assertions, but could equally well use
+ // arbitrary types.
+
+ // Create some distinct origin types. nullaryP and nullaryQ have no
+ // parameters and are identical (but have different type parameter names).
+ // unaryP has a parameter.
+ var nullaryP, nullaryQ, unaryP Type
+ {
+ // type nullaryP = func[P any]()
+ tparam := NewTypeParam(NewTypeName(token.NoPos, nil, "P", nil), &emptyInterface)
+ nullaryP = NewSignatureType(nil, nil, []*TypeParam{tparam}, nil, nil, false)
+ }
+ {
+ // type nullaryQ = func[Q any]()
+ tparam := NewTypeParam(NewTypeName(token.NoPos, nil, "Q", nil), &emptyInterface)
+ nullaryQ = NewSignatureType(nil, nil, []*TypeParam{tparam}, nil, nil, false)
+ }
+ {
+ // type unaryP = func[P any](_ P)
+ tparam := NewTypeParam(NewTypeName(token.NoPos, nil, "P", nil), &emptyInterface)
+ params := NewTuple(NewVar(token.NoPos, nil, "_", tparam))
+ unaryP = NewSignatureType(nil, nil, []*TypeParam{tparam}, params, nil, false)
+ }
+
+ ctxt := NewContext()
+
+ // Update the context with an instantiation of nullaryP.
+ inst := NewSignatureType(nil, nil, nil, nil, nil, false)
+ if got := ctxt.update("", nullaryP, []Type{Typ[Int]}, inst); got != inst {
+ t.Error("bad")
+ }
+
+ // unaryP is not identical to nullaryP, so we should not get inst when
+ // instantiated with identical type arguments.
+ if got := ctxt.lookup("", unaryP, []Type{Typ[Int]}); got != nil {
+ t.Error("bad")
+ }
+
+ // nullaryQ is identical to nullaryP, so we *should* get inst when
+ // instantiated with identical type arguments.
+ if got := ctxt.lookup("", nullaryQ, []Type{Typ[Int]}); got != inst {
+ t.Error("bad")
+ }
+
+ // ...but verify we don't get inst with different type arguments.
+ if got := ctxt.lookup("", nullaryQ, []Type{Typ[String]}); got != nil {
+ t.Error("bad")
+ }
+}
diff --git a/libgo/go/go/types/conversions.go b/libgo/go/go/types/conversions.go
index ad6d3ee..8474135 100644
--- a/libgo/go/go/types/conversions.go
+++ b/libgo/go/go/types/conversions.go
@@ -16,33 +16,71 @@ import (
func (check *Checker) conversion(x *operand, T Type) {
constArg := x.mode == constant_
- var ok bool
- var reason string
- switch {
- case constArg && isConstType(T):
- // constant conversion
- switch t := asBasic(T); {
- case representableConst(x.val, check, t, &x.val):
- ok = true
+ constConvertibleTo := func(T Type, val *constant.Value) bool {
+ switch t, _ := under(T).(*Basic); {
+ case t == nil:
+ // nothing to do
+ case representableConst(x.val, check, t, val):
+ return true
case isInteger(x.typ) && isString(t):
codepoint := unicode.ReplacementChar
if i, ok := constant.Uint64Val(x.val); ok && i <= unicode.MaxRune {
codepoint = rune(i)
}
- x.val = constant.MakeString(string(codepoint))
- ok = true
+ if val != nil {
+ *val = constant.MakeString(string(codepoint))
+ }
+ return true
}
- case x.convertibleTo(check, T, &reason):
+ return false
+ }
+
+ var ok bool
+ var cause string
+ switch {
+ case constArg && isConstType(T):
+ // constant conversion
+ ok = constConvertibleTo(T, &x.val)
+ case constArg && isTypeParam(T):
+ // x is convertible to T if it is convertible
+ // to each specific type in the type set of T.
+ // If T's type set is empty, or if it doesn't
+ // have specific types, constant x cannot be
+ // converted.
+ ok = T.(*TypeParam).underIs(func(u Type) bool {
+ // t is nil if there are no specific type terms
+ if u == nil {
+ cause = check.sprintf("%s does not contain specific types", T)
+ return false
+ }
+ if !constConvertibleTo(u, nil) {
+ cause = check.sprintf("cannot convert %s to %s (in %s)", x, u, T)
+ return false
+ }
+ return true
+ })
+ x.mode = value // type parameters are not constants
+ case x.convertibleTo(check, T, &cause):
// non-constant conversion
- x.mode = value
ok = true
+ x.mode = value
}
if !ok {
- if reason != "" {
- check.errorf(x, _InvalidConversion, "cannot convert %s to %s (%s)", x, T, reason)
+ // TODO(rfindley): use types2-style error reporting here.
+ if compilerErrorMessages {
+ if cause != "" {
+ // Add colon at end of line if we have a following cause.
+ check.errorf(x, _InvalidConversion, "cannot convert %s to type %s:\n\t%s", x, T, cause)
+ } else {
+ check.errorf(x, _InvalidConversion, "cannot convert %s to type %s", x, T)
+ }
} else {
- check.errorf(x, _InvalidConversion, "cannot convert %s to %s", x, T)
+ if cause != "" {
+ check.errorf(x, _InvalidConversion, "cannot convert %s to %s (%s)", x, T, cause)
+ } else {
+ check.errorf(x, _InvalidConversion, "cannot convert %s to %s", x, T)
+ }
}
x.mode = invalid
return
@@ -58,11 +96,11 @@ func (check *Checker) conversion(x *operand, T Type) {
// use the default type (e.g., []byte("foo") should report string
// not []byte as type for the constant "foo").
// - Keep untyped nil for untyped nil arguments.
- // - For integer to string conversions, keep the argument type.
+ // - For constant integer to string conversions, keep the argument type.
// (See also the TODO below.)
- if IsInterface(T) || constArg && !isConstType(T) || x.isNil() {
+ if IsInterface(T) && !isTypeParam(T) || constArg && !isConstType(T) || x.isNil() {
final = Default(x.typ) // default type of untyped nil is untyped nil
- } else if isInteger(x.typ) && isString(T) {
+ } else if x.mode == constant_ && isInteger(x.typ) && allString(T) {
final = x.typ
}
check.updateExprType(x.expr, final, true)
@@ -81,104 +119,168 @@ func (check *Checker) conversion(x *operand, T Type) {
// is tricky because we'd have to run updateExprType on the argument first.
// (Issue #21982.)
-// convertibleTo reports whether T(x) is valid.
+// convertibleTo reports whether T(x) is valid. In the failure case, *cause
+// may be set to the cause for the failure.
// The check parameter may be nil if convertibleTo is invoked through an
// exported API call, i.e., when all methods have been type-checked.
-func (x *operand) convertibleTo(check *Checker, T Type, reason *string) bool {
+func (x *operand) convertibleTo(check *Checker, T Type, cause *string) bool {
// "x is assignable to T"
- if ok, _ := x.assignableTo(check, T, nil); ok {
+ if ok, _ := x.assignableTo(check, T, cause); ok {
return true
}
- // "x's type and T have identical underlying types if tags are ignored"
+ // "V and T have identical underlying types if tags are ignored
+ // and V and T are not type parameters"
V := x.typ
Vu := under(V)
Tu := under(T)
- if check.identicalIgnoreTags(Vu, Tu) {
+ Vp, _ := V.(*TypeParam)
+ Tp, _ := T.(*TypeParam)
+ if IdenticalIgnoreTags(Vu, Tu) && Vp == nil && Tp == nil {
return true
}
- // "x's type and T are unnamed pointer types and their pointer base types
- // have identical underlying types if tags are ignored"
+ // "V and T are unnamed pointer types and their pointer base types
+ // have identical underlying types if tags are ignored
+ // and their pointer base types are not type parameters"
if V, ok := V.(*Pointer); ok {
if T, ok := T.(*Pointer); ok {
- if check.identicalIgnoreTags(under(V.base), under(T.base)) {
+ if IdenticalIgnoreTags(under(V.base), under(T.base)) && !isTypeParam(V.base) && !isTypeParam(T.base) {
return true
}
}
}
- // "x's type and T are both integer or floating point types"
- if isIntegerOrFloat(V) && isIntegerOrFloat(T) {
+ // "V and T are both integer or floating point types"
+ if isIntegerOrFloat(Vu) && isIntegerOrFloat(Tu) {
return true
}
- // "x's type and T are both complex types"
- if isComplex(V) && isComplex(T) {
+ // "V and T are both complex types"
+ if isComplex(Vu) && isComplex(Tu) {
return true
}
- // "x is an integer or a slice of bytes or runes and T is a string type"
- if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) {
+ // "V is an integer or a slice of bytes or runes and T is a string type"
+ if (isInteger(Vu) || isBytesOrRunes(Vu)) && isString(Tu) {
return true
}
- // "x is a string and T is a slice of bytes or runes"
- if isString(V) && isBytesOrRunes(Tu) {
+ // "V is a string and T is a slice of bytes or runes"
+ if isString(Vu) && isBytesOrRunes(Tu) {
return true
}
// package unsafe:
// "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer"
- if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) {
+ if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(Tu) {
return true
}
// "and vice versa"
- if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) {
+ if isUnsafePointer(Vu) && (isPointer(Tu) || isUintptr(Tu)) {
return true
}
- // "x is a slice, T is a pointer-to-array type,
+ // "V a slice, T is a pointer-to-array type,
// and the slice and array types have identical element types."
- if s := asSlice(V); s != nil {
- if p := asPointer(T); p != nil {
- if a := asArray(p.Elem()); a != nil {
- if check.identical(s.Elem(), a.Elem()) {
+ if s, _ := Vu.(*Slice); s != nil {
+ if p, _ := Tu.(*Pointer); p != nil {
+ if a, _ := under(p.Elem()).(*Array); a != nil {
+ if Identical(s.Elem(), a.Elem()) {
if check == nil || check.allowVersion(check.pkg, 1, 17) {
return true
}
- if reason != nil {
- *reason = "conversion of slices to array pointers requires go1.17 or later"
+ if cause != nil {
+ *cause = "conversion of slices to array pointers requires go1.17 or later"
}
}
}
}
}
+ // optimization: if we don't have type parameters, we're done
+ if Vp == nil && Tp == nil {
+ return false
+ }
+
+ errorf := func(format string, args ...any) {
+ if check != nil && cause != nil {
+ msg := check.sprintf(format, args...)
+ if *cause != "" {
+ msg += "\n\t" + *cause
+ }
+ *cause = msg
+ }
+ }
+
+ // generic cases with specific type terms
+ // (generic operands cannot be constants, so we can ignore x.val)
+ switch {
+ case Vp != nil && Tp != nil:
+ x := *x // don't clobber outer x
+ return Vp.is(func(V *term) bool {
+ if V == nil {
+ return false // no specific types
+ }
+ x.typ = V.typ
+ return Tp.is(func(T *term) bool {
+ if T == nil {
+ return false // no specific types
+ }
+ if !x.convertibleTo(check, T.typ, cause) {
+ errorf("cannot convert %s (in %s) to %s (in %s)", V.typ, Vp, T.typ, Tp)
+ return false
+ }
+ return true
+ })
+ })
+ case Vp != nil:
+ x := *x // don't clobber outer x
+ return Vp.is(func(V *term) bool {
+ if V == nil {
+ return false // no specific types
+ }
+ x.typ = V.typ
+ if !x.convertibleTo(check, T, cause) {
+ errorf("cannot convert %s (in %s) to %s", V.typ, Vp, T)
+ return false
+ }
+ return true
+ })
+ case Tp != nil:
+ return Tp.is(func(T *term) bool {
+ if T == nil {
+ return false // no specific types
+ }
+ if !x.convertibleTo(check, T.typ, cause) {
+ errorf("cannot convert %s to %s (in %s)", x.typ, T.typ, Tp)
+ return false
+ }
+ return true
+ })
+ }
+
return false
}
func isUintptr(typ Type) bool {
- t := asBasic(typ)
+ t, _ := under(typ).(*Basic)
return t != nil && t.kind == Uintptr
}
func isUnsafePointer(typ Type) bool {
- // TODO(gri): Is this asBasic(typ) instead of typ.(*Basic) correct?
- // (The former calls under(), while the latter doesn't.)
- // The spec does not say so, but gc claims it is. See also
- // issue 6326.
- t := asBasic(typ)
+ t, _ := under(typ).(*Basic)
return t != nil && t.kind == UnsafePointer
}
func isPointer(typ Type) bool {
- return asPointer(typ) != nil
+ _, ok := under(typ).(*Pointer)
+ return ok
}
func isBytesOrRunes(typ Type) bool {
- if s := asSlice(typ); s != nil {
- t := asBasic(s.elem)
+ if s, _ := under(typ).(*Slice); s != nil {
+ t, _ := under(s.elem).(*Basic)
return t != nil && (t.kind == Byte || t.kind == Rune)
}
return false
diff --git a/libgo/go/go/types/decl.go b/libgo/go/go/types/decl.go
index 9211feb..cd6f709a 100644
--- a/libgo/go/go/types/decl.go
+++ b/libgo/go/go/types/decl.go
@@ -8,7 +8,6 @@ import (
"fmt"
"go/ast"
"go/constant"
- "go/internal/typeparams"
"go/token"
)
@@ -51,7 +50,7 @@ func pathString(path []Object) string {
return s
}
-// objDecl type-checks the declaration of obj in its respective (file) context.
+// objDecl type-checks the declaration of obj in its respective (file) environment.
// For the meaning of def, see Checker.definedType, in typexpr.go.
func (check *Checker) objDecl(obj Object, def *Named) {
if trace && obj.Type() == nil {
@@ -118,7 +117,7 @@ func (check *Checker) objDecl(obj Object, def *Named) {
fallthrough
case grey:
- // We have a cycle.
+ // We have a (possibly invalid) cycle.
// In the existing code, this is marked by a non-nil type
// for the object except for constants and variables whose
// type may be non-nil (known), or nil if it depends on the
@@ -130,17 +129,17 @@ func (check *Checker) objDecl(obj Object, def *Named) {
// order code.
switch obj := obj.(type) {
case *Const:
- if check.cycle(obj) || obj.typ == nil {
+ if !check.validCycle(obj) || obj.typ == nil {
obj.typ = Typ[Invalid]
}
case *Var:
- if check.cycle(obj) || obj.typ == nil {
+ if !check.validCycle(obj) || obj.typ == nil {
obj.typ = Typ[Invalid]
}
case *TypeName:
- if check.cycle(obj) {
+ if !check.validCycle(obj) {
// break cycle
// (without this, calling underlying()
// below may lead to an endless loop
@@ -150,7 +149,7 @@ func (check *Checker) objDecl(obj Object, def *Named) {
}
case *Func:
- if check.cycle(obj) {
+ if !check.validCycle(obj) {
// Don't set obj.typ to Typ[Invalid] here
// because plenty of code type-asserts that
// functions have a *Signature type. Grey
@@ -172,11 +171,11 @@ func (check *Checker) objDecl(obj Object, def *Named) {
unreachable()
}
- // save/restore current context and setup object context
- defer func(ctxt context) {
- check.context = ctxt
- }(check.context)
- check.context = context{
+ // save/restore current environment and set up object environment
+ defer func(env environment) {
+ check.environment = env
+ }(check.environment)
+ check.environment = environment{
scope: d.file,
}
@@ -204,9 +203,9 @@ func (check *Checker) objDecl(obj Object, def *Named) {
}
}
-// cycle checks if the cycle starting with obj is valid and
+// validCycle checks if the cycle starting with obj is valid and
// reports an error if it is not.
-func (check *Checker) cycle(obj Object) (isCycle bool) {
+func (check *Checker) validCycle(obj Object) (valid bool) {
// The object map contains the package scope objects and the non-interface methods.
if debug {
info := check.objMap[obj]
@@ -222,13 +221,23 @@ func (check *Checker) cycle(obj Object) (isCycle bool) {
assert(obj.color() >= grey)
start := obj.color() - grey // index of obj in objPath
cycle := check.objPath[start:]
- nval := 0 // number of (constant or variable) values in the cycle
- ndef := 0 // number of type definitions in the cycle
+ tparCycle := false // if set, the cycle is through a type parameter list
+ nval := 0 // number of (constant or variable) values in the cycle; valid if !generic
+ ndef := 0 // number of type definitions in the cycle; valid if !generic
+loop:
for _, obj := range cycle {
switch obj := obj.(type) {
case *Const, *Var:
nval++
case *TypeName:
+ // If we reach a generic type that is part of a cycle
+ // and we are in a type parameter list, we have a cycle
+ // through a type parameter list, which is invalid.
+ if check.inTParamList && isGeneric(obj.typ) {
+ tparCycle = true
+ break loop
+ }
+
// Determine if the type name is an alias or not. For
// package-level objects, use the object map which
// provides syntactic information (which doesn't rely
@@ -256,105 +265,38 @@ func (check *Checker) cycle(obj Object) (isCycle bool) {
if trace {
check.trace(obj.Pos(), "## cycle detected: objPath = %s->%s (len = %d)", pathString(cycle), obj.Name(), len(cycle))
- check.trace(obj.Pos(), "## cycle contains: %d values, %d type definitions", nval, ndef)
+ if tparCycle {
+ check.trace(obj.Pos(), "## cycle contains: generic type in a type parameter list")
+ } else {
+ check.trace(obj.Pos(), "## cycle contains: %d values, %d type definitions", nval, ndef)
+ }
defer func() {
- if isCycle {
+ if valid {
+ check.trace(obj.Pos(), "=> cycle is valid")
+ } else {
check.trace(obj.Pos(), "=> error: cycle is invalid")
}
}()
}
- // A cycle involving only constants and variables is invalid but we
- // ignore them here because they are reported via the initialization
- // cycle check.
- if nval == len(cycle) {
- return false
- }
-
- // A cycle involving only types (and possibly functions) must have at least
- // one type definition to be permitted: If there is no type definition, we
- // have a sequence of alias type names which will expand ad infinitum.
- if nval == 0 && ndef > 0 {
- return false // cycle is permitted
- }
-
- check.cycleError(cycle)
-
- return true
-}
-
-type typeInfo uint
-
-// validType verifies that the given type does not "expand" infinitely
-// producing a cycle in the type graph. Cycles are detected by marking
-// defined types.
-// (Cycles involving alias types, as in "type A = [10]A" are detected
-// earlier, via the objDecl cycle detection mechanism.)
-func (check *Checker) validType(typ Type, path []Object) typeInfo {
- const (
- unknown typeInfo = iota
- marked
- valid
- invalid
- )
-
- switch t := typ.(type) {
- case *Array:
- return check.validType(t.elem, path)
-
- case *Struct:
- for _, f := range t.fields {
- if check.validType(f.typ, path) == invalid {
- return invalid
- }
- }
-
- case *Interface:
- for _, etyp := range t.embeddeds {
- if check.validType(etyp, path) == invalid {
- return invalid
- }
- }
-
- case *Named:
- // don't touch the type if it is from a different package or the Universe scope
- // (doing so would lead to a race condition - was issue #35049)
- if t.obj.pkg != check.pkg {
- return valid
+ if !tparCycle {
+ // A cycle involving only constants and variables is invalid but we
+ // ignore them here because they are reported via the initialization
+ // cycle check.
+ if nval == len(cycle) {
+ return true
}
- // don't report a 2nd error if we already know the type is invalid
- // (e.g., if a cycle was detected earlier, via under).
- if t.underlying == Typ[Invalid] {
- t.info = invalid
- return invalid
+ // A cycle involving only types (and possibly functions) must have at least
+ // one type definition to be permitted: If there is no type definition, we
+ // have a sequence of alias type names which will expand ad infinitum.
+ if nval == 0 && ndef > 0 {
+ return true
}
-
- switch t.info {
- case unknown:
- t.info = marked
- t.info = check.validType(t.orig, append(path, t.obj)) // only types of current package added to path
- case marked:
- // cycle detected
- for i, tn := range path {
- if t.obj.pkg != check.pkg {
- panic("internal error: type cycle via package-external type")
- }
- if tn == t.obj {
- check.cycleError(path[i:])
- t.info = invalid
- return t.info
- }
- }
- panic("internal error: cycle start not found")
- }
- return t.info
-
- case *instance:
- return check.validType(t.expand(), path)
}
- return valid
+ check.cycleError(cycle)
+ return false
}
// cycleError reports a declaration cycle starting with
@@ -365,7 +307,16 @@ func (check *Checker) cycleError(cycle []Object) {
// cycle? That would be more consistent with other error messages.
i := firstInSrc(cycle)
obj := cycle[i]
- check.errorf(obj, _InvalidDeclCycle, "illegal cycle in declaration of %s", obj.Name())
+ // If obj is a type alias, mark it as valid (not broken) in order to avoid follow-on errors.
+ tname, _ := obj.(*TypeName)
+ if tname != nil && tname.IsAlias() {
+ check.validAlias(tname, Typ[Invalid])
+ }
+ if tname != nil && compilerErrorMessages {
+ check.errorf(obj, _InvalidDeclCycle, "invalid recursive type %s", obj.Name())
+ } else {
+ check.errorf(obj, _InvalidDeclCycle, "illegal cycle in declaration of %s", obj.Name())
+ }
for range cycle {
check.errorf(obj, _InvalidDeclCycle, "\t%s refers to", obj.Name()) // secondary error, \t indented
i++
@@ -566,172 +517,87 @@ func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) {
}
}
- check.initVars(lhs, []ast.Expr{init}, token.NoPos)
-}
-
-// under returns the expanded underlying type of n0; possibly by following
-// forward chains of named types. If an underlying type is found, resolve
-// the chain by setting the underlying type for each defined type in the
-// chain before returning it. If no underlying type is found or a cycle
-// is detected, the result is Typ[Invalid]. If a cycle is detected and
-// n0.check != nil, the cycle is reported.
-func (n0 *Named) under() Type {
- u := n0.underlying
-
- if u == Typ[Invalid] {
- return u
- }
-
- // If the underlying type of a defined type is not a defined
- // (incl. instance) type, then that is the desired underlying
- // type.
- switch u.(type) {
- case nil:
- return Typ[Invalid]
- default:
- // common case
- return u
- case *Named, *instance:
- // handled below
- }
-
- if n0.check == nil {
- panic("internal error: Named.check == nil but type is incomplete")
- }
-
- // Invariant: after this point n0 as well as any named types in its
- // underlying chain should be set up when this function exits.
- check := n0.check
-
- // If we can't expand u at this point, it is invalid.
- n := asNamed(u)
- if n == nil {
- n0.underlying = Typ[Invalid]
- return n0.underlying
- }
-
- // Otherwise, follow the forward chain.
- seen := map[*Named]int{n0: 0}
- path := []Object{n0.obj}
- for {
- u = n.underlying
- if u == nil {
- u = Typ[Invalid]
- break
- }
- var n1 *Named
- switch u1 := u.(type) {
- case *Named:
- n1 = u1
- case *instance:
- n1, _ = u1.expand().(*Named)
- if n1 == nil {
- u = Typ[Invalid]
- }
- }
- if n1 == nil {
- break // end of chain
- }
-
- seen[n] = len(seen)
- path = append(path, n.obj)
- n = n1
-
- if i, ok := seen[n]; ok {
- // cycle
- check.cycleError(path[i:])
- u = Typ[Invalid]
- break
- }
- }
-
- for n := range seen {
- // We should never have to update the underlying type of an imported type;
- // those underlying types should have been resolved during the import.
- // Also, doing so would lead to a race condition (was issue #31749).
- // Do this check always, not just in debug mode (it's cheap).
- if n.obj.pkg != check.pkg {
- panic("internal error: imported type with unresolved underlying type")
- }
- n.underlying = u
- }
-
- return u
+ check.initVars(lhs, []ast.Expr{init}, nil)
}
-func (n *Named) setUnderlying(typ Type) {
- if n != nil {
- n.underlying = typ
+// 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.IsMethodSet()
}
func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
assert(obj.typ == nil)
+ var rhs Type
check.later(func() {
- check.validType(obj.typ, nil)
- })
+ if t, _ := obj.typ.(*Named); t != nil { // type may be invalid
+ check.validType(t)
+ }
+ // 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, _UnsupportedFeature, "using type constraint %s requires go1.18 or later", rhs)
+ }
+ }).describef(obj, "validType(%s)", obj.Name())
alias := tdecl.Assign.IsValid()
- if alias && typeparams.Get(tdecl) != nil {
+ if alias && tdecl.TypeParams.NumFields() != 0 {
// The parser will ensure this but we may still get an invalid AST.
// Complain and continue as regular type definition.
- check.error(atPos(tdecl.Assign), 0, "generic type cannot be alias")
+ check.error(atPos(tdecl.Assign), _BadDecl, "generic type cannot be alias")
alias = false
}
+ // alias declaration
if alias {
- // type alias declaration
if !check.allowVersion(check.pkg, 1, 9) {
check.errorf(atPos(tdecl.Assign), _BadDecl, "type aliases requires go1.9 or later")
}
- obj.typ = Typ[Invalid]
- obj.typ = check.anyType(tdecl.Type)
-
- } else {
- // defined type declaration
+ check.brokenAlias(obj)
+ rhs = check.varType(tdecl.Type)
+ check.validAlias(obj, rhs)
+ return
+ }
- named := check.newNamed(obj, nil, nil)
- def.setUnderlying(named)
- obj.typ = named // make sure recursive type declarations terminate
+ // type definition or generic type declaration
+ named := check.newNamed(obj, nil, nil, nil, nil)
+ def.setUnderlying(named)
- if tparams := typeparams.Get(tdecl); tparams != nil {
- check.openScope(tdecl, "type parameters")
- defer check.closeScope()
- named.tparams = check.collectTypeParams(tparams)
- }
+ if tdecl.TypeParams != nil {
+ check.openScope(tdecl, "type parameters")
+ defer check.closeScope()
+ check.collectTypeParams(&named.tparams, tdecl.TypeParams)
+ }
- // determine underlying type of named
- named.orig = check.definedType(tdecl.Type, named)
+ // determine underlying type of named
+ 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:
- //
- // type (
- // A B
- // B *C
- // C A
- // )
- //
- // The type of C is the (named) type of A which is incomplete,
- // and which has as its underlying type the named type B.
- // Determine the (final, unnamed) underlying type by resolving
- // any forward chain.
- // TODO(gri) Investigate if we can just use named.origin here
- // and rely on lazy computation of the underlying type.
- named.underlying = under(named)
+ // If the underlying was not set while type-checking the right-hand side, it
+ // is invalid and an error should have been reported elsewhere.
+ if named.underlying == nil {
+ named.underlying = Typ[Invalid]
}
-}
-
-func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeName) {
- // Type parameter lists should not be empty. The parser will
- // complain but we still may get an incorrect AST: ignore it.
- if list.NumFields() == 0 {
- return
+ // Disallow a lone type parameter as the RHS of a type declaration (issue #45639).
+ // We don't need this restriction anymore if we make the underlying type of a type
+ // parameter its constraint interface: if the RHS is a lone type parameter, we will
+ // use its underlying type (like we do for any RHS in a type declaration), and its
+ // underlying type is an interface and the type declaration is well defined.
+ if isTypeParam(rhs) {
+ check.error(tdecl.Type, _MisplacedTypeParam, "cannot use a type parameter as RHS in type declaration")
+ named.underlying = Typ[Invalid]
}
+}
+func (check *Checker) collectTypeParams(dst **TypeParamList, list *ast.FieldList) {
+ var tparams []*TypeParam
// Declare type parameters up-front, with empty interface as type bound.
// The scope of type parameters starts at the beginning of the type parameter
// list (so we can have mutually recursive parameterized interfaces).
@@ -739,53 +605,85 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeNam
tparams = check.declareTypeParams(tparams, f.Names)
}
- setBoundAt := func(at int, bound Type) {
- assert(IsInterface(bound))
- tparams[at].typ.(*_TypeParam).bound = bound
- }
+ // Set the type parameters before collecting the type constraints because
+ // the parameterized type may be used by the constraints (issue #47887).
+ // Example: type T[P T[P]] interface{}
+ *dst = bindTParams(tparams)
+
+ // Signal to cycle detection that we are in a type parameter list.
+ // We can only be inside one type parameter list at any given time:
+ // function closures may appear inside a type parameter list but they
+ // cannot be generic, and their bodies are processed in delayed and
+ // sequential fashion. Note that with each new declaration, we save
+ // the existing environment and restore it when done; thus inTPList is
+ // true exactly only when we are in a specific type parameter list.
+ assert(!check.inTParamList)
+ check.inTParamList = true
+ defer func() {
+ check.inTParamList = false
+ }()
index := 0
- var bound Type
+ var bounds []Type
for _, f := range list.List {
- if f.Type == nil {
- goto next
- }
-
- // The predeclared identifier "any" is visible only as a constraint
- // in a type parameter list. Look for it before general constraint
- // resolution.
- if tident, _ := unparen(f.Type).(*ast.Ident); tident != nil && tident.Name == "any" && check.lookup("any") == nil {
- bound = universeAny
+ var bound Type
+ // NOTE: we may be able to assert that f.Type != nil here, but this is not
+ // an invariant of the AST, so we are cautious.
+ if f.Type != nil {
+ bound = check.bound(f.Type)
+ if isTypeParam(bound) {
+ // We may be able to allow this since it is now well-defined what
+ // the underlying type and thus type set of a type parameter is.
+ // But we may need some additional form of cycle detection within
+ // type parameter lists.
+ check.error(f.Type, _MisplacedTypeParam, "cannot use a type parameter as constraint")
+ bound = Typ[Invalid]
+ }
} else {
- bound = check.typ(f.Type)
+ bound = Typ[Invalid]
}
-
- // type bound must be an interface
- // TODO(gri) We should delay the interface check because
- // we may not have a complete interface yet:
- // type C(type T C) interface {}
- // (issue #39724).
- if _, ok := under(bound).(*Interface); ok {
- // Otherwise, set the bound for each type parameter.
- for i := range f.Names {
- setBoundAt(index+i, bound)
- }
- } else if bound != Typ[Invalid] {
- check.errorf(f.Type, _Todo, "%s is not an interface", bound)
+ bounds = append(bounds, bound)
+ for i := range f.Names {
+ tparams[index+i].bound = bound
}
-
- next:
index += len(f.Names)
}
+}
- return
+func (check *Checker) bound(x ast.Expr) Type {
+ // A type set literal of the form ~T and A|B may only appear as constraint;
+ // embed it in an implicit interface so that only interface type-checking
+ // needs to take care of such type expressions.
+ wrap := false
+ switch op := x.(type) {
+ case *ast.UnaryExpr:
+ wrap = op.Op == token.TILDE
+ case *ast.BinaryExpr:
+ wrap = op.Op == token.OR
+ }
+ if wrap {
+ x = &ast.InterfaceType{Methods: &ast.FieldList{List: []*ast.Field{{Type: x}}}}
+ t := check.typ(x)
+ // mark t as implicit interface if all went well
+ if t, _ := t.(*Interface); t != nil {
+ t.implicit = true
+ }
+ return t
+ }
+ return check.typ(x)
}
-func (check *Checker) declareTypeParams(tparams []*TypeName, names []*ast.Ident) []*TypeName {
+func (check *Checker) declareTypeParams(tparams []*TypeParam, names []*ast.Ident) []*TypeParam {
+ // Use Typ[Invalid] for the type constraint to ensure that a type
+ // is present even if the actual constraint has not been assigned
+ // yet.
+ // TODO(gri) Need to systematically review all uses of type parameter
+ // constraints to make sure we don't rely on them if they
+ // are not properly set yet.
for _, name := range names {
- tpar := NewTypeName(name.Pos(), check.pkg, name.Name, nil)
- check.newTypeParam(tpar, len(tparams), &emptyInterface) // assigns type to tpar as a side-effect
- check.declare(check.scope, name, tpar, check.scope.pos) // TODO(gri) check scope position
+ tname := NewTypeName(name.Pos(), check.pkg, name.Name, nil)
+ tpar := check.newTypeParam(tname, Typ[Invalid]) // assigns type to tpar as a side-effect
+ check.declare(check.scope, name, tname, check.scope.pos) // TODO(gri) check scope position
tparams = append(tparams, tpar)
}
@@ -813,9 +711,11 @@ func (check *Checker) collectMethods(obj *TypeName) {
// spec: "If the base type is a struct type, the non-blank method
// and field names must be distinct."
- base := asNamed(obj.typ) // shouldn't fail but be conservative
+ base, _ := obj.typ.(*Named) // shouldn't fail but be conservative
if base != nil {
- if t, _ := base.underlying.(*Struct); t != nil {
+ assert(base.targs.Len() == 0) // collectMethods should not be called on an instantiated type
+ u := base.under()
+ if t, _ := u.(*Struct); t != nil {
for _, fld := range t.fields {
if fld.name != "_" {
assert(mset.insert(fld) == nil)
@@ -826,7 +726,8 @@ func (check *Checker) collectMethods(obj *TypeName) {
// Checker.Files may be called multiple times; additional package files
// may add methods to already type-checked types. Add pre-existing methods
// so that we can detect redeclarations.
- for _, m := range base.methods {
+ for i := 0; i < base.methods.Len(); i++ {
+ m := base.methods.At(i, nil)
assert(m.name != "_")
assert(mset.insert(m) == nil)
}
@@ -851,7 +752,8 @@ func (check *Checker) collectMethods(obj *TypeName) {
}
if base != nil {
- base.methods = append(base.methods, m)
+ base.resolve(nil) // TODO(mdempsky): Probably unnecessary.
+ base.AddMethod(m)
}
}
}
@@ -877,6 +779,10 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
check.funcType(sig, fdecl.Recv, fdecl.Type)
obj.color_ = saved
+ if fdecl.Type.TypeParams.NumFields() > 0 && fdecl.Body == nil {
+ check.softErrorf(fdecl.Name, _BadDecl, "parameterized function is missing function body")
+ }
+
// function body must be type-checked after global declarations
// (functions implemented elsewhere have no body)
if !check.conf.IgnoreFuncBodies && fdecl.Body != nil {
diff --git a/libgo/go/go/types/errorcodes.go b/libgo/go/go/types/errorcodes.go
index 3d24da7..b3796e8 100644
--- a/libgo/go/go/types/errorcodes.go
+++ b/libgo/go/go/types/errorcodes.go
@@ -281,16 +281,7 @@ const (
_IncomparableMapKey
// _InvalidIfaceEmbed occurs when a non-interface type is embedded in an
- // interface.
- //
- // Example:
- // type T struct {}
- //
- // func (T) m()
- //
- // type I interface {
- // T
- // }
+ // interface (for go 1.17 or earlier).
_InvalidIfaceEmbed
// _InvalidPtrEmbed occurs when an embedded field is of the pointer form *T,
@@ -884,7 +875,7 @@ const (
// context in which it is used.
//
// Example:
- // var _ = 1 + ""
+ // var _ = 1 + new(int)
_InvalidUntypedConversion
// _BadOffsetofSyntax occurs when unsafe.Offsetof is called with an argument
@@ -1310,7 +1301,103 @@ const (
// var _ = unsafe.Slice(&x, uint64(1) << 63)
_InvalidUnsafeSlice
- // _Todo is a placeholder for error codes that have not been decided.
- // TODO(rFindley) remove this error code after deciding on errors for generics code.
- _Todo
+ // All codes below were added in Go 1.18.
+
+ // _UnsupportedFeature occurs when a language feature is used that is not
+ // supported at this Go version.
+ _UnsupportedFeature
+
+ // _NotAGenericType occurs when a non-generic type is used where a generic
+ // type is expected: in type or function instantiation.
+ //
+ // Example:
+ // type T int
+ //
+ // var _ T[int]
+ _NotAGenericType
+
+ // _WrongTypeArgCount occurs when a type or function is instantiated with an
+ // incorrent number of type arguments, including when a generic type or
+ // function is used without instantiation.
+ //
+ // Errors inolving failed type inference are assigned other error codes.
+ //
+ // Example:
+ // type T[p any] int
+ //
+ // var _ T[int, string]
+ //
+ // Example:
+ // func f[T any]() {}
+ //
+ // var x = f
+ _WrongTypeArgCount
+
+ // _CannotInferTypeArgs occurs when type or function type argument inference
+ // fails to infer all type arguments.
+ //
+ // Example:
+ // func f[T any]() {}
+ //
+ // func _() {
+ // f()
+ // }
+ //
+ // Example:
+ // type N[P, Q any] struct{}
+ //
+ // var _ N[int]
+ _CannotInferTypeArgs
+
+ // _InvalidTypeArg occurs when a type argument does not satisfy its
+ // corresponding type parameter constraints.
+ //
+ // Example:
+ // type T[P ~int] struct{}
+ //
+ // var _ T[string]
+ _InvalidTypeArg // arguments? InferenceFailed
+
+ // _InvalidInstanceCycle occurs when an invalid cycle is detected
+ // within the instantiation graph.
+ //
+ // Example:
+ // func f[T any]() { f[*T]() }
+ _InvalidInstanceCycle
+
+ // _InvalidUnion occurs when an embedded union or approximation element is
+ // not valid.
+ //
+ // Example:
+ // type _ interface {
+ // ~int | interface{ m() }
+ // }
+ _InvalidUnion
+
+ // _MisplacedConstraintIface occurs when a constraint-type interface is used
+ // outside of constraint position.
+ //
+ // Example:
+ // type I interface { ~int }
+ //
+ // var _ I
+ _MisplacedConstraintIface
+
+ // _InvalidMethodTypeParams occurs when methods have type parameters.
+ //
+ // Example:
+ // type T int
+ //
+ // func (T) m[P any]() {}
+ _InvalidMethodTypeParams
+
+ // _MisplacedTypeParam occurs when a type parameter is used in a place where
+ // it is not permitted.
+ //
+ // Example:
+ // type T[P any] P
+ //
+ // Example:
+ // type T[P any] struct{ *P }
+ _MisplacedTypeParam
)
diff --git a/libgo/go/go/types/errorcodes_test.go b/libgo/go/go/types/errorcodes_test.go
index 9652920..7ce0158 100644
--- a/libgo/go/go/types/errorcodes_test.go
+++ b/libgo/go/go/types/errorcodes_test.go
@@ -173,8 +173,10 @@ func TestErrorCodeStyle(t *testing.T) {
}
}
doc := spec.Doc.Text()
- if !strings.HasPrefix(doc, name) {
- t.Errorf("doc for %q does not start with identifier", name)
+ if doc == "" {
+ t.Errorf("%q is undocumented", name)
+ } else if !strings.HasPrefix(doc, name) {
+ t.Errorf("doc for %q does not start with the error code name", name)
}
lowerComment := strings.ToLower(strings.TrimPrefix(doc, name))
for _, bad := range forbiddenInComment {
diff --git a/libgo/go/go/types/errors.go b/libgo/go/go/types/errors.go
index 2263106..ce62a8c 100644
--- a/libgo/go/go/types/errors.go
+++ b/libgo/go/go/types/errors.go
@@ -7,6 +7,7 @@
package types
import (
+ "bytes"
"errors"
"fmt"
"go/ast"
@@ -62,40 +63,63 @@ func (check *Checker) markImports(pkg *Package) {
}
}
-func (check *Checker) sprintf(format string, args ...interface{}) string {
+func (check *Checker) sprintf(format string, args ...any) string {
+ return sprintf(check.fset, check.qualifier, false, format, args...)
+}
+
+func sprintf(fset *token.FileSet, qf Qualifier, debug bool, format string, args ...any) string {
for i, arg := range args {
switch a := arg.(type) {
case nil:
arg = "<nil>"
case operand:
- panic("internal error: should always pass *operand")
+ panic("got operand instead of *operand")
case *operand:
- arg = operandString(a, check.qualifier)
+ arg = operandString(a, qf)
case token.Pos:
- arg = check.fset.Position(a).String()
+ if fset != nil {
+ arg = fset.Position(a).String()
+ }
case ast.Expr:
arg = ExprString(a)
+ case []ast.Expr:
+ var buf bytes.Buffer
+ buf.WriteByte('[')
+ writeExprList(&buf, a)
+ buf.WriteByte(']')
+ arg = buf.String()
case Object:
- arg = ObjectString(a, check.qualifier)
+ arg = ObjectString(a, qf)
case Type:
- arg = TypeString(a, check.qualifier)
+ arg = typeString(a, qf, debug)
+ case []Type:
+ var buf bytes.Buffer
+ buf.WriteByte('[')
+ for i, x := range a {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ buf.WriteString(typeString(x, qf, debug))
+ }
+ buf.WriteByte(']')
+ arg = buf.String()
}
args[i] = arg
}
return fmt.Sprintf(format, args...)
}
-func (check *Checker) trace(pos token.Pos, format string, args ...interface{}) {
+func (check *Checker) trace(pos token.Pos, format string, args ...any) {
fmt.Printf("%s:\t%s%s\n",
check.fset.Position(pos),
strings.Repeat(". ", check.indent),
- check.sprintf(format, args...),
+ sprintf(check.fset, check.qualifier, true, format, args...),
)
}
// dump is only needed for debugging
-func (check *Checker) dump(format string, args ...interface{}) {
- fmt.Println(check.sprintf(format, args...))
+func (check *Checker) dump(format string, args ...any) {
+ fmt.Println(sprintf(check.fset, check.qualifier, true, format, args...))
}
func (check *Checker) err(err error) {
@@ -164,7 +188,7 @@ func (check *Checker) newError(at positioner, code errorCode, soft bool, msg str
}
// newErrorf creates a new Error, but does not handle it.
-func (check *Checker) newErrorf(at positioner, code errorCode, soft bool, format string, args ...interface{}) error {
+func (check *Checker) newErrorf(at positioner, code errorCode, soft bool, format string, args ...any) error {
msg := check.sprintf(format, args...)
return check.newError(at, code, soft, msg)
}
@@ -173,23 +197,23 @@ func (check *Checker) error(at positioner, code errorCode, msg string) {
check.err(check.newError(at, code, false, msg))
}
-func (check *Checker) errorf(at positioner, code errorCode, format string, args ...interface{}) {
+func (check *Checker) errorf(at positioner, code errorCode, format string, args ...any) {
check.error(at, code, check.sprintf(format, args...))
}
-func (check *Checker) softErrorf(at positioner, code errorCode, format string, args ...interface{}) {
+func (check *Checker) softErrorf(at positioner, code errorCode, format string, args ...any) {
check.err(check.newErrorf(at, code, true, format, args...))
}
-func (check *Checker) invalidAST(at positioner, format string, args ...interface{}) {
+func (check *Checker) invalidAST(at positioner, format string, args ...any) {
check.errorf(at, 0, "invalid AST: "+format, args...)
}
-func (check *Checker) invalidArg(at positioner, code errorCode, format string, args ...interface{}) {
+func (check *Checker) invalidArg(at positioner, code errorCode, format string, args ...any) {
check.errorf(at, code, "invalid argument: "+format, args...)
}
-func (check *Checker) invalidOp(at positioner, code errorCode, format string, args ...interface{}) {
+func (check *Checker) invalidOp(at positioner, code errorCode, format string, args ...any) {
check.errorf(at, code, "invalid operation: "+format, args...)
}
@@ -236,7 +260,7 @@ func (s atPos) Pos() token.Pos {
func spanOf(at positioner) posSpan {
switch x := at.(type) {
case nil:
- panic("internal error: nil")
+ panic("nil positioner")
case posSpan:
return x
case ast.Node:
@@ -259,7 +283,7 @@ func stripAnnotations(s string) string {
var b strings.Builder
for _, r := range s {
// strip #'s and subscript digits
- if r != instanceMarker && !('₀' <= r && r < '₀'+10) { // '₀' == U+2080
+ if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
b.WriteRune(r)
}
}
diff --git a/libgo/go/go/types/errors_test.go b/libgo/go/go/types/errors_test.go
index fdbe07c..942a9fd 100644
--- a/libgo/go/go/types/errors_test.go
+++ b/libgo/go/go/types/errors_test.go
@@ -15,7 +15,6 @@ func TestStripAnnotations(t *testing.T) {
{"foo", "foo"},
{"foo₀", "foo"},
{"foo(T₀)", "foo(T)"},
- {"#foo(T₀)", "foo(T)"},
} {
got := stripAnnotations(test.in)
if got != test.want {
diff --git a/libgo/go/go/types/eval.go b/libgo/go/go/types/eval.go
index 5125960..c8bb005 100644
--- a/libgo/go/go/types/eval.go
+++ b/libgo/go/go/types/eval.go
@@ -35,9 +35,10 @@ func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (_ Type
return info.Types[node], err
}
-// CheckExpr type checks the expression expr as if it had appeared at
-// position pos of package pkg. Type information about the expression
-// is recorded in info.
+// CheckExpr type checks the expression expr as if it had appeared at position
+// pos of package pkg. Type information about the expression is recorded in
+// info. The expression may be an uninstantiated parameterized function or
+// type.
//
// If pkg == nil, the Universe scope is used and the provided
// position pos is ignored. If pkg != nil, and pos is invalid,
@@ -91,8 +92,8 @@ func CheckExpr(fset *token.FileSet, pkg *Package, pos token.Pos, expr ast.Expr,
// evaluate node
var x operand
- check.rawExpr(&x, expr, nil)
- check.processDelayed(0) // incl. all functions
+ check.rawExpr(&x, expr, nil, true) // allow generic expressions
+ check.processDelayed(0) // incl. all functions
check.recordUntyped()
return nil
diff --git a/libgo/go/go/types/eval_test.go b/libgo/go/go/types/eval_test.go
index 41d3a61..b0745c1 100644
--- a/libgo/go/go/types/eval_test.go
+++ b/libgo/go/go/types/eval_test.go
@@ -111,7 +111,7 @@ func TestEvalPos(t *testing.T) {
x = a + len(s)
return float64(x)
/* true => true, untyped bool */
- /* fmt.Println => , func(a ...interface{}) (n int, err error) */
+ /* fmt.Println => , func(a ...any) (n int, err error) */
/* c => 3, untyped float */
/* T => , p.T */
/* a => , int */
@@ -195,10 +195,10 @@ func TestEvalPos(t *testing.T) {
}
}
-// split splits string s at the first occurrence of s.
+// split splits string s at the first occurrence of s, trimming spaces.
func split(s, sep string) (string, string) {
- i := strings.Index(s, sep)
- return strings.TrimSpace(s[:i]), strings.TrimSpace(s[i+len(sep):])
+ before, after, _ := strings.Cut(s, sep)
+ return strings.TrimSpace(before), strings.TrimSpace(after)
}
func TestCheckExpr(t *testing.T) {
@@ -218,7 +218,7 @@ type T []int
type S struct{ X int }
func f(a int, s string) S {
- /* fmt.Println => func fmt.Println(a ...interface{}) (n int, err error) */
+ /* fmt.Println => func fmt.Println(a ...any) (n int, err error) */
/* fmt.Stringer.String => func (fmt.Stringer).String() string */
fmt.Println("calling f")
diff --git a/libgo/go/go/types/example_test.go b/libgo/go/go/types/example_test.go
index ffbb5c0..fd52c9f 100644
--- a/libgo/go/go/types/example_test.go
+++ b/libgo/go/go/types/example_test.go
@@ -6,7 +6,6 @@
// access to compiled packages for import.
//
//go:build !arm && !arm64
-// +build !arm,!arm64
package types_test
diff --git a/libgo/go/go/types/expr.go b/libgo/go/go/types/expr.go
index 58962e7..44e0288 100644
--- a/libgo/go/go/types/expr.go
+++ b/libgo/go/go/types/expr.go
@@ -64,17 +64,17 @@ var unaryOpPredicates opPredicates
func init() {
// Setting unaryOpPredicates in init avoids declaration cycles.
unaryOpPredicates = opPredicates{
- token.ADD: isNumeric,
- token.SUB: isNumeric,
- token.XOR: isInteger,
- token.NOT: isBoolean,
+ token.ADD: allNumeric,
+ token.SUB: allNumeric,
+ token.XOR: allInteger,
+ token.NOT: allBoolean,
}
}
func (check *Checker) op(m opPredicates, x *operand, op token.Token) bool {
if pred := m[op]; pred != nil {
if !pred(x.typ) {
- check.invalidOp(x, _UndefinedOp, "operator %s not defined for %s", op, x)
+ check.invalidOp(x, _UndefinedOp, "operator %s not defined on %s", op, x)
return false
}
} else {
@@ -100,8 +100,10 @@ func (check *Checker) overflow(x *operand, op token.Token, opPos token.Pos) {
// Typed constants must be representable in
// their type after each constant operation.
+ // x.typ cannot be a type parameter (type
+ // parameters cannot be constant types).
if isTyped(x.typ) {
- check.representable(x, asBasic(x.typ))
+ check.representable(x, under(x.typ).(*Basic))
return
}
@@ -114,9 +116,7 @@ func (check *Checker) overflow(x *operand, op token.Token, opPos token.Pos) {
}
// 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 ast.Expr) string {
switch e := e.(type) {
case *ast.BinaryExpr:
@@ -144,6 +144,15 @@ var op2str2 = [...]string{
token.SHL: "shift",
}
+// If typ is a type parameter, underIs returns the result of typ.underIs(f).
+// Otherwise, underIs returns the result of f(under(typ)).
+func underIs(typ Type, f func(Type) bool) bool {
+ if tpar, _ := typ.(*TypeParam); tpar != nil {
+ return tpar.underIs(f)
+ }
+ return f(under(typ))
+}
+
// The unary expression e may be nil. It's passed in for better error messages only.
func (check *Checker) unary(x *operand, e *ast.UnaryExpr) {
check.expr(x, e.X)
@@ -164,19 +173,26 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr) {
return
case token.ARROW:
- typ := asChan(x.typ)
- if typ == nil {
+ u := structuralType(x.typ)
+ if u == nil {
+ check.invalidOp(x, _InvalidReceive, "cannot receive from %s: no structural type", x)
+ x.mode = invalid
+ return
+ }
+ ch, _ := u.(*Chan)
+ if ch == nil {
check.invalidOp(x, _InvalidReceive, "cannot receive from non-channel %s", x)
x.mode = invalid
return
}
- if typ.dir == SendOnly {
+ if ch.dir == SendOnly {
check.invalidOp(x, _InvalidReceive, "cannot receive from send-only channel %s", x)
x.mode = invalid
return
}
+
x.mode = commaok
- x.typ = typ.elem
+ x.typ = ch.elem
check.hasCallOrRecv = true
return
}
@@ -456,8 +472,11 @@ func (check *Checker) invalidConversion(code errorCode, x *operand, target Type)
// Also, if x is a constant, it must be representable as a value of typ,
// and if x is the (formerly untyped) lhs operand of a non-constant
// shift, it must be an integer value.
-//
func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) {
+ check.updateExprType0(nil, x, typ, final)
+}
+
+func (check *Checker) updateExprType0(parent, x ast.Expr, typ Type, final bool) {
old, found := check.untyped[x]
if !found {
return // nothing to do
@@ -499,7 +518,7 @@ func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) {
// No operands to take care of.
case *ast.ParenExpr:
- check.updateExprType(x.X, typ, final)
+ check.updateExprType0(x, x.X, typ, final)
case *ast.UnaryExpr:
// If x is a constant, the operands were constants.
@@ -510,7 +529,7 @@ func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) {
if old.val != nil {
break
}
- check.updateExprType(x.X, typ, final)
+ check.updateExprType0(x, x.X, typ, final)
case *ast.BinaryExpr:
if old.val != nil {
@@ -522,11 +541,11 @@ func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) {
} else if isShift(x.Op) {
// The result type depends only on lhs operand.
// The rhs type was updated when checking the shift.
- check.updateExprType(x.X, typ, final)
+ check.updateExprType0(x, x.X, typ, final)
} else {
// The operand types match the result type.
- check.updateExprType(x.X, typ, final)
- check.updateExprType(x.Y, typ, final)
+ check.updateExprType0(x, x.X, typ, final)
+ check.updateExprType0(x, x.Y, typ, final)
}
default:
@@ -536,7 +555,7 @@ func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) {
// If the new type is not final and still untyped, just
// update the recorded type.
if !final && isUntyped(typ) {
- old.typ = asBasic(typ)
+ old.typ = under(typ).(*Basic)
check.untyped[x] = old
return
}
@@ -549,8 +568,12 @@ func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) {
// If x is the lhs of a shift, its final type must be integer.
// We already know from the shift check that it is representable
// as an integer if it is a constant.
- if !isInteger(typ) {
- check.invalidOp(x, _InvalidShiftOperand, "shifted operand %s (type %s) must be integer", x, typ)
+ if !allInteger(typ) {
+ if compilerErrorMessages {
+ check.invalidOp(x, _InvalidShiftOperand, "%s (shift of type %s)", parent, typ)
+ } else {
+ check.invalidOp(x, _InvalidShiftOperand, "shifted operand %s (type %s) must be integer", x, typ)
+ }
return
}
// Even if we have an integer, if the value is a constant we
@@ -582,7 +605,11 @@ func (check *Checker) updateExprVal(x ast.Expr, val constant.Value) {
func (check *Checker) convertUntyped(x *operand, target Type) {
newType, val, code := check.implicitTypeAndValue(x, target)
if code != 0 {
- check.invalidConversion(code, x, target.Underlying())
+ t := target
+ if !isTypeParam(target) {
+ t = safeUnderlying(target)
+ }
+ check.invalidConversion(code, x, t)
x.mode = invalid
return
}
@@ -603,7 +630,6 @@ func (check *Checker) convertUntyped(x *operand, target Type) {
// If x is a constant operand, the returned constant.Value will be the
// representation of x in this context.
func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, constant.Value, errorCode) {
- target = expand(target)
if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] {
return x.typ, nil, 0
}
@@ -622,10 +648,10 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
return x.typ, nil, 0
}
- switch t := optype(target).(type) {
+ switch u := under(target).(type) {
case *Basic:
if x.mode == constant_ {
- v, code := check.representation(x, t)
+ v, code := check.representation(x, u)
if code != 0 {
return nil, nil, code
}
@@ -661,19 +687,23 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
default:
return nil, nil, _InvalidUntypedConversion
}
- case *_Sum:
- ok := t.is(func(t Type) bool {
- target, _, _ := check.implicitTypeAndValue(x, t)
- return target != nil
- })
- if !ok {
- return nil, nil, _InvalidUntypedConversion
- }
- // keep nil untyped (was bug #39755)
- if x.isNil() {
- return Typ[UntypedNil], nil, 0
- }
case *Interface:
+ if isTypeParam(target) {
+ if !u.typeSet().underIs(func(u Type) bool {
+ if u == nil {
+ return false
+ }
+ t, _, _ := check.implicitTypeAndValue(x, u)
+ return t != nil
+ }) {
+ return nil, nil, _InvalidUntypedConversion
+ }
+ // keep nil untyped (was bug #39755)
+ if x.isNil() {
+ return Typ[UntypedNil], nil, 0
+ }
+ break
+ }
// Values must have concrete dynamic types. If the value is nil,
// keep it untyped (this is important for tools such as go vet which
// need the dynamic type for argument checking of say, print
@@ -682,8 +712,7 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
return Typ[UntypedNil], nil, 0
}
// cannot assign untyped values to non-empty interfaces
- check.completeInterface(token.NoPos, t)
- if !t.Empty() {
+ if !u.Empty() {
return nil, nil, _InvalidUntypedConversion
}
return Default(x.typ), nil, 0
@@ -707,23 +736,33 @@ func (check *Checker) comparison(x, y *operand, op token.Token) {
xok, _ := x.assignableTo(check, y.typ, nil)
yok, _ := y.assignableTo(check, x.typ, nil)
if xok || yok {
+ equality := false
defined := false
switch op {
case token.EQL, token.NEQ:
// spec: "The equality operators == and != apply to operands that are comparable."
+ equality = true
defined = Comparable(x.typ) && Comparable(y.typ) || x.isNil() && hasNil(y.typ) || y.isNil() && hasNil(x.typ)
case token.LSS, token.LEQ, token.GTR, token.GEQ:
// spec: The ordering operators <, <=, >, and >= apply to operands that are ordered."
- defined = isOrdered(x.typ) && isOrdered(y.typ)
+ defined = allOrdered(x.typ) && allOrdered(y.typ)
default:
unreachable()
}
if !defined {
- typ := x.typ
- if x.isNil() {
- typ = y.typ
+ if equality && (isTypeParam(x.typ) || isTypeParam(y.typ)) {
+ typ := x.typ
+ if isTypeParam(y.typ) {
+ typ = y.typ
+ }
+ err = check.sprintf("%s is not comparable", typ)
+ } else {
+ typ := x.typ
+ if x.isNil() {
+ typ = y.typ
+ }
+ err = check.sprintf("operator %s not defined on %s", op, typ)
}
- err = check.sprintf("operator %s not defined for %s", op, typ)
code = _UndefinedOp
}
} else {
@@ -765,7 +804,7 @@ func (check *Checker) shift(x, y *operand, e ast.Expr, op token.Token) {
xval = constant.ToInt(x.val)
}
- if isInteger(x.typ) || isUntyped(x.typ) && xval != nil && xval.Kind() == constant.Int {
+ if allInteger(x.typ) || isUntyped(x.typ) && xval != nil && xval.Kind() == constant.Int {
// The lhs is of integer type or an untyped constant representable
// as an integer. Nothing to do.
} else {
@@ -802,8 +841,8 @@ func (check *Checker) shift(x, y *operand, e ast.Expr, op token.Token) {
// Check that RHS is otherwise at least of integer type.
switch {
- case isInteger(y.typ):
- if !isUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) {
+ case allInteger(y.typ):
+ if !allUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) {
check.invalidOp(y, _InvalidShiftCount, "signed shift count %s requires go1.13 or later", y)
x.mode = invalid
return
@@ -891,7 +930,7 @@ func (check *Checker) shift(x, y *operand, e ast.Expr, op token.Token) {
}
// non-constant shift - lhs must be an integer
- if !isInteger(x.typ) {
+ if !allInteger(x.typ) {
check.invalidOp(x, _InvalidShiftOperand, "shifted operand %s must be integer", x)
x.mode = invalid
return
@@ -905,19 +944,19 @@ var binaryOpPredicates opPredicates
func init() {
// Setting binaryOpPredicates in init avoids declaration cycles.
binaryOpPredicates = opPredicates{
- token.ADD: isNumericOrString,
- token.SUB: isNumeric,
- token.MUL: isNumeric,
- token.QUO: isNumeric,
- token.REM: isInteger,
-
- token.AND: isInteger,
- token.OR: isInteger,
- token.XOR: isInteger,
- token.AND_NOT: isInteger,
-
- token.LAND: isBoolean,
- token.LOR: isBoolean,
+ token.ADD: allNumericOrString,
+ token.SUB: allNumeric,
+ token.MUL: allNumeric,
+ token.QUO: allNumeric,
+ token.REM: allInteger,
+
+ token.AND: allInteger,
+ token.OR: allInteger,
+ token.XOR: allInteger,
+ token.AND_NOT: allInteger,
+
+ token.LAND: allBoolean,
+ token.LOR: allBoolean,
}
}
@@ -943,14 +982,35 @@ func (check *Checker) binary(x *operand, e ast.Expr, lhs, rhs ast.Expr, op token
return
}
- check.convertUntyped(x, y.typ)
- if x.mode == invalid {
- return
+ // TODO(gri) make canMix more efficient - called for each binary operation
+ canMix := func(x, y *operand) bool {
+ if IsInterface(x.typ) && !isTypeParam(x.typ) || IsInterface(y.typ) && !isTypeParam(y.typ) {
+ return true
+ }
+ if allBoolean(x.typ) != allBoolean(y.typ) {
+ return false
+ }
+ if allString(x.typ) != allString(y.typ) {
+ return false
+ }
+ if x.isNil() && !hasNil(y.typ) {
+ return false
+ }
+ if y.isNil() && !hasNil(x.typ) {
+ return false
+ }
+ return true
}
- check.convertUntyped(&y, x.typ)
- if y.mode == invalid {
- x.mode = invalid
- return
+ if canMix(x, &y) {
+ check.convertUntyped(x, y.typ)
+ if x.mode == invalid {
+ return
+ }
+ check.convertUntyped(&y, x.typ)
+ if y.mode == invalid {
+ x.mode = invalid
+ return
+ }
}
if isComparison(op) {
@@ -958,7 +1018,7 @@ func (check *Checker) binary(x *operand, e ast.Expr, lhs, rhs ast.Expr, op token
return
}
- if !check.identical(x.typ, y.typ) {
+ if !Identical(x.typ, y.typ) {
// only report an error if we have valid types
// (otherwise we had an error reported elsewhere already)
if x.typ != Typ[Invalid] && y.typ != Typ[Invalid] {
@@ -966,7 +1026,11 @@ func (check *Checker) binary(x *operand, e ast.Expr, lhs, rhs ast.Expr, op token
if e != nil {
posn = e
}
- check.invalidOp(posn, _MismatchedTypes, "mismatched types %s and %s", x.typ, y.typ)
+ if e != nil {
+ check.invalidOp(posn, _MismatchedTypes, "%s (mismatched types %s and %s)", e, x.typ, y.typ)
+ } else {
+ check.invalidOp(posn, _MismatchedTypes, "%s %s= %s (mismatched types %s and %s)", lhs, op, rhs, x.typ, y.typ)
+ }
}
x.mode = invalid
return
@@ -979,7 +1043,7 @@ func (check *Checker) binary(x *operand, e ast.Expr, lhs, rhs ast.Expr, op token
if op == token.QUO || op == token.REM {
// check for zero divisor
- if (x.mode == constant_ || isInteger(x.typ)) && y.mode == constant_ && constant.Sign(y.val) == 0 {
+ if (x.mode == constant_ || allInteger(x.typ)) && y.mode == constant_ && constant.Sign(y.val) == 0 {
check.invalidOp(&y, _DivByZero, "division by zero")
x.mode = invalid
return
@@ -1031,8 +1095,10 @@ const (
// rawExpr typechecks expression e and initializes x with the expression
// value or type. If an error occurred, x.mode is set to invalid.
// If hint != nil, it is the type of a composite literal element.
+// If allowGeneric is set, the operand type may be an uninstantiated
+// parameterized type or function value.
//
-func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind {
+func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type, allowGeneric bool) exprKind {
if trace {
check.trace(e.Pos(), "expr %s", e)
check.indent++
@@ -1043,11 +1109,40 @@ func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind {
}
kind := check.exprInternal(x, e, hint)
+
+ if !allowGeneric {
+ check.nonGeneric(x)
+ }
+
check.record(x)
return kind
}
+// If x is a generic function or type, nonGeneric reports an error and invalidates x.mode and x.typ.
+// Otherwise it leaves x alone.
+func (check *Checker) nonGeneric(x *operand) {
+ if x.mode == invalid || x.mode == novalue {
+ return
+ }
+ var what string
+ switch t := x.typ.(type) {
+ case *Named:
+ if isGeneric(t) {
+ what = "type"
+ }
+ case *Signature:
+ if t.tparams != nil {
+ what = "function"
+ }
+ }
+ if what != "" {
+ check.errorf(x.expr, _WrongTypeArgCount, "cannot use generic %s %s without instantiation", what, x.expr)
+ x.mode = invalid
+ x.typ = Typ[Invalid]
+ }
+}
+
// exprInternal contains the core of type checking of expressions.
// Must only be called by rawExpr.
//
@@ -1146,7 +1241,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
case hint != nil:
// no composite literal type present - use hint (element type of enclosing type)
typ = hint
- base, _ = deref(under(typ)) // *T implies &T{}
+ base, _ = deref(structuralType(typ)) // *T implies &T{}
default:
// TODO(gri) provide better error messages depending on context
@@ -1154,8 +1249,14 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
goto Error
}
- switch utyp := optype(base).(type) {
+ switch utyp := structuralType(base).(type) {
case *Struct:
+ // Prevent crash if the struct referred to is not yet set up.
+ // See analogous comment for *Array.
+ if utyp.fields == nil {
+ check.error(e, _InvalidDeclCycle, "illegal cycle in type declaration")
+ goto Error
+ }
if len(e.Elts) == 0 {
break
}
@@ -1266,7 +1367,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
check.error(e, _InvalidTypeCycle, "illegal cycle in type declaration")
goto Error
}
- visited := make(map[interface{}][]Type, len(e.Elts))
+ visited := make(map[any][]Type, len(e.Elts))
for _, e := range e.Elts {
kv, _ := e.(*ast.KeyValueExpr)
if kv == nil {
@@ -1282,9 +1383,9 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
duplicate := false
// if the key is of interface type, the type is also significant when checking for duplicates
xkey := keyVal(x.val)
- if asInterface(utyp.key) != nil {
+ if IsInterface(utyp.key) {
for _, vtyp := range visited[xkey] {
- if check.identical(vtyp, x.typ) {
+ if Identical(vtyp, x.typ) {
duplicate = true
break
}
@@ -1326,16 +1427,17 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
x.typ = typ
case *ast.ParenExpr:
- kind := check.rawExpr(x, e.X, nil)
+ kind := check.rawExpr(x, e.X, nil, false)
x.expr = e
return kind
case *ast.SelectorExpr:
check.selector(x, e)
- case *ast.IndexExpr:
- if check.indexExpr(x, e) {
- check.funcInst(x, e)
+ case *ast.IndexExpr, *ast.IndexListExpr:
+ ix := typeparams.UnpackIndexExpr(e)
+ if check.indexExpr(x, ix) {
+ check.funcInst(x, ix)
}
if x.mode == invalid {
goto Error
@@ -1352,12 +1454,16 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
if x.mode == invalid {
goto Error
}
+ // TODO(gri) we may want to permit type assertions on type parameter values at some point
+ if isTypeParam(x.typ) {
+ check.invalidOp(x, _InvalidAssert, "cannot use type assertion on type parameter value %s", x)
+ goto Error
+ }
xtyp, _ := under(x.typ).(*Interface)
if xtyp == nil {
check.invalidOp(x, _InvalidAssert, "%s is not an interface", x)
goto Error
}
- check.ordinaryType(x, xtyp)
// x.(type) expressions are handled explicitly in type switches
if e.Type == nil {
// Don't use invalidAST because this can occur in the AST produced by
@@ -1377,20 +1483,31 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
return check.callExpr(x, e)
case *ast.StarExpr:
- check.exprOrType(x, e.X)
+ check.exprOrType(x, e.X, false)
switch x.mode {
case invalid:
goto Error
case typexpr:
x.typ = &Pointer{base: x.typ}
default:
- if typ := asPointer(x.typ); typ != nil {
- x.mode = variable
- x.typ = typ.base
- } else {
- check.invalidOp(x, _InvalidIndirection, "cannot indirect %s", x)
+ var base Type
+ if !underIs(x.typ, func(u Type) bool {
+ p, _ := u.(*Pointer)
+ if p == nil {
+ check.invalidOp(x, _InvalidIndirection, "cannot indirect %s", x)
+ return false
+ }
+ if base != nil && !Identical(p.base, base) {
+ check.invalidOp(x, _InvalidIndirection, "pointers of %s must have identical base types", x)
+ return false
+ }
+ base = p.base
+ return true
+ }) {
goto Error
}
+ x.mode = variable
+ x.typ = base
}
case *ast.UnaryExpr:
@@ -1425,12 +1542,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
// types, which are comparatively rare.
default:
- if typeparams.IsListExpr(e) {
- // catch-all for unexpected expression lists
- check.errorf(e, _Todo, "unexpected list of expressions")
- } else {
- panic(fmt.Sprintf("%s: unknown expression type %T", check.fset.Position(e.Pos()), e))
- }
+ panic(fmt.Sprintf("%s: unknown expression type %T", check.fset.Position(e.Pos()), e))
}
// everything went well
@@ -1443,7 +1555,7 @@ Error:
return statement // avoid follow-up errors
}
-func keyVal(x constant.Value) interface{} {
+func keyVal(x constant.Value) any {
switch x.Kind() {
case constant.Bool:
return constant.BoolVal(x)
@@ -1475,7 +1587,7 @@ func (check *Checker) typeAssertion(at positioner, x *operand, xtyp *Interface,
}
var msg string
if wrongType != nil {
- if check.identical(method.typ, wrongType.typ) {
+ if Identical(method.typ, wrongType.typ) {
msg = fmt.Sprintf("missing method %s (%s has pointer receiver)", method.name, method.name)
} else {
msg = fmt.Sprintf("wrong type for method %s (have %s, want %s)", method.name, wrongType.typ, method.typ)
@@ -1491,14 +1603,14 @@ func (check *Checker) typeAssertion(at positioner, x *operand, xtyp *Interface,
// If an error occurred, x.mode is set to invalid.
//
func (check *Checker) expr(x *operand, e ast.Expr) {
- check.rawExpr(x, e, nil)
+ check.rawExpr(x, e, nil, false)
check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
check.singleValue(x)
}
// multiExpr is like expr but the result may also be a multi-value.
func (check *Checker) multiExpr(x *operand, e ast.Expr) {
- check.rawExpr(x, e, nil)
+ check.rawExpr(x, e, nil, false)
check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
}
@@ -1508,16 +1620,18 @@ func (check *Checker) multiExpr(x *operand, e ast.Expr) {
//
func (check *Checker) exprWithHint(x *operand, e ast.Expr, hint Type) {
assert(hint != nil)
- check.rawExpr(x, e, hint)
+ check.rawExpr(x, e, hint, false)
check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
check.singleValue(x)
}
// exprOrType typechecks expression or type e and initializes x with the expression value or type.
+// If allowGeneric is set, the operand type may be an uninstantiated parameterized type or function
+// value.
// If an error occurred, x.mode is set to invalid.
//
-func (check *Checker) exprOrType(x *operand, e ast.Expr) {
- check.rawExpr(x, e, nil)
+func (check *Checker) exprOrType(x *operand, e ast.Expr, allowGeneric bool) {
+ check.rawExpr(x, e, nil, allowGeneric)
check.exclude(x, 1<<novalue)
check.singleValue(x)
}
@@ -1556,7 +1670,11 @@ func (check *Checker) singleValue(x *operand) {
// tuple types are never named - no need for underlying type below
if t, ok := x.typ.(*Tuple); ok {
assert(t.Len() != 1)
- check.errorf(x, _TooManyValues, "%d-valued %s where single value is expected", t.Len(), x)
+ if compilerErrorMessages {
+ check.errorf(x, _TooManyValues, "multiple-value %s in single-value context", x)
+ } else {
+ check.errorf(x, _TooManyValues, "%d-valued %s where single value is expected", t.Len(), x)
+ }
x.mode = invalid
}
}
diff --git a/libgo/go/go/types/exprstring.go b/libgo/go/go/types/exprstring.go
index f05e642..544cd84 100644
--- a/libgo/go/go/types/exprstring.go
+++ b/libgo/go/go/types/exprstring.go
@@ -67,16 +67,11 @@ func WriteExpr(buf *bytes.Buffer, x ast.Expr) {
buf.WriteByte('.')
buf.WriteString(x.Sel.Name)
- case *ast.IndexExpr:
- WriteExpr(buf, x.X)
+ case *ast.IndexExpr, *ast.IndexListExpr:
+ ix := typeparams.UnpackIndexExpr(x)
+ WriteExpr(buf, ix.X)
buf.WriteByte('[')
- exprs := typeparams.UnpackExpr(x.Index)
- for i, e := range exprs {
- if i > 0 {
- buf.WriteString(", ")
- }
- WriteExpr(buf, e)
- }
+ writeExprList(buf, ix.Indices)
buf.WriteByte(']')
case *ast.SliceExpr:
@@ -145,29 +140,8 @@ func WriteExpr(buf *bytes.Buffer, x ast.Expr) {
writeSigExpr(buf, x)
case *ast.InterfaceType:
- // separate type list types from method list
- // TODO(gri) we can get rid of this extra code if writeExprList does the separation
- var types []ast.Expr
- var methods []*ast.Field
- for _, f := range x.Methods.List {
- if len(f.Names) > 1 && f.Names[0].Name == "type" {
- // type list type
- types = append(types, f.Type)
- } else {
- // method or embedded interface
- methods = append(methods, f)
- }
- }
-
buf.WriteString("interface{")
- writeFieldList(buf, methods, "; ", true)
- if len(types) > 0 {
- if len(methods) > 0 {
- buf.WriteString("; ")
- }
- buf.WriteString("type ")
- writeExprList(buf, types)
- }
+ writeFieldList(buf, x.Methods.List, "; ", true)
buf.WriteByte('}')
case *ast.MapType:
diff --git a/libgo/go/go/types/exprstring_test.go b/libgo/go/go/types/exprstring_test.go
index 5110288..27cd532 100644
--- a/libgo/go/go/types/exprstring_test.go
+++ b/libgo/go/go/types/exprstring_test.go
@@ -27,6 +27,33 @@ var testExprs = []testEntry{
{"func(x int) complex128 {}", "(func(x int) complex128 literal)"},
{"[]int{1, 2, 3}", "([]int literal)"},
+ // type expressions
+ dup("[1 << 10]byte"),
+ dup("[]int"),
+ dup("*int"),
+ dup("struct{x int}"),
+ dup("func()"),
+ dup("func(int, float32) string"),
+ dup("interface{m()}"),
+ dup("interface{m() string; n(x int)}"),
+ dup("interface{~int}"),
+
+ dup("map[string]int"),
+ dup("chan E"),
+ dup("<-chan E"),
+ dup("chan<- E"),
+
+ // new interfaces
+ dup("interface{int}"),
+ dup("interface{~int}"),
+ dup("interface{~int}"),
+ dup("interface{int | string}"),
+ dup("interface{~int | ~string; float64; m()}"),
+
+ // See above.
+ // dup("interface{type a, b, c; ~int | ~string; float64; m()}"),
+ dup("interface{~T[int, string] | string}"),
+
// non-type expressions
dup("(x)"),
dup("x.f"),
diff --git a/libgo/go/go/types/gotype.go b/libgo/go/go/types/gotype.go
index ca1d42c..5d27bb7 100644
--- a/libgo/go/go/types/gotype.go
+++ b/libgo/go/go/types/gotype.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build ignore
-// +build ignore
// Build this command explicitly: go build gotype.go
@@ -180,7 +179,7 @@ func report(err error) {
}
// parse may be called concurrently
-func parse(filename string, src interface{}) (*ast.File, error) {
+func parse(filename string, src any) (*ast.File, error) {
if *verbose {
fmt.Println(filename)
}
diff --git a/libgo/go/go/types/hilbert_test.go b/libgo/go/go/types/hilbert_test.go
index 791357a..732e269 100644
--- a/libgo/go/go/types/hilbert_test.go
+++ b/libgo/go/go/types/hilbert_test.go
@@ -86,7 +86,7 @@ type gen struct {
bytes.Buffer
}
-func (g *gen) p(format string, args ...interface{}) {
+func (g *gen) p(format string, args ...any) {
fmt.Fprintf(&g.Buffer, format, args...)
}
diff --git a/libgo/go/go/types/index.go b/libgo/go/go/types/index.go
index 2ba3475..db4732c 100644
--- a/libgo/go/go/types/index.go
+++ b/libgo/go/go/types/index.go
@@ -15,33 +15,42 @@ import (
// If e is a valid function instantiation, indexExpr returns true.
// In that case x represents the uninstantiated function value and
// it is the caller's responsibility to instantiate the function.
-func (check *Checker) indexExpr(x *operand, e *ast.IndexExpr) (isFuncInst bool) {
- check.exprOrType(x, e.X)
+func (check *Checker) indexExpr(x *operand, e *typeparams.IndexExpr) (isFuncInst bool) {
+ check.exprOrType(x, e.X, true)
+ // x may be generic
switch x.mode {
case invalid:
- check.use(typeparams.UnpackExpr(e.Index)...)
+ check.use(e.Indices...)
return false
case typexpr:
// type instantiation
x.mode = invalid
- x.typ = check.varType(e)
+ // TODO(gri) here we re-evaluate e.X - try to avoid this
+ x.typ = check.varType(e.Orig)
if x.typ != Typ[Invalid] {
x.mode = typexpr
}
return false
case value:
- if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 {
+ if sig, _ := under(x.typ).(*Signature); sig != nil && sig.TypeParams().Len() > 0 {
// function instantiation
return true
}
}
+ // x should not be generic at this point, but be safe and check
+ check.nonGeneric(x)
+ if x.mode == invalid {
+ return false
+ }
+
+ // ordinary index expression
valid := false
length := int64(-1) // valid if >= 0
- switch typ := optype(x.typ).(type) {
+ switch typ := under(x.typ).(type) {
case *Basic:
if isString(typ) {
valid = true
@@ -64,7 +73,7 @@ func (check *Checker) indexExpr(x *operand, e *ast.IndexExpr) (isFuncInst bool)
x.typ = typ.elem
case *Pointer:
- if typ := asArray(typ.base); typ != nil {
+ if typ, _ := under(typ.base).(*Array); typ != nil {
valid = true
length = typ.len
x.mode = variable
@@ -80,7 +89,7 @@ func (check *Checker) indexExpr(x *operand, e *ast.IndexExpr) (isFuncInst bool)
index := check.singleIndex(e)
if index == nil {
x.mode = invalid
- return
+ return false
}
var key operand
check.expr(&key, index)
@@ -88,102 +97,101 @@ func (check *Checker) indexExpr(x *operand, e *ast.IndexExpr) (isFuncInst bool)
// ok to continue even if indexing failed - map element type is known
x.mode = mapindex
x.typ = typ.elem
- x.expr = e
- return
+ x.expr = e.Orig
+ return false
- case *_Sum:
- // A sum type can be indexed if all of the sum's types
- // support indexing and have the same index and element
- // type. Special rules apply for maps in the sum type.
- var tkey, telem Type // key is for map types only
- nmaps := 0 // number of map types in sum type
- if typ.is(func(t Type) bool {
- var e Type
- switch t := under(t).(type) {
+ case *Interface:
+ if !isTypeParam(x.typ) {
+ break
+ }
+ // TODO(gri) report detailed failure cause for better error messages
+ var key, elem Type // key != nil: we must have all maps
+ mode := variable // non-maps result mode
+ // TODO(gri) factor out closure and use it for non-typeparam cases as well
+ if typ.typeSet().underIs(func(u Type) bool {
+ l := int64(-1) // valid if >= 0
+ var k, e Type // k is only set for maps
+ switch t := u.(type) {
case *Basic:
if isString(t) {
e = universeByte
+ mode = value
}
case *Array:
+ l = t.len
e = t.elem
+ if x.mode != variable {
+ mode = value
+ }
case *Pointer:
- if t := asArray(t.base); t != nil {
+ if t, _ := under(t.base).(*Array); t != nil {
+ l = t.len
e = t.elem
}
case *Slice:
e = t.elem
case *Map:
- // If there are multiple maps in the sum type,
- // they must have identical key types.
- // TODO(gri) We may be able to relax this rule
- // but it becomes complicated very quickly.
- if tkey != nil && !Identical(t.key, tkey) {
- return false
- }
- tkey = t.key
+ k = t.key
e = t.elem
- nmaps++
- case *_TypeParam:
- check.errorf(x, 0, "type of %s contains a type parameter - cannot index (implementation restriction)", x)
- case *instance:
- panic("unimplemented")
}
- if e == nil || telem != nil && !Identical(e, telem) {
+ if e == nil {
return false
}
- telem = e
+ if elem == nil {
+ // first type
+ length = l
+ key, elem = k, e
+ return true
+ }
+ // all map keys must be identical (incl. all nil)
+ // (that is, we cannot mix maps with other types)
+ if !Identical(key, k) {
+ return false
+ }
+ // all element types must be identical
+ if !Identical(elem, e) {
+ return false
+ }
+ // track the minimal length for arrays, if any
+ if l >= 0 && l < length {
+ length = l
+ }
return true
}) {
- // If there are maps, the index expression must be assignable
- // to the map key type (as for simple map index expressions).
- if nmaps > 0 {
+ // For maps, the index expression must be assignable to the map key type.
+ if key != nil {
index := check.singleIndex(e)
if index == nil {
x.mode = invalid
- return
+ return false
}
- var key operand
- check.expr(&key, index)
- check.assignment(&key, tkey, "map index")
+ var k operand
+ check.expr(&k, index)
+ check.assignment(&k, key, "map index")
// ok to continue even if indexing failed - map element type is known
-
- // If there are only maps, we are done.
- if nmaps == len(typ.types) {
- x.mode = mapindex
- x.typ = telem
- x.expr = e
- return
- }
-
- // Otherwise we have mix of maps and other types. For
- // now we require that the map key be an integer type.
- // TODO(gri) This is probably not good enough.
- valid = isInteger(tkey)
- // avoid 2nd indexing error if indexing failed above
- if !valid && key.mode == invalid {
- x.mode = invalid
- return
- }
- x.mode = value // map index expressions are not addressable
- } else {
- // no maps
- valid = true
- x.mode = variable
+ x.mode = mapindex
+ x.typ = elem
+ x.expr = e
+ return false
}
- x.typ = telem
+
+ // no maps
+ valid = true
+ x.mode = mode
+ x.typ = elem
}
}
if !valid {
check.invalidOp(x, _NonIndexableOperand, "cannot index %s", x)
x.mode = invalid
- return
+ return false
}
index := check.singleIndex(e)
if index == nil {
x.mode = invalid
- return
+ return false
}
// In pathological (invalid) cases (e.g.: type T1 [][[]T1{}[0][0]]T0)
@@ -206,11 +214,20 @@ func (check *Checker) sliceExpr(x *operand, e *ast.SliceExpr) {
valid := false
length := int64(-1) // valid if >= 0
- switch typ := optype(x.typ).(type) {
+ switch u := structuralString(x.typ).(type) {
+ case nil:
+ check.invalidOp(x, _NonSliceableOperand, "cannot slice %s: %s has no structural type", x, x.typ)
+ x.mode = invalid
+ return
+
case *Basic:
- if isString(typ) {
+ if isString(u) {
if e.Slice3 {
- check.invalidOp(x, _InvalidSliceExpr, "3-index slice of string")
+ at := e.Max
+ if at == nil {
+ at = e // e.Index[2] should be present but be careful
+ }
+ check.invalidOp(at, _InvalidSliceExpr, "3-index slice of string")
x.mode = invalid
return
}
@@ -220,36 +237,31 @@ func (check *Checker) sliceExpr(x *operand, e *ast.SliceExpr) {
}
// spec: "For untyped string operands the result
// is a non-constant value of type string."
- if typ.kind == UntypedString {
+ if isUntyped(x.typ) {
x.typ = Typ[String]
}
}
case *Array:
valid = true
- length = typ.len
+ length = u.len
if x.mode != variable {
check.invalidOp(x, _NonSliceableOperand, "cannot slice %s (value not addressable)", x)
x.mode = invalid
return
}
- x.typ = &Slice{elem: typ.elem}
+ x.typ = &Slice{elem: u.elem}
case *Pointer:
- if typ := asArray(typ.base); typ != nil {
+ if u, _ := under(u.base).(*Array); u != nil {
valid = true
- length = typ.len
- x.typ = &Slice{elem: typ.elem}
+ length = u.len
+ x.typ = &Slice{elem: u.elem}
}
case *Slice:
valid = true
// x.typ doesn't change
-
- case *_Sum, *_TypeParam:
- check.errorf(x, 0, "generic slice expressions not yet implemented")
- x.mode = invalid
- return
}
if !valid {
@@ -298,9 +310,13 @@ func (check *Checker) sliceExpr(x *operand, e *ast.SliceExpr) {
L:
for i, x := range ind[:len(ind)-1] {
if x > 0 {
- for _, y := range ind[i+1:] {
- if y >= 0 && x > y {
- check.errorf(inNode(e, e.Rbrack), _SwappedSliceIndices, "swapped slice indices: %d > %d", x, y)
+ for j, y := range ind[i+1:] {
+ if y >= 0 && y < x {
+ // The value y corresponds to the expression e.Index[i+1+j].
+ // Because y >= 0, it must have been set from the expression
+ // when checking indices and thus e.Index[i+1+j] is not nil.
+ at := []ast.Expr{e.Low, e.High, e.Max}[i+1+j]
+ check.errorf(at, _SwappedSliceIndices, "invalid slice indices: %d < %d", y, x)
break L // only report one error, ok to continue
}
}
@@ -311,23 +327,16 @@ L:
// singleIndex returns the (single) index from the index expression e.
// If the index is missing, or if there are multiple indices, an error
// is reported and the result is nil.
-func (check *Checker) singleIndex(e *ast.IndexExpr) ast.Expr {
- index := e.Index
- if index == nil {
- check.invalidAST(e, "missing index for %s", e)
- return nil
- }
-
- indexes := typeparams.UnpackExpr(index)
- if len(indexes) == 0 {
- check.invalidAST(index, "index expression %v with 0 indices", index)
+func (check *Checker) singleIndex(expr *typeparams.IndexExpr) ast.Expr {
+ if len(expr.Indices) == 0 {
+ check.invalidAST(expr.Orig, "index expression %v with 0 indices", expr)
return nil
}
- if len(indexes) > 1 {
+ if len(expr.Indices) > 1 {
// TODO(rFindley) should this get a distinct error code?
- check.invalidOp(indexes[1], _InvalidIndex, "more than one index")
+ check.invalidOp(expr.Indices[1], _InvalidIndex, "more than one index")
}
- return indexes[0]
+ return expr.Indices[0]
}
// index checks an index expression for validity.
@@ -375,7 +384,7 @@ func (check *Checker) isValidIndex(x *operand, code errorCode, what string, allo
}
// spec: "the index x must be of integer type or an untyped constant"
- if !isInteger(x.typ) {
+ if !allInteger(x.typ) {
check.invalidArg(x, code, "%s %s must be integer", what, x)
return false
}
diff --git a/libgo/go/go/types/infer.go b/libgo/go/go/types/infer.go
index 5d49351..2678da3 100644
--- a/libgo/go/go/types/infer.go
+++ b/libgo/go/go/types/infer.go
@@ -8,6 +8,7 @@
package types
import (
+ "fmt"
"go/token"
"strings"
)
@@ -17,17 +18,18 @@ import (
// function arguments args, if any. There must be at least one type parameter, no more type arguments
// than type parameters, and params and args must match in number (incl. zero).
// If successful, infer returns the complete list of type arguments, one for each type parameter.
-// Otherwise the result is nil and appropriate errors will be reported unless report is set to false.
+// Otherwise the result is nil and appropriate errors will be reported.
//
-// Inference proceeds in 3 steps:
+// Inference proceeds as follows:
//
-// 1) Start with given type arguments.
-// 2) Infer type arguments from typed function arguments.
-// 3) Infer type arguments from untyped function arguments.
+// Starting with given type arguments
+// 1) apply FTI (function type inference) with typed arguments,
+// 2) apply CTI (constraint type inference),
+// 3) apply FTI with untyped function arguments,
+// 4) apply CTI.
//
-// Constraint type inference is used after each step to expand the set of type arguments.
-//
-func (check *Checker) infer(posn positioner, tparams []*TypeName, targs []Type, params *Tuple, args []*operand, report bool) (result []Type) {
+// The process stops as soon as all type arguments are known or an error occurs.
+func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type, params *Tuple, args []*operand) (result []Type) {
if debug {
defer func() {
assert(result == nil || len(result) == len(tparams))
@@ -45,34 +47,69 @@ func (check *Checker) infer(posn positioner, tparams []*TypeName, targs []Type,
// Function parameters and arguments must match in number.
assert(params.Len() == len(args))
- // --- 0 ---
// If we already have all type arguments, we're done.
if len(targs) == n {
return targs
}
// len(targs) < n
- // --- 1 ---
- // Explicitly provided type arguments take precedence over any inferred types;
- // and types inferred via constraint type inference take precedence over types
- // inferred from function arguments.
- // If we have type arguments, see how far we get with constraint type inference.
- if len(targs) > 0 {
- var index int
- targs, index = check.inferB(tparams, targs, report)
- if targs == nil || index < 0 {
- return targs
+ // If we have more than 2 arguments, we may have arguments with named and unnamed types.
+ // If that is the case, permutate params and args such that the arguments with named
+ // types are first in the list. This doesn't affect type inference if all types are taken
+ // as is. But when we have inexact unification enabled (as is the case for function type
+ // inference), when a named type is unified with an unnamed type, unification proceeds
+ // with the underlying type of the named type because otherwise unification would fail
+ // right away. This leads to an asymmetry in type inference: in cases where arguments of
+ // named and unnamed types are passed to parameters with identical type, different types
+ // (named vs underlying) may be inferred depending on the order of the arguments.
+ // By ensuring that named types are seen first, order dependence is avoided and unification
+ // succeeds where it can.
+ //
+ // This code is disabled for now pending decision whether we want to address cases like
+ // these and make the spec on type inference more complicated (see issue #43056).
+ const enableArgSorting = false
+ if m := len(args); m >= 2 && enableArgSorting {
+ // Determine indices of arguments with named and unnamed types.
+ var named, unnamed []int
+ for i, arg := range args {
+ if hasName(arg.typ) {
+ named = append(named, i)
+ } else {
+ unnamed = append(unnamed, i)
+ }
+ }
+
+ // If we have named and unnamed types, move the arguments with
+ // named types first. Update the parameter list accordingly.
+ // Make copies so as not to clobber the incoming slices.
+ if len(named) != 0 && len(unnamed) != 0 {
+ params2 := make([]*Var, m)
+ args2 := make([]*operand, m)
+ i := 0
+ for _, j := range named {
+ params2[i] = params.At(j)
+ args2[i] = args[j]
+ i++
+ }
+ for _, j := range unnamed {
+ params2[i] = params.At(j)
+ args2[i] = args[j]
+ i++
+ }
+ params = NewTuple(params2...)
+ args = args2
}
}
- // Continue with the type arguments we have now. Avoid matching generic
+ // --- 1 ---
+ // Continue with the type arguments we have. Avoid matching generic
// parameters that already have type arguments against function arguments:
// It may fail because matching uses type identity while parameter passing
// uses assignment rules. Instantiate the parameter list with the type
// arguments we have, and continue with that parameter list.
- // First, make sure we have a "full" list of type arguments, so of which
- // may be nil (unknown).
+ // First, make sure we have a "full" list of type arguments, some of which
+ // may be nil (unknown). Make a copy so as to not clobber the incoming slice.
if len(targs) < n {
targs2 := make([]Type, n)
copy(targs2, targs)
@@ -82,18 +119,17 @@ func (check *Checker) infer(posn positioner, tparams []*TypeName, targs []Type,
// Substitute type arguments for their respective type parameters in params,
// if any. Note that nil targs entries are ignored by check.subst.
- // TODO(gri) Can we avoid this (we're setting known type argumemts below,
+ // TODO(gri) Can we avoid this (we're setting known type arguments below,
// but that doesn't impact the isParameterized check for now).
if params.Len() > 0 {
smap := makeSubstMap(tparams, targs)
- params = check.subst(token.NoPos, params, smap).(*Tuple)
+ params = check.subst(token.NoPos, params, smap, nil).(*Tuple)
}
- // --- 2 ---
// Unify parameter and argument types for generic parameters with typed arguments
// and collect the indices of generic parameters with untyped arguments.
// Terminology: generic parameter = function parameter with a type-parameterized type
- u := newUnifier(check, false)
+ u := newUnifier(false)
u.x.init(tparams)
// Set the type arguments which we know already.
@@ -104,9 +140,6 @@ func (check *Checker) infer(posn positioner, tparams []*TypeName, targs []Type,
}
errorf := func(kind string, tpar, targ Type, arg *operand) {
- if !report {
- return
- }
// provide a better error message if we can
targs, index := u.x.types()
if index == 0 {
@@ -121,17 +154,21 @@ func (check *Checker) infer(posn positioner, tparams []*TypeName, targs []Type,
}
}
if allFailed {
- check.errorf(arg, _Todo, "%s %s of %s does not match %s (cannot infer %s)", kind, targ, arg.expr, tpar, typeNamesString(tparams))
+ check.errorf(arg, _CannotInferTypeArgs, "%s %s of %s does not match %s (cannot infer %s)", kind, targ, arg.expr, tpar, typeParamsString(tparams))
return
}
}
smap := makeSubstMap(tparams, targs)
// TODO(rFindley): pass a positioner here, rather than arg.Pos().
- inferred := check.subst(arg.Pos(), tpar, smap)
+ inferred := check.subst(arg.Pos(), tpar, smap, nil)
+ // _CannotInferTypeArgs indicates a failure of inference, though the actual
+ // error may be better attributed to a user-provided type argument (hence
+ // _InvalidTypeArg). We can't differentiate these cases, so fall back on
+ // the more general _CannotInferTypeArgs.
if inferred != tpar {
- check.errorf(arg, _Todo, "%s %s of %s does not match inferred type %s for %s", kind, targ, arg.expr, inferred, tpar)
+ check.errorf(arg, _CannotInferTypeArgs, "%s %s of %s does not match inferred type %s for %s", kind, targ, arg.expr, inferred, tpar)
} else {
- check.errorf(arg, 0, "%s %s of %s does not match %s", kind, targ, arg.expr, tpar)
+ check.errorf(arg, _CannotInferTypeArgs, "%s %s of %s does not match %s", kind, targ, arg.expr, tpar)
}
}
@@ -170,10 +207,11 @@ func (check *Checker) infer(posn positioner, tparams []*TypeName, targs []Type,
return targs
}
+ // --- 2 ---
// See how far we get with constraint type inference.
// Note that even if we don't have any type arguments, constraint type inference
// may produce results for constraints that explicitly specify a type.
- targs, index = check.inferB(tparams, targs, report)
+ targs, index = check.inferB(posn, tparams, targs)
if targs == nil || index < 0 {
return targs
}
@@ -189,7 +227,7 @@ func (check *Checker) infer(posn positioner, tparams []*TypeName, targs []Type,
// only parameter type it can possibly match against is a *TypeParam.
// Thus, only consider untyped arguments for generic parameters that
// are not of composite types and which don't have a type inferred yet.
- if tpar, _ := par.typ.(*_TypeParam); tpar != nil && targs[tpar.index] == nil {
+ if tpar, _ := par.typ.(*TypeParam); tpar != nil && targs[tpar.index] == nil {
arg := args[i]
targ := Default(arg.typ)
// The default type for an untyped nil is untyped nil. We must not
@@ -208,8 +246,9 @@ func (check *Checker) infer(posn positioner, tparams []*TypeName, targs []Type,
return targs
}
+ // --- 4 ---
// Again, follow up with constraint type inference.
- targs, index = check.inferB(tparams, targs, report)
+ targs, index = check.inferB(posn, tparams, targs)
if targs == nil || index < 0 {
return targs
}
@@ -217,24 +256,22 @@ func (check *Checker) infer(posn positioner, tparams []*TypeName, targs []Type,
// At least one type argument couldn't be inferred.
assert(index >= 0 && targs[index] == nil)
tpar := tparams[index]
- if report {
- check.errorf(posn, _Todo, "cannot infer %s (%v) (%v)", tpar.name, tpar.pos, targs)
- }
+ check.errorf(posn, _CannotInferTypeArgs, "cannot infer %s (%v)", tpar.obj.name, tpar.obj.pos)
return nil
}
-// typeNamesString produces a string containing all the
-// type names in list suitable for human consumption.
-func typeNamesString(list []*TypeName) string {
+// typeParamsString produces a string containing all the type parameter names
+// in list suitable for human consumption.
+func typeParamsString(list []*TypeParam) string {
// common cases
n := len(list)
switch n {
case 0:
return ""
case 1:
- return list[0].name
+ return list[0].obj.name
case 2:
- return list[0].name + " and " + list[1].name
+ return list[0].obj.name + " and " + list[1].obj.name
}
// general case (n > 2)
@@ -243,15 +280,15 @@ func typeNamesString(list []*TypeName) string {
if i > 0 {
b.WriteString(", ")
}
- b.WriteString(tname.name)
+ b.WriteString(tname.obj.name)
}
b.WriteString(", and ")
- b.WriteString(list[n-1].name)
+ b.WriteString(list[n-1].obj.name)
return b.String()
}
// IsParameterized reports whether typ contains any of the type parameters of tparams.
-func isParameterized(tparams []*TypeName, typ Type) bool {
+func isParameterized(tparams []*TypeParam, typ Type) bool {
w := tpWalker{
seen: make(map[Type]bool),
tparams: tparams,
@@ -261,7 +298,7 @@ func isParameterized(tparams []*TypeName, typ Type) bool {
type tpWalker struct {
seen map[Type]bool
- tparams []*TypeName
+ tparams []*TypeParam
}
func (w *tpWalker) isParameterized(typ Type) (res bool) {
@@ -302,9 +339,6 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
}
}
- case *_Sum:
- return w.isParameterizedList(t.types)
-
case *Signature:
// t.tparams may not be nil if we are looking at a signature
// of a generic function type (or an interface method) that is
@@ -316,24 +350,15 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
return w.isParameterized(t.params) || w.isParameterized(t.results)
case *Interface:
- if t.allMethods != nil {
- // TODO(rFindley) at some point we should enforce completeness here
- for _, m := range t.allMethods {
- if w.isParameterized(m.typ) {
- return true
- }
+ tset := t.typeSet()
+ for _, m := range tset.methods {
+ if w.isParameterized(m.typ) {
+ return true
}
- return w.isParameterizedList(unpackType(t.allTypes))
}
-
- return t.iterate(func(t *Interface) bool {
- for _, m := range t.methods {
- if w.isParameterized(m.typ) {
- return true
- }
- }
- return w.isParameterizedList(unpackType(t.types))
- }, nil)
+ return tset.is(func(t *term) bool {
+ return t != nil && w.isParameterized(t.typ)
+ })
case *Map:
return w.isParameterized(t.key) || w.isParameterized(t.elem)
@@ -342,14 +367,11 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
return w.isParameterized(t.elem)
case *Named:
- return w.isParameterizedList(t.targs)
+ return w.isParameterizedTypeList(t.targs.list())
- case *_TypeParam:
+ case *TypeParam:
// t must be one of w.tparams
- return t.index < len(w.tparams) && w.tparams[t.index].typ == t
-
- case *instance:
- return w.isParameterizedList(t.targs)
+ return tparamIndex(w.tparams, t) >= 0
default:
unreachable()
@@ -358,7 +380,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
return false
}
-func (w *tpWalker) isParameterizedList(list []Type) bool {
+func (w *tpWalker) isParameterizedTypeList(list []Type) bool {
for _, t := range list {
if w.isParameterized(t) {
return true
@@ -375,12 +397,12 @@ func (w *tpWalker) isParameterizedList(list []Type) bool {
// first type argument in that list that couldn't be inferred (and thus is nil). If all
// type arguments were inferred successfully, index is < 0. The number of type arguments
// provided may be less than the number of type parameters, but there must be at least one.
-func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (types []Type, index int) {
+func (check *Checker) inferB(posn positioner, tparams []*TypeParam, targs []Type) (types []Type, index int) {
assert(len(tparams) >= len(targs) && len(targs) > 0)
- // Setup bidirectional unification between those structural bounds
+ // Setup bidirectional unification between constraints
// and the corresponding type arguments (which may be nil!).
- u := newUnifier(check, false)
+ u := newUnifier(false)
u.x.init(tparams)
u.y = u.x // type parameters between LHS and RHS of unification are identical
@@ -391,24 +413,28 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty
}
}
- // Unify type parameters with their structural constraints, if any.
+ // If a constraint has a structural type, unify the corresponding type parameter with it.
for _, tpar := range tparams {
- typ := tpar.typ.(*_TypeParam)
- sbound := check.structuralType(typ.bound)
+ sbound := structuralType(tpar)
if sbound != nil {
- if !u.unify(typ, sbound) {
- if report {
- check.errorf(tpar, _Todo, "%s does not match %s", tpar, sbound)
- }
+ // If the structural type is the underlying type of a single
+ // defined type in the constraint, use that defined type instead.
+ if named, _ := tpar.singleType().(*Named); named != nil {
+ sbound = named
+ }
+ if !u.unify(tpar, sbound) {
+ // TODO(gri) improve error message by providing the type arguments
+ // which we know already
+ check.errorf(posn, _InvalidTypeArg, "%s does not match %s", tpar, sbound)
return nil, 0
}
}
}
// u.x.types() now contains the incoming type arguments plus any additional type
- // arguments for which there were structural constraints. The newly inferred non-
- // nil entries may still contain references to other type parameters. For instance,
- // for [A any, B interface{type []C}, C interface{type *A}], if A == int
+ // arguments which were inferred from structural types. The newly inferred non-
+ // nil entries may still contain references to other type parameters.
+ // For instance, for [A any, B interface{ []C }, C interface{ *A }], if A == int
// was given, unification produced the type list [int, []C, *A]. We eliminate the
// remaining type parameters by substituting the type parameters in this type list
// until nothing changes anymore.
@@ -419,6 +445,34 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty
}
}
+ // The data structure of each (provided or inferred) type represents a graph, where
+ // each node corresponds to a type and each (directed) vertice points to a component
+ // type. The substitution process described above repeatedly replaces type parameter
+ // nodes in these graphs with the graphs of the types the type parameters stand for,
+ // which creates a new (possibly bigger) graph for each type.
+ // The substitution process will not stop if the replacement graph for a type parameter
+ // also contains that type parameter.
+ // For instance, for [A interface{ *A }], without any type argument provided for A,
+ // unification produces the type list [*A]. Substituting A in *A with the value for
+ // A will lead to infinite expansion by producing [**A], [****A], [********A], etc.,
+ // because the graph A -> *A has a cycle through A.
+ // Generally, cycles may occur across multiple type parameters and inferred types
+ // (for instance, consider [P interface{ *Q }, Q interface{ func(P) }]).
+ // We eliminate cycles by walking the graphs for all type parameters. If a cycle
+ // through a type parameter is detected, cycleFinder nils out the respectice type
+ // which kills the cycle; this also means that the respective type could not be
+ // inferred.
+ //
+ // TODO(gri) If useful, we could report the respective cycle as an error. We don't
+ // do this now because type inference will fail anyway, and furthermore,
+ // constraints with cycles of this kind cannot currently be satisfied by
+ // any user-suplied type. But should that change, reporting an error
+ // would be wrong.
+ w := cycleFinder{tparams, types, make(map[Type]bool)}
+ for _, t := range tparams {
+ w.typ(t) // t != nil
+ }
+
// dirty tracks the indices of all types that may still contain type parameters.
// We know that nil type entries and entries corresponding to provided (non-nil)
// type arguments are clean, so exclude them from the start.
@@ -437,7 +491,7 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty
n := 0
for _, index := range dirty {
t0 := types[index]
- if t1 := check.subst(token.NoPos, t0, smap); t1 != t0 {
+ if t1 := check.subst(token.NoPos, t0, smap, nil); t1 != t0 {
types[index] = t1
dirty[n] = index
n++
@@ -468,15 +522,97 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty
return
}
-// structuralType returns the structural type of a constraint, if any.
-func (check *Checker) structuralType(constraint Type) Type {
- if iface, _ := under(constraint).(*Interface); iface != nil {
- check.completeInterface(token.NoPos, iface)
- types := unpackType(iface.allTypes)
- if len(types) == 1 {
- return types[0]
+type cycleFinder struct {
+ tparams []*TypeParam
+ types []Type
+ seen map[Type]bool
+}
+
+func (w *cycleFinder) typ(typ Type) {
+ if w.seen[typ] {
+ // We have seen typ before. If it is one of the type parameters
+ // in tparams, iterative substitution will lead to infinite expansion.
+ // Nil out the corresponding type which effectively kills the cycle.
+ if tpar, _ := typ.(*TypeParam); tpar != nil {
+ if i := tparamIndex(w.tparams, tpar); i >= 0 {
+ // cycle through tpar
+ w.types[i] = nil
+ }
+ }
+ // If we don't have one of our type parameters, the cycle is due
+ // to an ordinary recursive type and we can just stop walking it.
+ return
+ }
+ w.seen[typ] = true
+ defer delete(w.seen, typ)
+
+ switch t := typ.(type) {
+ case *Basic:
+ // nothing to do
+
+ case *Array:
+ w.typ(t.elem)
+
+ case *Slice:
+ w.typ(t.elem)
+
+ case *Struct:
+ w.varList(t.fields)
+
+ case *Pointer:
+ w.typ(t.base)
+
+ // case *Tuple:
+ // This case should not occur because tuples only appear
+ // in signatures where they are handled explicitly.
+
+ case *Signature:
+ // There are no "method types" so we should never see a recv.
+ assert(t.recv == nil)
+ if t.params != nil {
+ w.varList(t.params.vars)
+ }
+ if t.results != nil {
+ w.varList(t.results.vars)
+ }
+
+ case *Union:
+ for _, t := range t.terms {
+ w.typ(t.typ)
+ }
+
+ case *Interface:
+ for _, m := range t.methods {
+ w.typ(m.typ)
+ }
+ for _, t := range t.embeddeds {
+ w.typ(t)
+ }
+
+ case *Map:
+ w.typ(t.key)
+ w.typ(t.elem)
+
+ case *Chan:
+ w.typ(t.elem)
+
+ case *Named:
+ for _, tpar := range t.TypeArgs().list() {
+ w.typ(tpar)
+ }
+
+ case *TypeParam:
+ if i := tparamIndex(w.tparams, t); i >= 0 && w.types[i] != nil {
+ w.typ(w.types[i])
}
- return nil
+
+ default:
+ panic(fmt.Sprintf("unexpected %T", typ))
+ }
+}
+
+func (w *cycleFinder) varList(list []*Var) {
+ for _, v := range list {
+ w.typ(v.typ)
}
- return constraint
}
diff --git a/libgo/go/go/types/initorder.go b/libgo/go/go/types/initorder.go
index 77a739c..1118b58 100644
--- a/libgo/go/go/types/initorder.go
+++ b/libgo/go/go/types/initorder.go
@@ -7,6 +7,7 @@ package types
import (
"container/heap"
"fmt"
+ "sort"
)
// initOrder computes the Info.InitOrder for package variables.
@@ -184,6 +185,12 @@ type graphNode struct {
ndeps int // number of outstanding dependencies before this object can be initialized
}
+// cost returns the cost of removing this node, which involves copying each
+// predecessor to each successor (and vice-versa).
+func (n *graphNode) cost() int {
+ return len(n.pred) * len(n.succ)
+}
+
type nodeSet map[*graphNode]bool
func (s *nodeSet) add(p *graphNode) {
@@ -221,35 +228,48 @@ func dependencyGraph(objMap map[Object]*declInfo) []*graphNode {
}
}
+ var G, funcG []*graphNode // separate non-functions and functions
+ for _, n := range M {
+ if _, ok := n.obj.(*Func); ok {
+ funcG = append(funcG, n)
+ } else {
+ G = append(G, n)
+ }
+ }
+
// remove function nodes and collect remaining graph nodes in G
// (Mutually recursive functions may introduce cycles among themselves
// which are permitted. Yet such cycles may incorrectly inflate the dependency
// count for variables which in turn may not get scheduled for initialization
// in correct order.)
- var G []*graphNode
- for obj, n := range M {
- if _, ok := obj.(*Func); ok {
- // connect each predecessor p of n with each successor s
- // and drop the function node (don't collect it in G)
- for p := range n.pred {
- // ignore self-cycles
- if p != n {
- // Each successor s of n becomes a successor of p, and
- // each predecessor p of n becomes a predecessor of s.
- for s := range n.succ {
- // ignore self-cycles
- if s != n {
- p.succ.add(s)
- s.pred.add(p)
- delete(s.pred, n) // remove edge to n
- }
+ //
+ // Note that because we recursively copy predecessors and successors
+ // throughout the function graph, the cost of removing a function at
+ // position X is proportional to cost * (len(funcG)-X). Therefore, we should
+ // remove high-cost functions last.
+ sort.Slice(funcG, func(i, j int) bool {
+ return funcG[i].cost() < funcG[j].cost()
+ })
+ for _, n := range funcG {
+ // connect each predecessor p of n with each successor s
+ // and drop the function node (don't collect it in G)
+ for p := range n.pred {
+ // ignore self-cycles
+ if p != n {
+ // Each successor s of n becomes a successor of p, and
+ // each predecessor p of n becomes a predecessor of s.
+ for s := range n.succ {
+ // ignore self-cycles
+ if s != n {
+ p.succ.add(s)
+ s.pred.add(p)
}
- delete(p.succ, n) // remove edge to n
}
+ delete(p.succ, n) // remove edge to n
}
- } else {
- // collect non-function nodes
- G = append(G, n)
+ }
+ for s := range n.succ {
+ delete(s.pred, n) // remove edge to n
}
}
@@ -284,11 +304,11 @@ func (a nodeQueue) Less(i, j int) bool {
return x.ndeps < y.ndeps || x.ndeps == y.ndeps && x.obj.order() < y.obj.order()
}
-func (a *nodeQueue) Push(x interface{}) {
+func (a *nodeQueue) Push(x any) {
panic("unreachable")
}
-func (a *nodeQueue) Pop() interface{} {
+func (a *nodeQueue) Pop() any {
n := len(*a)
x := (*a)[n-1]
x.index = -1 // for safety
diff --git a/libgo/go/go/types/instantiate.go b/libgo/go/go/types/instantiate.go
new file mode 100644
index 0000000..09a841b
--- /dev/null
+++ b/libgo/go/go/types/instantiate.go
@@ -0,0 +1,275 @@
+// 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.
+
+// This file implements instantiation of generic types
+// through substitution of type parameters by type arguments.
+
+package types
+
+import (
+ "errors"
+ "fmt"
+ "go/token"
+)
+
+// Instantiate instantiates the type orig with the given type arguments targs.
+// orig must be a *Named or a *Signature type. If there is no error, the
+// resulting Type is a new, instantiated (not parameterized) type of the same
+// kind (either a *Named or a *Signature). Methods attached to a *Named type
+// are also instantiated, and associated with a new *Func that has the same
+// position as the original method, but nil function scope.
+//
+// If ctxt is non-nil, it may be used to de-duplicate the instance against
+// previous instances with the same identity. As a special case, generic
+// *Signature origin types are only considered identical if they are pointer
+// equivalent, so that instantiating distinct (but possibly identical)
+// signatures will yield different instances.
+//
+// If validate is set, Instantiate verifies that the number of type arguments
+// and parameters match, and that the type arguments satisfy their
+// corresponding type constraints. If verification fails, the resulting error
+// may wrap an *ArgumentError indicating which type argument did not satisfy
+// its corresponding type parameter constraint, and why.
+//
+// If validate is not set, Instantiate does not verify the type argument count
+// or whether the type arguments satisfy their constraints. Instantiate is
+// guaranteed to not return an error, but may panic. Specifically, for
+// *Signature types, Instantiate will panic immediately if the type argument
+// count is incorrect; for *Named types, a panic may occur later inside the
+// *Named API.
+func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, error) {
+ if validate {
+ var tparams []*TypeParam
+ switch t := orig.(type) {
+ case *Named:
+ tparams = t.TypeParams().list()
+ case *Signature:
+ tparams = t.TypeParams().list()
+ }
+ if len(targs) != len(tparams) {
+ return nil, fmt.Errorf("got %d type arguments but %s has %d type parameters", len(targs), orig, len(tparams))
+ }
+ if i, err := (*Checker)(nil).verify(token.NoPos, tparams, targs); err != nil {
+ return nil, &ArgumentError{i, err}
+ }
+ }
+
+ inst := (*Checker)(nil).instance(token.NoPos, orig, targs, ctxt)
+ return inst, nil
+}
+
+// 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 token.Pos, orig Type, targs []Type, ctxt *Context) (res Type) {
+ var h string
+ if ctxt != nil {
+ h = ctxt.instanceHash(orig, targs)
+ // typ may already have been instantiated with identical type arguments. In
+ // that case, re-use the existing instance.
+ if inst := ctxt.lookup(h, orig, targs); inst != nil {
+ return inst
+ }
+ }
+
+ switch orig := orig.(type) {
+ case *Named:
+ tname := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
+ named := check.newNamed(tname, orig, nil, nil, nil) // underlying, tparams, and methods are set when named is resolved
+ named.targs = newTypeList(targs)
+ named.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, *methodList) {
+ return expandNamed(ctxt, n, pos)
+ }
+ res = named
+
+ case *Signature:
+ tparams := orig.TypeParams()
+ if !check.validateTArgLen(pos, tparams.Len(), len(targs)) {
+ return Typ[Invalid]
+ }
+ if tparams.Len() == 0 {
+ return orig // nothing to do (minor optimization)
+ }
+ sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), ctxt).(*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 == orig {
+ copy := *sig
+ sig = &copy
+ }
+ // After instantiating a generic signature, it is not generic
+ // anymore; we need to set tparams to nil.
+ sig.tparams = nil
+ res = sig
+ default:
+ // only types and functions can be generic
+ panic(fmt.Sprintf("%v: cannot instantiate %v", pos, orig))
+ }
+
+ if ctxt != nil {
+ // It's possible that we've lost a race to add named to the context.
+ // In this case, use whichever instance is recorded in the context.
+ res = ctxt.update(h, orig, targs, res)
+ }
+
+ return res
+}
+
+// validateTArgLen verifies that the length of targs and tparams matches,
+// reporting an error if not. If validation fails and check is nil,
+// validateTArgLen panics.
+func (check *Checker) validateTArgLen(pos token.Pos, ntparams, ntargs int) bool {
+ if ntargs != ntparams {
+ // TODO(gri) provide better error message
+ if check != nil {
+ check.errorf(atPos(pos), _WrongTypeArgCount, "got %d arguments but %d type parameters", ntargs, ntparams)
+ return false
+ }
+ panic(fmt.Sprintf("%v: got %d arguments but %d type parameters", pos, ntargs, ntparams))
+ }
+ return true
+}
+
+func (check *Checker) verify(pos token.Pos, tparams []*TypeParam, targs []Type) (int, error) {
+ smap := makeSubstMap(tparams, targs)
+ for i, tpar := range tparams {
+ // 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 instantiated
+ // the parameterized type.
+ bound := check.subst(pos, tpar.bound, smap, nil)
+ if err := check.implements(targs[i], bound); err != nil {
+ return i, err
+ }
+ }
+ return -1, nil
+}
+
+// implements checks if V implements T and reports an error if it doesn't.
+// The receiver may be nil if implements is called through an exported
+// API call such as AssignableTo.
+func (check *Checker) implements(V, T Type) error {
+ Vu := under(V)
+ Tu := under(T)
+ if Vu == Typ[Invalid] || Tu == Typ[Invalid] {
+ return nil // avoid follow-on errors
+ }
+ if p, _ := Vu.(*Pointer); p != nil && under(p.base) == Typ[Invalid] {
+ return nil // avoid follow-on errors (see issue #49541 for an example)
+ }
+
+ var qf Qualifier
+ if check != nil {
+ qf = check.qualifier
+ }
+ errorf := func(format string, args ...any) error {
+ return errors.New(sprintf(nil, qf, false, format, args...))
+ }
+
+ Ti, _ := Tu.(*Interface)
+ if Ti == nil {
+ var fset *token.FileSet
+ if check != nil {
+ fset = check.fset
+ }
+ var cause string
+ if isInterfacePtr(Tu) {
+ cause = sprintf(fset, qf, false, "type %s is pointer to interface, not interface", T)
+ } else {
+ cause = sprintf(fset, qf, false, "%s is not an interface", T)
+ }
+ return errorf("%s does not implement %s (%s)", V, T, cause)
+ }
+
+ // Every type satisfies the empty interface.
+ if Ti.Empty() {
+ return nil
+ }
+ // T is not the empty interface (i.e., the type set of T is restricted)
+
+ // An interface V with an empty type set satisfies any interface.
+ // (The empty set is a subset of any set.)
+ Vi, _ := Vu.(*Interface)
+ if Vi != nil && Vi.typeSet().IsEmpty() {
+ return nil
+ }
+ // type set of V is not empty
+
+ // No type with non-empty type set satisfies the empty type set.
+ if Ti.typeSet().IsEmpty() {
+ return errorf("cannot implement %s (empty type set)", T)
+ }
+
+ // V must implement T's methods, if any.
+ if Ti.NumMethods() > 0 {
+ if m, wrong := check.missingMethod(V, Ti, true); m != nil /* !Implements(V, Ti) */ {
+ if check != nil && compilerErrorMessages {
+ return errorf("%s does not implement %s %s", V, T, check.missingMethodReason(V, T, m, wrong))
+ }
+ var cause string
+ if wrong != nil {
+ if Identical(m.typ, wrong.typ) {
+ cause = fmt.Sprintf("missing method %s (%s has pointer receiver)", m.name, m.name)
+ } else {
+ cause = fmt.Sprintf("wrong type for method %s (have %s, want %s)", m.Name(), wrong.typ, m.typ)
+ }
+ } else {
+ cause = "missing method " + m.Name()
+ }
+ return errorf("%s does not implement %s: %s", V, T, cause)
+ }
+ }
+
+ // If T is comparable, V must be comparable.
+ // Remember as a pending error and report only if we don't have a more specific error.
+ var pending error
+ if Ti.IsComparable() && !Comparable(V) {
+ pending = errorf("%s does not implement comparable", V)
+ }
+
+ // V must also be in the set of types of T, if any.
+ // Constraints with empty type sets were already excluded above.
+ if !Ti.typeSet().hasTerms() {
+ return pending // nothing to do
+ }
+
+ // If V is itself an interface, each of its possible types must be in the set
+ // of T types (i.e., the V type set must be a subset of the T type set).
+ // Interfaces V with empty type sets were already excluded above.
+ if Vi != nil {
+ if !Vi.typeSet().subsetOf(Ti.typeSet()) {
+ // TODO(gri) report which type is missing
+ return errorf("%s does not implement %s", V, T)
+ }
+ return pending
+ }
+
+ // Otherwise, V's type must be included in the iface type set.
+ var alt Type
+ if Ti.typeSet().is(func(t *term) bool {
+ if !t.includes(V) {
+ // If V ∉ t.typ but V ∈ ~t.typ then remember this type
+ // so we can suggest it as an alternative in the error
+ // message.
+ if alt == nil && !t.tilde && Identical(t.typ, under(t.typ)) {
+ tt := *t
+ tt.tilde = true
+ if tt.includes(V) {
+ alt = t.typ
+ }
+ }
+ return true
+ }
+ return false
+ }) {
+ if alt != nil {
+ return errorf("%s does not implement %s (possibly missing ~ for %s in constraint %s)", V, T, alt, T)
+ } else {
+ return errorf("%s does not implement %s", V, T)
+ }
+ }
+
+ return pending
+}
diff --git a/libgo/go/go/types/instantiate_test.go b/libgo/go/go/types/instantiate_test.go
new file mode 100644
index 0000000..281c8bb
--- /dev/null
+++ b/libgo/go/go/types/instantiate_test.go
@@ -0,0 +1,253 @@
+// 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.
+
+package types_test
+
+import (
+ "go/token"
+ . "go/types"
+ "strings"
+ "testing"
+)
+
+func TestInstantiateEquality(t *testing.T) {
+ emptySignature := NewSignatureType(nil, nil, nil, nil, nil, false)
+ tests := []struct {
+ src string
+ name1 string
+ targs1 []Type
+ name2 string
+ targs2 []Type
+ wantEqual bool
+ }{
+ {
+ "package basictype; type T[P any] int",
+ "T", []Type{Typ[Int]},
+ "T", []Type{Typ[Int]},
+ true,
+ },
+ {
+ "package differenttypeargs; type T[P any] int",
+ "T", []Type{Typ[Int]},
+ "T", []Type{Typ[String]},
+ false,
+ },
+ {
+ "package typeslice; type T[P any] int",
+ "T", []Type{NewSlice(Typ[Int])},
+ "T", []Type{NewSlice(Typ[Int])},
+ true,
+ },
+ {
+ // interface{interface{...}} is equivalent to interface{...}
+ "package equivalentinterfaces; type T[P any] int",
+ "T", []Type{
+ NewInterfaceType([]*Func{NewFunc(token.NoPos, nil, "M", emptySignature)}, nil),
+ },
+ "T", []Type{
+ NewInterfaceType(
+ nil,
+ []Type{
+ NewInterfaceType([]*Func{NewFunc(token.NoPos, nil, "M", emptySignature)}, nil),
+ },
+ ),
+ },
+ true,
+ },
+ {
+ // int|string is equivalent to string|int
+ "package equivalenttypesets; type T[P any] int",
+ "T", []Type{
+ NewInterfaceType(nil, []Type{
+ NewUnion([]*Term{NewTerm(false, Typ[Int]), NewTerm(false, Typ[String])}),
+ }),
+ },
+ "T", []Type{
+ NewInterfaceType(nil, []Type{
+ NewUnion([]*Term{NewTerm(false, Typ[String]), NewTerm(false, Typ[Int])}),
+ }),
+ },
+ true,
+ },
+ {
+ "package basicfunc; func F[P any]() {}",
+ "F", []Type{Typ[Int]},
+ "F", []Type{Typ[Int]},
+ true,
+ },
+ {
+ "package funcslice; func F[P any]() {}",
+ "F", []Type{NewSlice(Typ[Int])},
+ "F", []Type{NewSlice(Typ[Int])},
+ true,
+ },
+ {
+ "package funcwithparams; func F[P any](x string) float64 { return 0 }",
+ "F", []Type{Typ[Int]},
+ "F", []Type{Typ[Int]},
+ true,
+ },
+ {
+ "package differentfuncargs; func F[P any](x string) float64 { return 0 }",
+ "F", []Type{Typ[Int]},
+ "F", []Type{Typ[String]},
+ false,
+ },
+ {
+ "package funcequality; func F1[P any](x int) {}; func F2[Q any](x int) {}",
+ "F1", []Type{Typ[Int]},
+ "F2", []Type{Typ[Int]},
+ false,
+ },
+ {
+ "package funcsymmetry; func F1[P any](x P) {}; func F2[Q any](x Q) {}",
+ "F1", []Type{Typ[Int]},
+ "F2", []Type{Typ[Int]},
+ false,
+ },
+ }
+
+ for _, test := range tests {
+ pkg, err := pkgForMode(".", test.src, nil, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ t.Run(pkg.Name(), func(t *testing.T) {
+ ctxt := NewContext()
+
+ T1 := pkg.Scope().Lookup(test.name1).Type()
+ res1, err := Instantiate(ctxt, T1, test.targs1, false)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ T2 := pkg.Scope().Lookup(test.name2).Type()
+ res2, err := Instantiate(ctxt, T2, test.targs2, false)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if gotEqual := res1 == res2; gotEqual != test.wantEqual {
+ t.Errorf("%s == %s: %t, want %t", res1, res2, gotEqual, test.wantEqual)
+ }
+ })
+ }
+}
+
+func TestInstantiateNonEquality(t *testing.T) {
+ const src = "package p; type T[P any] int"
+
+ pkg1, err := pkgForMode(".", src, nil, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ pkg2, err := pkgForMode(".", src, nil, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // We consider T1 and T2 to be distinct types, so their instances should not
+ // be deduplicated by the context.
+ T1 := pkg1.Scope().Lookup("T").Type().(*Named)
+ T2 := pkg2.Scope().Lookup("T").Type().(*Named)
+
+ ctxt := NewContext()
+ res1, err := Instantiate(ctxt, T1, []Type{Typ[Int]}, false)
+ if err != nil {
+ t.Fatal(err)
+ }
+ res2, err := Instantiate(ctxt, T2, []Type{Typ[Int]}, false)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if res1 == res2 {
+ t.Errorf("instance from pkg1 (%s) is pointer-equivalent to instance from pkg2 (%s)", res1, res2)
+ }
+ if Identical(res1, res2) {
+ t.Errorf("instance from pkg1 (%s) is identical to instance from pkg2 (%s)", res1, res2)
+ }
+}
+
+func TestMethodInstantiation(t *testing.T) {
+ const prefix = `package p
+
+type T[P any] struct{}
+
+var X T[int]
+
+`
+ tests := []struct {
+ decl string
+ want string
+ }{
+ {"func (r T[P]) m() P", "func (T[int]).m() int"},
+ {"func (r T[P]) m(P)", "func (T[int]).m(int)"},
+ {"func (r *T[P]) m(P)", "func (*T[int]).m(int)"},
+ {"func (r T[P]) m() T[P]", "func (T[int]).m() T[int]"},
+ {"func (r T[P]) m(T[P])", "func (T[int]).m(T[int])"},
+ {"func (r T[P]) m(T[P], P, string)", "func (T[int]).m(T[int], int, string)"},
+ {"func (r T[P]) m(T[P], T[string], T[int])", "func (T[int]).m(T[int], T[string], T[int])"},
+ }
+
+ for _, test := range tests {
+ src := prefix + test.decl
+ pkg, err := pkgForMode(".", src, nil, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ typ := NewPointer(pkg.Scope().Lookup("X").Type())
+ obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m")
+ m, _ := obj.(*Func)
+ if m == nil {
+ t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj)
+ }
+ if got := ObjectString(m, RelativeTo(pkg)); got != test.want {
+ t.Errorf("instantiated %q, want %q", got, test.want)
+ }
+ }
+}
+
+func TestImmutableSignatures(t *testing.T) {
+ const src = `package p
+
+type T[P any] struct{}
+
+func (T[P]) m() {}
+
+var _ T[int]
+`
+ pkg, err := pkgForMode(".", src, nil, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ typ := pkg.Scope().Lookup("T").Type().(*Named)
+ obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m")
+ if obj == nil {
+ t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj)
+ }
+
+ // Verify that the original method is not mutated by instantiating T (this
+ // bug manifested when subst did not return a new signature).
+ want := "func (T[P]).m()"
+ if got := stripAnnotations(ObjectString(obj, RelativeTo(pkg))); got != want {
+ t.Errorf("instantiated %q, want %q", got, want)
+ }
+}
+
+// Copied from errors.go.
+func stripAnnotations(s string) string {
+ var b strings.Builder
+ for _, r := range s {
+ // strip #'s and subscript digits
+ if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
+ b.WriteRune(r)
+ }
+ }
+ if b.Len() < len(s) {
+ return b.String()
+ }
+ return s
+}
diff --git a/libgo/go/go/types/interface.go b/libgo/go/go/types/interface.go
new file mode 100644
index 0000000..b9d4660
--- /dev/null
+++ b/libgo/go/go/types/interface.go
@@ -0,0 +1,225 @@
+// 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.
+
+package types
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+// ----------------------------------------------------------------------------
+// API
+
+// An Interface represents an interface type.
+type Interface struct {
+ check *Checker // for error reporting; nil once type set is computed
+ obj *TypeName // type name object defining this interface; or nil (for better error messages)
+ methods []*Func // ordered list of explicitly declared methods
+ embeddeds []Type // ordered list of explicitly embedded elements
+ embedPos *[]token.Pos // positions of embedded elements; or nil (for error messages) - use pointer to save space
+ implicit bool // interface is wrapper for type set literal (non-interface T, ~T, or A|B)
+ complete bool // indicates that obj, methods, and embeddeds are set and type set can be computed
+
+ tset *_TypeSet // type set described by this interface, computed lazily
+}
+
+// typeSet returns the type set for interface t.
+func (t *Interface) typeSet() *_TypeSet { return computeInterfaceTypeSet(t.check, token.NoPos, t) }
+
+// emptyInterface represents the empty (completed) interface
+var emptyInterface = Interface{complete: true, tset: &topTypeSet}
+
+// NewInterface returns a new interface for the given methods and embedded types.
+// NewInterface takes ownership of the provided methods and may modify their types
+// by setting missing receivers.
+//
+// Deprecated: Use NewInterfaceType instead which allows arbitrary embedded types.
+func NewInterface(methods []*Func, embeddeds []*Named) *Interface {
+ tnames := make([]Type, len(embeddeds))
+ for i, t := range embeddeds {
+ tnames[i] = t
+ }
+ return NewInterfaceType(methods, tnames)
+}
+
+// NewInterfaceType returns a new interface for the given methods and embedded
+// types. NewInterfaceType takes ownership of the provided methods and may
+// modify their types by setting missing receivers.
+//
+// To avoid race conditions, the interface's type set should be computed before
+// concurrent use of the interface, by explicitly calling Complete.
+func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
+ if len(methods) == 0 && len(embeddeds) == 0 {
+ return &emptyInterface
+ }
+
+ // set method receivers if necessary
+ typ := new(Interface)
+ for _, m := range methods {
+ if sig := m.typ.(*Signature); sig.recv == nil {
+ sig.recv = NewVar(m.pos, m.pkg, "", typ)
+ }
+ }
+
+ // sort for API stability
+ sortMethods(methods)
+
+ typ.methods = methods
+ typ.embeddeds = embeddeds
+ typ.complete = true
+
+ return typ
+}
+
+// MarkImplicit marks the interface t as implicit, meaning this interface
+// corresponds to a constraint literal such as ~T or A|B without explicit
+// interface embedding. MarkImplicit should be called before any concurrent use
+// of implicit interfaces.
+func (t *Interface) MarkImplicit() {
+ t.implicit = true
+}
+
+// NumExplicitMethods returns the number of explicitly declared methods of interface t.
+func (t *Interface) NumExplicitMethods() int { return len(t.methods) }
+
+// ExplicitMethod returns the i'th explicitly declared method of interface t for 0 <= i < t.NumExplicitMethods().
+// The methods are ordered by their unique Id.
+func (t *Interface) ExplicitMethod(i int) *Func { return t.methods[i] }
+
+// NumEmbeddeds returns the number of embedded types in interface t.
+func (t *Interface) NumEmbeddeds() int { return len(t.embeddeds) }
+
+// Embedded returns the i'th embedded defined (*Named) type of interface t for 0 <= i < t.NumEmbeddeds().
+// The result is nil if the i'th embedded type is not a defined type.
+//
+// Deprecated: Use EmbeddedType which is not restricted to defined (*Named) types.
+func (t *Interface) Embedded(i int) *Named { tname, _ := t.embeddeds[i].(*Named); return tname }
+
+// EmbeddedType returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds().
+func (t *Interface) EmbeddedType(i int) Type { return t.embeddeds[i] }
+
+// NumMethods returns the total number of methods of interface t.
+func (t *Interface) NumMethods() int { return t.typeSet().NumMethods() }
+
+// Method returns the i'th method of interface t for 0 <= i < t.NumMethods().
+// The methods are ordered by their unique Id.
+func (t *Interface) Method(i int) *Func { return t.typeSet().Method(i) }
+
+// Empty reports whether t is the empty interface.
+func (t *Interface) Empty() bool { return t.typeSet().IsAll() }
+
+// IsComparable reports whether each type in interface t's type set is comparable.
+func (t *Interface) IsComparable() bool { return t.typeSet().IsComparable(nil) }
+
+// IsMethodSet reports whether the interface t is fully described by its method
+// set.
+func (t *Interface) IsMethodSet() bool { return t.typeSet().IsMethodSet() }
+
+// IsImplicit reports whether the interface t is a wrapper for a type set literal.
+func (t *Interface) IsImplicit() bool { return t.implicit }
+
+// Complete computes the interface's type set. It must be called by users of
+// NewInterfaceType and NewInterface after the interface's embedded types are
+// fully defined and before using the interface type in any way other than to
+// form other types. The interface must not contain duplicate methods or a
+// panic occurs. Complete returns the receiver.
+//
+// Interface types that have been completed are safe for concurrent use.
+func (t *Interface) Complete() *Interface {
+ if !t.complete {
+ t.complete = true
+ }
+ t.typeSet() // checks if t.tset is already set
+ return t
+}
+
+func (t *Interface) Underlying() Type { return t }
+func (t *Interface) String() string { return TypeString(t, nil) }
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, def *Named) {
+ addEmbedded := func(pos token.Pos, typ Type) {
+ ityp.embeddeds = append(ityp.embeddeds, typ)
+ if ityp.embedPos == nil {
+ ityp.embedPos = new([]token.Pos)
+ }
+ *ityp.embedPos = append(*ityp.embedPos, pos)
+ }
+
+ for _, f := range iface.Methods.List {
+ if len(f.Names) == 0 {
+ addEmbedded(f.Type.Pos(), parseUnion(check, f.Type))
+ continue
+ }
+ // f.Name != nil
+
+ // We have a method with name f.Names[0].
+ name := f.Names[0]
+ if name.Name == "_" {
+ check.errorf(name, _BlankIfaceMethod, "invalid method name _")
+ continue // ignore
+ }
+
+ typ := check.typ(f.Type)
+ sig, _ := typ.(*Signature)
+ if sig == nil {
+ if typ != Typ[Invalid] {
+ check.invalidAST(f.Type, "%s is not a method signature", typ)
+ }
+ continue // ignore
+ }
+
+ // Always type-check method type parameters but complain if they are not enabled.
+ // (This extra check is needed here because interface method signatures don't have
+ // a receiver specification.)
+ if sig.tparams != nil {
+ var at positioner = f.Type
+ if ftyp, _ := f.Type.(*ast.FuncType); ftyp != nil && ftyp.TypeParams != nil {
+ at = ftyp.TypeParams
+ }
+ check.errorf(at, _InvalidMethodTypeParams, "methods cannot have type parameters")
+ }
+
+ // use named receiver type if available (for better error messages)
+ var recvTyp Type = ityp
+ if def != nil {
+ recvTyp = def
+ }
+ sig.recv = NewVar(name.Pos(), check.pkg, "", recvTyp)
+
+ m := NewFunc(name.Pos(), check.pkg, name.Name, sig)
+ check.recordDef(name, m)
+ ityp.methods = append(ityp.methods, m)
+ }
+
+ // All methods and embedded elements for this interface are collected;
+ // i.e., this interface may be used in a type set computation.
+ ityp.complete = true
+
+ if len(ityp.methods) == 0 && len(ityp.embeddeds) == 0 {
+ // empty interface
+ ityp.tset = &topTypeSet
+ return
+ }
+
+ // sort for API stability
+ sortMethods(ityp.methods)
+ // (don't sort embeddeds: they must correspond to *embedPos entries)
+
+ // Compute type set with a non-nil *Checker as soon as possible
+ // to report any errors. Subsequent uses of type sets will use
+ // this computed type set and won't need to pass in a *Checker.
+ //
+ // Pin the checker to the interface type in the interim, in case the type set
+ // must be used before delayed funcs are processed (see issue #48234).
+ // TODO(rfindley): clean up use of *Checker with computeInterfaceTypeSet
+ ityp.check = check
+ check.later(func() {
+ computeInterfaceTypeSet(check, iface.Pos(), ityp)
+ ityp.check = nil
+ }).describef(iface, "compute type set for %s", ityp)
+}
diff --git a/libgo/go/go/types/issues_test.go b/libgo/go/go/types/issues_test.go
index 6d08f88..e3efb3f 100644
--- a/libgo/go/go/types/issues_test.go
+++ b/libgo/go/go/types/issues_test.go
@@ -636,7 +636,34 @@ var _ T = template /* ERROR cannot use.*text/template.* as T value */.Template{}
}
imp := importHelper{pkg: a, fallback: importer.Default()}
- checkFiles(t, nil, "", []string{"b.go"}, [][]byte{[]byte(bsrc)}, false, imp)
- checkFiles(t, nil, "", []string{"c.go"}, [][]byte{[]byte(csrc)}, false, imp)
- checkFiles(t, nil, "", []string{"t.go"}, [][]byte{[]byte(tsrc)}, false, imp)
+ testFiles(t, nil, []string{"b.go"}, [][]byte{[]byte(bsrc)}, false, imp)
+ testFiles(t, nil, []string{"c.go"}, [][]byte{[]byte(csrc)}, false, imp)
+ testFiles(t, nil, []string{"t.go"}, [][]byte{[]byte(tsrc)}, false, imp)
+}
+
+func TestIssue50646(t *testing.T) {
+ anyType := Universe.Lookup("any").Type()
+ comparableType := Universe.Lookup("comparable").Type()
+
+ if !Comparable(anyType) {
+ t.Errorf("any is not a comparable type")
+ }
+ if !Comparable(comparableType) {
+ t.Errorf("comparable is not a comparable type")
+ }
+
+ // TODO(gri) should comparable be an alias, like any? (see #50791)
+ if !Implements(anyType, comparableType.Underlying().(*Interface)) {
+ t.Errorf("any does not implement comparable")
+ }
+ if !Implements(comparableType, anyType.(*Interface)) {
+ t.Errorf("comparable does not implement any")
+ }
+
+ if !AssignableTo(anyType, comparableType) {
+ t.Errorf("any not assignable to comparable")
+ }
+ if !AssignableTo(comparableType, anyType) {
+ t.Errorf("comparable not assignable to any")
+ }
}
diff --git a/libgo/go/go/types/labels.go b/libgo/go/go/types/labels.go
index 8cf6e63..f3b7f21 100644
--- a/libgo/go/go/types/labels.go
+++ b/libgo/go/go/types/labels.go
@@ -36,7 +36,8 @@ func (check *Checker) labels(body *ast.BlockStmt) {
}
// spec: "It is illegal to define a label that is never used."
- for _, obj := range all.elems {
+ for name, obj := range all.elems {
+ obj = resolve(name, obj)
if lbl := obj.(*Label); !lbl.used {
check.softErrorf(lbl, _UnusedLabel, "label %s declared but not used", lbl.name)
}
diff --git a/libgo/go/go/types/lookup.go b/libgo/go/go/types/lookup.go
index 9c7bfd4..8198b05 100644
--- a/libgo/go/go/types/lookup.go
+++ b/libgo/go/go/types/lookup.go
@@ -6,13 +6,20 @@
package types
-import "go/token"
+import (
+ "strings"
+)
+
+// Internal use of LookupFieldOrMethod: If the obj result is a method
+// associated with a concrete (non-interface) type, the method's signature
+// may not be fully set up. Call Checker.objDecl(obj, nil) before accessing
+// the method's type.
// LookupFieldOrMethod looks up a field or method with given package and name
// in T and returns the corresponding *Var or *Func, an index sequence, and a
// bool indicating if there were any pointer indirections on the path to the
// field or method. If addressable is set, T is the type of an addressable
-// variable (only matters for method lookups).
+// variable (only matters for method lookups). T must not be nil.
//
// The last index entry is the field or method index in the (possibly embedded)
// type where the entry was found, either:
@@ -35,29 +42,20 @@ import "go/token"
// the method's formal receiver base type, nor was the receiver addressable.
//
func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
- return (*Checker)(nil).lookupFieldOrMethod(T, addressable, pkg, name)
-}
-
-// Internal use of Checker.lookupFieldOrMethod: If the obj result is a method
-// associated with a concrete (non-interface) type, the method's signature
-// may not be fully set up. Call Checker.objDecl(obj, nil) before accessing
-// the method's type.
-// TODO(gri) Now that we provide the *Checker, we can probably remove this
-// caveat by calling Checker.objDecl from lookupFieldOrMethod. Investigate.
+ if T == nil {
+ panic("LookupFieldOrMethod on nil type")
+ }
-// lookupFieldOrMethod is like the external version but completes interfaces
-// as necessary.
-func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
- // Methods cannot be associated to a named pointer type
+ // Methods cannot be associated to a named pointer type.
// (spec: "The type denoted by T is called the receiver base type;
// it must not be a pointer or interface type and it must be declared
// in the same package as the method.").
// Thus, if we have a named pointer type, proceed with the underlying
// pointer type but discard the result if it is a method since we would
// not have found it for T (see also issue 8590).
- if t := asNamed(T); t != nil {
- if p, _ := t.underlying.(*Pointer); p != nil {
- obj, index, indirect = check.rawLookupFieldOrMethod(p, false, pkg, name)
+ if t, _ := T.(*Named); t != nil {
+ if p, _ := t.Underlying().(*Pointer); p != nil {
+ obj, index, indirect = lookupFieldOrMethod(p, false, pkg, name)
if _, ok := obj.(*Func); ok {
return nil, nil, false
}
@@ -65,7 +63,21 @@ func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package
}
}
- return check.rawLookupFieldOrMethod(T, addressable, pkg, name)
+ obj, index, indirect = lookupFieldOrMethod(T, addressable, pkg, name)
+
+ // If we didn't find anything and if we have a type parameter with a structural constraint,
+ // see if there is a matching field (but not a method, those need to be declared explicitly
+ // in the constraint). If the structural constraint is a named pointer type (see above), we
+ // are ok here because only fields are accepted as results.
+ if obj == nil && isTypeParam(T) {
+ if t := structuralType(T); t != nil {
+ obj, index, indirect = lookupFieldOrMethod(t, addressable, pkg, name)
+ if _, ok := obj.(*Var); !ok {
+ obj, index, indirect = nil, nil, false // accept fields (variables) only
+ }
+ }
+ }
+ return
}
// TODO(gri) The named type consolidation and seen maps below must be
@@ -73,10 +85,11 @@ func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package
// types always have only one representation (even when imported
// indirectly via different packages.)
-// rawLookupFieldOrMethod should only be called by lookupFieldOrMethod and missingMethod.
-func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
+// lookupFieldOrMethod should only be called by LookupFieldOrMethod and missingMethod.
+//
+// The resulting object may not be fully type-checked.
+func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
// WARNING: The code in this function is extremely subtle - do not modify casually!
- // This function and NewMethodSet should be kept in sync.
if name == "_" {
return // blank fields/methods are never found
@@ -84,10 +97,11 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
typ, isPtr := deref(T)
- // *typ where typ is an interface has no methods.
- // Be cautious: typ may be nil (issue 39634, crash #3).
- if typ == nil || isPtr && IsInterface(typ) {
- return
+ // *typ where typ is an interface (incl. a type parameter) has no methods.
+ if isPtr {
+ if _, ok := under(typ).(*Interface); ok {
+ return
+ }
}
// Start with typ as single entry at shallowest depth.
@@ -107,13 +121,12 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
var next []embeddedType // embedded types found at current depth
// look for (pkg, name) in all types at current depth
- var tpar *_TypeParam // set if obj receiver is a type parameter
for _, e := range current {
typ := e.typ
// If we have a named type, we may have associated methods.
// Look for those first.
- if named := asNamed(typ); named != nil {
+ if named, _ := typ.(*Named); named != nil {
if seen[named] {
// We have seen this type before, at a more shallow depth
// (note that multiples of this type at the current depth
@@ -128,7 +141,8 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
seen[named] = true
// look for a matching attached method
- if i, m := lookupMethod(named.methods, pkg, name); m != nil {
+ named.resolve(nil)
+ if i, m := named.lookupMethod(pkg, name); m != nil {
// potential match
// caution: method may not have a proper signature yet
index = concat(e.index, i)
@@ -139,19 +153,9 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
indirect = e.indirect
continue // we can't have a matching field or interface method
}
-
- // continue with underlying type, but only if it's not a type parameter
- // TODO(gri) is this what we want to do for type parameters? (spec question)
- // TODO(#45639) the error message produced as a result of skipping an
- // underlying type parameter should be improved.
- typ = named.under()
- if asTypeParam(typ) != nil {
- continue
- }
}
- tpar = nil
- switch t := typ.(type) {
+ switch t := under(typ).(type) {
case *Struct:
// look for a matching field and collect embedded types
for i, f := range t.fields {
@@ -184,29 +188,13 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
}
case *Interface:
- // look for a matching method
- // TODO(gri) t.allMethods is sorted - use binary search
- check.completeInterface(token.NoPos, t)
- if i, m := lookupMethod(t.allMethods, pkg, name); m != nil {
- assert(m.typ != nil)
- index = concat(e.index, i)
- if obj != nil || e.multiples {
- return nil, index, false // collision
- }
- obj = m
- indirect = e.indirect
- }
-
- case *_TypeParam:
- // only consider explicit methods in the type parameter bound, not
- // methods that may be common to all types in the type list.
- if i, m := lookupMethod(t.Bound().allMethods, pkg, name); m != nil {
+ // look for a matching method (interface may be a type parameter)
+ if i, m := t.typeSet().LookupMethod(pkg, name); m != nil {
assert(m.typ != nil)
index = concat(e.index, i)
if obj != nil || e.multiples {
return nil, index, false // collision
}
- tpar = t
obj = m
indirect = e.indirect
}
@@ -221,15 +209,14 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
// is shorthand for (&x).m()".
if f, _ := obj.(*Func); f != nil {
// determine if method has a pointer receiver
- hasPtrRecv := tpar == nil && ptrRecv(f)
- if hasPtrRecv && !indirect && !addressable {
+ if f.hasPtrRecv() && !indirect && !addressable {
return nil, nil, true // pointer/addressable receiver required
}
}
return
}
- current = check.consolidateMultiples(next)
+ current = consolidateMultiples(next)
}
return nil, nil, false // not found
@@ -246,7 +233,7 @@ type embeddedType struct {
// consolidateMultiples collects multiple list entries with the same type
// into a single entry marked as containing multiples. The result is the
// consolidated list.
-func (check *Checker) consolidateMultiples(list []embeddedType) []embeddedType {
+func consolidateMultiples(list []embeddedType) []embeddedType {
if len(list) <= 1 {
return list // at most one entry - nothing to do
}
@@ -254,7 +241,7 @@ func (check *Checker) consolidateMultiples(list []embeddedType) []embeddedType {
n := 0 // number of entries w/ unique type
prev := make(map[Type]int) // index at which type was previously seen
for _, e := range list {
- if i, found := check.lookupType(prev, e.typ); found {
+ if i, found := lookupType(prev, e.typ); found {
list[i].multiples = true
// ignore this entry
} else {
@@ -266,14 +253,14 @@ func (check *Checker) consolidateMultiples(list []embeddedType) []embeddedType {
return list[:n]
}
-func (check *Checker) lookupType(m map[Type]int, typ Type) (int, bool) {
+func lookupType(m map[Type]int, typ Type) (int, bool) {
// fast path: maybe the types are equal
if i, found := m[typ]; found {
return i, true
}
for t, i := range m {
- if check.identical(t, typ) {
+ if Identical(t, typ) {
return i, true
}
}
@@ -306,41 +293,34 @@ func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType b
// To improve error messages, also report the wrong signature
// when the method exists on *V instead of V.
func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, wrongType *Func) {
- check.completeInterface(token.NoPos, T)
-
// fast path for common case
if T.Empty() {
return
}
- if ityp := asInterface(V); ityp != nil {
- check.completeInterface(token.NoPos, ityp)
- // TODO(gri) allMethods is sorted - can do this more efficiently
- for _, m := range T.allMethods {
- _, f := lookupMethod(ityp.allMethods, m.pkg, m.name)
+ if ityp, _ := under(V).(*Interface); ityp != nil {
+ // TODO(gri) the methods are sorted - could do this more efficiently
+ for _, m := range T.typeSet().methods {
+ _, f := ityp.typeSet().LookupMethod(m.pkg, m.name)
if f == nil {
- // if m is the magic method == we're ok (interfaces are comparable)
- if m.name == "==" || !static {
+ if !static {
continue
}
return m, f
}
+ // both methods must have the same number of type parameters
ftyp := f.typ.(*Signature)
mtyp := m.typ.(*Signature)
- if len(ftyp.tparams) != len(mtyp.tparams) {
+ if ftyp.TypeParams().Len() != mtyp.TypeParams().Len() {
return m, f
}
+ if ftyp.TypeParams().Len() > 0 {
+ panic("method with type parameters")
+ }
- // If the methods have type parameters we don't care whether they
- // are the same or not, as long as they match up. Use unification
- // to see if they can be made to match.
- // TODO(gri) is this always correct? what about type bounds?
- // (Alternative is to rename/subst type parameters and compare.)
- u := newUnifier(check, true)
- u.x.init(ftyp.tparams)
- if !u.unify(ftyp, mtyp) {
+ if !Identical(ftyp, mtyp) {
return m, f
}
}
@@ -349,17 +329,20 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
}
// A concrete type implements T if it implements all methods of T.
- Vd, _ := deref(V)
- Vn := asNamed(Vd)
- for _, m := range T.allMethods {
+ for _, m := range T.typeSet().methods {
// TODO(gri) should this be calling lookupFieldOrMethod instead (and why not)?
- obj, _, _ := check.rawLookupFieldOrMethod(V, false, m.pkg, m.name)
+ obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name)
// Check if *V implements this method of T.
if obj == nil {
ptr := NewPointer(V)
- obj, _, _ = check.rawLookupFieldOrMethod(ptr, false, m.pkg, m.name)
+ obj, _, _ = lookupFieldOrMethod(ptr, false, m.pkg, m.name)
+
if obj != nil {
+ // methods may not have a fully set up signature yet
+ if check != nil {
+ check.objDecl(obj, nil)
+ }
return m, obj.(*Func)
}
}
@@ -367,10 +350,6 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
// we must have a method (not a field of matching function type)
f, _ := obj.(*Func)
if f == nil {
- // if m is the magic method == and V is comparable, we're ok
- if m.name == "==" && Comparable(V) {
- continue
- }
return m, nil
}
@@ -382,38 +361,14 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
// both methods must have the same number of type parameters
ftyp := f.typ.(*Signature)
mtyp := m.typ.(*Signature)
- if len(ftyp.tparams) != len(mtyp.tparams) {
+ if ftyp.TypeParams().Len() != mtyp.TypeParams().Len() {
return m, f
}
-
- // If V is a (instantiated) generic type, its methods are still
- // parameterized using the original (declaration) receiver type
- // parameters (subst simply copies the existing method list, it
- // does not instantiate the methods).
- // In order to compare the signatures, substitute the receiver
- // type parameters of ftyp with V's instantiation type arguments.
- // This lazily instantiates the signature of method f.
- if Vn != nil && len(Vn.tparams) > 0 {
- // Be careful: The number of type arguments may not match
- // the number of receiver parameters. If so, an error was
- // reported earlier but the length discrepancy is still
- // here. Exit early in this case to prevent an assertion
- // failure in makeSubstMap.
- // TODO(gri) Can we avoid this check by fixing the lengths?
- if len(ftyp.rparams) != len(Vn.targs) {
- return
- }
- ftyp = check.subst(token.NoPos, ftyp, makeSubstMap(ftyp.rparams, Vn.targs)).(*Signature)
+ if ftyp.TypeParams().Len() > 0 {
+ panic("method with type parameters")
}
- // If the methods have type parameters we don't care whether they
- // are the same or not, as long as they match up. Use unification
- // to see if they can be made to match.
- // TODO(gri) is this always correct? what about type bounds?
- // (Alternative is to rename/subst type parameters and compare.)
- u := newUnifier(check, true)
- u.x.init(ftyp.tparams)
- if !u.unify(ftyp, mtyp) {
+ if !Identical(ftyp, mtyp) {
return m, f
}
}
@@ -421,6 +376,68 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
return
}
+// missingMethodReason returns a string giving the detailed reason for a missing method m,
+// where m is missing from V, but required by T. It puts the reason in parentheses,
+// and may include more have/want info after that. If non-nil, wrongType is a relevant
+// method that matches in some way. It may have the correct name, but wrong type, or
+// it may have a pointer receiver.
+func (check *Checker) missingMethodReason(V, T Type, m, wrongType *Func) string {
+ var r string
+ var mname string
+ if compilerErrorMessages {
+ mname = m.Name() + " method"
+ } else {
+ mname = "method " + m.Name()
+ }
+ if wrongType != nil {
+ pos := check.fset.Position(wrongType.Pos())
+ if Identical(m.typ, wrongType.typ) {
+ if m.Name() == wrongType.Name() {
+ r = check.sprintf("(%s has pointer receiver) at %s", mname, pos)
+ } else {
+ r = check.sprintf("(missing %s)\n\t\thave %s^^%s at %s\n\t\twant %s^^%s",
+ mname, wrongType.Name(), wrongType.typ, pos, m.Name(), m.typ)
+ }
+ } else {
+ if compilerErrorMessages {
+ r = check.sprintf("(wrong type for %s)\n\t\thave %s^^%s at %s\n\t\twant %s^^%s",
+ mname, wrongType.Name(), wrongType.typ, pos, m.Name(), m.typ)
+ } else {
+ r = check.sprintf("(wrong type for %s)\n\thave %s at %s\nwant %s",
+ mname, wrongType.typ, pos, m.typ)
+ }
+ }
+ // This is a hack to print the function type without the leading
+ // 'func' keyword in the have/want printouts. We could change to have
+ // an extra formatting option for types2.Type that doesn't print out
+ // 'func'.
+ r = strings.Replace(r, "^^func", "", -1)
+ } else if IsInterface(T) {
+ if isInterfacePtr(V) {
+ r = "(" + check.interfacePtrError(V) + ")"
+ }
+ } else if isInterfacePtr(T) {
+ r = "(" + check.interfacePtrError(T) + ")"
+ }
+ if r == "" {
+ r = check.sprintf("(missing %s)", mname)
+ }
+ return r
+}
+
+func isInterfacePtr(T Type) bool {
+ p, _ := under(T).(*Pointer)
+ return p != nil && IsInterface(p.base)
+}
+
+func (check *Checker) interfacePtrError(T Type) string {
+ assert(isInterfacePtr(T))
+ if p, _ := under(T).(*Pointer); isTypeParam(p.base) {
+ return check.sprintf("type %s is pointer to type parameter, not type parameter", T)
+ }
+ return check.sprintf("type %s is pointer to interface, not interface", T)
+}
+
// assertableTo reports whether a value of type V can be asserted to have type T.
// It returns (nil, false) as affirmative answer. Otherwise it returns a missing
// method required by V and whether it is missing or just has the wrong type.
@@ -432,7 +449,7 @@ func (check *Checker) assertableTo(V *Interface, T Type) (method, wrongType *Fun
// no static check is required if T is an interface
// spec: "If T is an interface type, x.(T) asserts that the
// dynamic type of x implements the interface T."
- if asInterface(T) != nil && !forceStrict {
+ if IsInterface(T) && !forceStrict {
return
}
return check.missingMethod(T, V, false)
@@ -442,6 +459,13 @@ func (check *Checker) assertableTo(V *Interface, T Type) (method, wrongType *Fun
// Otherwise it returns (typ, false).
func deref(typ Type) (Type, bool) {
if p, _ := typ.(*Pointer); p != nil {
+ // p.base should never be nil, but be conservative
+ if p.base == nil {
+ if debug {
+ panic("pointer with nil base type (possibly due to an invalid cyclic declaration)")
+ }
+ return Typ[Invalid], true
+ }
return p.base, true
}
return typ, false
@@ -450,8 +474,8 @@ func deref(typ Type) (Type, bool) {
// derefStructPtr dereferences typ if it is a (named or unnamed) pointer to a
// (named or unnamed) struct and returns its base. Otherwise it returns typ.
func derefStructPtr(typ Type) Type {
- if p := asPointer(typ); p != nil {
- if asStruct(p.base) != nil {
+ if p, _ := under(typ).(*Pointer); p != nil {
+ if _, ok := under(p.base).(*Struct); ok {
return p.base
}
}
diff --git a/libgo/go/go/types/map.go b/libgo/go/go/types/map.go
new file mode 100644
index 0000000..01e13b2
--- /dev/null
+++ b/libgo/go/go/types/map.go
@@ -0,0 +1,24 @@
+// Copyright 2011 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.
+
+package types
+
+// A Map represents a map type.
+type Map struct {
+ key, elem Type
+}
+
+// NewMap returns a new map for the given key and element types.
+func NewMap(key, elem Type) *Map {
+ return &Map{key: key, elem: elem}
+}
+
+// Key returns the key type of map m.
+func (m *Map) Key() Type { return m.key }
+
+// Elem returns the element type of map m.
+func (m *Map) Elem() Type { return m.elem }
+
+func (t *Map) Underlying() Type { return t }
+func (t *Map) String() string { return TypeString(t, nil) }
diff --git a/libgo/go/go/types/methodlist.go b/libgo/go/go/types/methodlist.go
new file mode 100644
index 0000000..10a2a32
--- /dev/null
+++ b/libgo/go/go/types/methodlist.go
@@ -0,0 +1,78 @@
+// Copyright 2022 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.
+
+package types
+
+import "sync"
+
+// methodList holds a list of methods that may be lazily resolved by a provided
+// resolution method.
+type methodList struct {
+ methods []*Func
+
+ // guards synchronizes the instantiation of lazy methods. For lazy method
+ // lists, guards is non-nil and of the length passed to newLazyMethodList.
+ // For non-lazy method lists, guards is nil.
+ guards *[]sync.Once
+}
+
+// newMethodList creates a non-lazy method list holding the given methods.
+func newMethodList(methods []*Func) *methodList {
+ return &methodList{methods: methods}
+}
+
+// newLazyMethodList creates a lazy method list of the given length. Methods
+// may be resolved lazily for a given index by providing a resolver function.
+func newLazyMethodList(length int) *methodList {
+ guards := make([]sync.Once, length)
+ return &methodList{
+ methods: make([]*Func, length),
+ guards: &guards,
+ }
+}
+
+// isLazy reports whether the receiver is a lazy method list.
+func (l *methodList) isLazy() bool {
+ return l != nil && l.guards != nil
+}
+
+// Add appends a method to the method list if not not already present. Add
+// panics if the receiver is lazy.
+func (l *methodList) Add(m *Func) {
+ assert(!l.isLazy())
+ if i, _ := lookupMethod(l.methods, m.pkg, m.name); i < 0 {
+ l.methods = append(l.methods, m)
+ }
+}
+
+// Lookup looks up the method identified by pkg and name in the receiver.
+// Lookup panics if the receiver is lazy.
+func (l *methodList) Lookup(pkg *Package, name string) (int, *Func) {
+ assert(!l.isLazy())
+ if l == nil {
+ return -1, nil
+ }
+ return lookupMethod(l.methods, pkg, name)
+}
+
+// Len returns the length of the method list.
+func (l *methodList) Len() int {
+ if l == nil {
+ return 0
+ }
+ return len(l.methods)
+}
+
+// At returns the i'th method of the method list. At panics if i is out of
+// bounds, or if the receiver is lazy and resolve is nil.
+func (l *methodList) At(i int, resolve func() *Func) *Func {
+ if !l.isLazy() {
+ return l.methods[i]
+ }
+ assert(resolve != nil)
+ (*l.guards)[i].Do(func() {
+ l.methods[i] = resolve()
+ })
+ return l.methods[i]
+}
diff --git a/libgo/go/go/types/methodlist_test.go b/libgo/go/go/types/methodlist_test.go
new file mode 100644
index 0000000..e628bce
--- /dev/null
+++ b/libgo/go/go/types/methodlist_test.go
@@ -0,0 +1,41 @@
+// Copyright 2022 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.
+
+package types
+
+import (
+ "go/token"
+ "testing"
+)
+
+func TestLazyMethodList(t *testing.T) {
+ l := newLazyMethodList(2)
+
+ if got := l.Len(); got != 2 {
+ t.Fatalf("Len() = %d, want 2", got)
+ }
+
+ f0 := NewFunc(token.NoPos, nil, "f0", nil)
+ f1 := NewFunc(token.NoPos, nil, "f1", nil)
+
+ // Verify that methodList.At is idempotent, by calling it repeatedly with a
+ // resolve func that returns different pointer values (f0 or f1).
+ steps := []struct {
+ index int
+ resolve *Func // the *Func returned by the resolver
+ want *Func // the actual *Func returned by methodList.At
+ }{
+ {0, f0, f0},
+ {0, f1, f0},
+ {1, f1, f1},
+ {1, f0, f1},
+ }
+
+ for i, step := range steps {
+ got := l.At(step.index, func() *Func { return step.resolve })
+ if got != step.want {
+ t.Errorf("step %d: At(%d, ...) = %s, want %s", i, step.index, got.Name(), step.want.Name())
+ }
+ }
+}
diff --git a/libgo/go/go/types/methodset.go b/libgo/go/go/types/methodset.go
index ae8011a..c1d1e93 100644
--- a/libgo/go/go/types/methodset.go
+++ b/libgo/go/go/types/methodset.go
@@ -111,7 +111,7 @@ func NewMethodSet(T Type) *MethodSet {
// If we have a named type, we may have associated methods.
// Look for those first.
- if named := asNamed(typ); named != nil {
+ if named, _ := typ.(*Named); named != nil {
if seen[named] {
// We have seen this type before, at a more shallow depth
// (note that multiples of this type at the current depth
@@ -125,17 +125,12 @@ func NewMethodSet(T Type) *MethodSet {
}
seen[named] = true
- mset = mset.add(named.methods, e.index, e.indirect, e.multiples)
-
- // continue with underlying type, but only if it's not a type parameter
- // TODO(rFindley): should this use named.under()? Can there be a difference?
- typ = named.underlying
- if _, ok := typ.(*_TypeParam); ok {
- continue
+ for i := 0; i < named.NumMethods(); i++ {
+ mset = mset.addOne(named.Method(i), concat(e.index, i), e.indirect, e.multiples)
}
}
- switch t := typ.(type) {
+ switch t := under(typ).(type) {
case *Struct:
for i, f := range t.fields {
if fset == nil {
@@ -157,10 +152,7 @@ func NewMethodSet(T Type) *MethodSet {
}
case *Interface:
- mset = mset.add(t.allMethods, e.index, true, e.multiples)
-
- case *_TypeParam:
- mset = mset.add(t.Bound().allMethods, e.index, true, e.multiples)
+ mset = mset.add(t.typeSet().methods, e.index, true, e.multiples)
}
}
@@ -190,12 +182,7 @@ func NewMethodSet(T Type) *MethodSet {
}
}
- // It's ok to call consolidateMultiples with a nil *Checker because
- // MethodSets are not used internally (outside debug mode). When used
- // externally, interfaces are expected to be completed and then we do
- // not need a *Checker to complete them when (indirectly) calling
- // Checker.identical via consolidateMultiples.
- current = (*Checker)(nil).consolidateMultiples(next)
+ current = consolidateMultiples(next)
}
if len(base) == 0 {
@@ -229,42 +216,28 @@ func (s methodSet) add(list []*Func, index []int, indirect bool, multiples bool)
if len(list) == 0 {
return s
}
- if s == nil {
- s = make(methodSet)
- }
for i, f := range list {
- key := f.Id()
- // if f is not in the set, add it
- if !multiples {
- // TODO(gri) A found method may not be added because it's not in the method set
- // (!indirect && ptrRecv(f)). A 2nd method on the same level may be in the method
- // set and may not collide with the first one, thus leading to a false positive.
- // Is that possible? Investigate.
- if _, found := s[key]; !found && (indirect || !ptrRecv(f)) {
- s[key] = &Selection{MethodVal, nil, f, concat(index, i), indirect}
- continue
- }
- }
- s[key] = nil // collision
+ s = s.addOne(f, concat(index, i), indirect, multiples)
}
return s
}
-// ptrRecv reports whether the receiver is of the form *T.
-func ptrRecv(f *Func) bool {
- // If a method's receiver type is set, use that as the source of truth for the receiver.
- // Caution: Checker.funcDecl (decl.go) marks a function by setting its type to an empty
- // signature. We may reach here before the signature is fully set up: we must explicitly
- // check if the receiver is set (we cannot just look for non-nil f.typ).
- if sig, _ := f.typ.(*Signature); sig != nil && sig.recv != nil {
- _, isPtr := deref(sig.recv.typ)
- return isPtr
+func (s methodSet) addOne(f *Func, index []int, indirect bool, multiples bool) methodSet {
+ if s == nil {
+ s = make(methodSet)
}
-
- // If a method's type is not set it may be a method/function that is:
- // 1) client-supplied (via NewFunc with no signature), or
- // 2) internally created but not yet type-checked.
- // For case 1) we can't do anything; the client must know what they are doing.
- // For case 2) we can use the information gathered by the resolver.
- return f.hasPtrRecv
+ key := f.Id()
+ // if f is not in the set, add it
+ if !multiples {
+ // TODO(gri) A found method may not be added because it's not in the method set
+ // (!indirect && f.hasPtrRecv()). A 2nd method on the same level may be in the method
+ // set and may not collide with the first one, thus leading to a false positive.
+ // Is that possible? Investigate.
+ if _, found := s[key]; !found && (indirect || !f.hasPtrRecv()) {
+ s[key] = &Selection{MethodVal, nil, f, index, indirect}
+ return s
+ }
+ }
+ s[key] = nil // collision
+ return s
}
diff --git a/libgo/go/go/types/methodset_test.go b/libgo/go/go/types/methodset_test.go
index 4a373fa..73a8442 100644
--- a/libgo/go/go/types/methodset_test.go
+++ b/libgo/go/go/types/methodset_test.go
@@ -7,7 +7,6 @@ package types_test
import (
"testing"
- "go/internal/typeparams"
. "go/types"
)
@@ -47,12 +46,15 @@ func TestNewMethodSet(t *testing.T) {
genericTests := map[string][]method{
// By convention, look up a in the scope of "g"
- "type C interface{ f() }; func g[T C](a T){}": {{"f", []int{0}, true}},
- "type C interface{ f() }; func g[T C]() { var a T; _ = a }": {{"f", []int{0}, true}},
- "type C interface{ f() }; func g[T C]() { var a struct{T}; _ = a }": {{"f", []int{0, 0}, true}},
+ "type C interface{ f() }; func g[T C](a T){}": {{"f", []int{0}, true}},
+ "type C interface{ f() }; func g[T C]() { var a T; _ = a }": {{"f", []int{0}, true}},
- // Issue #45639.
- "type C interface{ f() }; func g[T C]() { type Y T; var a Y; _ = a }": {},
+ // Issue #43621: We don't allow this anymore. Keep this code in case we
+ // decide to revisit this decision.
+ // "type C interface{ f() }; func g[T C]() { var a struct{T}; _ = a }": {{"f", []int{0, 0}, true}},
+
+ // Issue #45639: We also don't allow this anymore.
+ // "type C interface{ f() }; func g[T C]() { type Y T; var a Y; _ = a }": {},
}
check := func(src string, methods []method, generic bool) {
@@ -101,9 +103,7 @@ func TestNewMethodSet(t *testing.T) {
check(src, methods, false)
}
- if typeparams.Enabled {
- for src, methods := range genericTests {
- check(src, methods, true)
- }
+ for src, methods := range genericTests {
+ check(src, methods, true)
}
}
diff --git a/libgo/go/go/types/mono.go b/libgo/go/go/types/mono.go
new file mode 100644
index 0000000..84e1e97
--- /dev/null
+++ b/libgo/go/go/types/mono.go
@@ -0,0 +1,336 @@
+// 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.
+
+package types
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+// This file implements a check to validate that a Go package doesn't
+// have unbounded recursive instantiation, which is not compatible
+// with compilers using static instantiation (such as
+// monomorphization).
+//
+// It implements a sort of "type flow" analysis by detecting which
+// type parameters are instantiated with other type parameters (or
+// types derived thereof). A package cannot be statically instantiated
+// if the graph has any cycles involving at least one derived type.
+//
+// Concretely, we construct a directed, weighted graph. Vertices are
+// used to represent type parameters as well as some defined
+// types. Edges are used to represent how types depend on each other:
+//
+// * Everywhere a type-parameterized function or type is instantiated,
+// we add edges to each type parameter from the vertices (if any)
+// representing each type parameter or defined type referenced by
+// the type argument. If the type argument is just the referenced
+// type itself, then the edge has weight 0, otherwise 1.
+//
+// * For every defined type declared within a type-parameterized
+// function or method, we add an edge of weight 1 to the defined
+// type from each ambient type parameter.
+//
+// For example, given:
+//
+// func f[A, B any]() {
+// type T int
+// f[T, map[A]B]()
+// }
+//
+// we construct vertices representing types A, B, and T. Because of
+// declaration "type T int", we construct edges T<-A and T<-B with
+// weight 1; and because of instantiation "f[T, map[A]B]" we construct
+// edges A<-T with weight 0, and B<-A and B<-B with weight 1.
+//
+// Finally, we look for any positive-weight cycles. Zero-weight cycles
+// are allowed because static instantiation will reach a fixed point.
+
+type monoGraph struct {
+ vertices []monoVertex
+ edges []monoEdge
+
+ // canon maps method receiver type parameters to their respective
+ // receiver type's type parameters.
+ canon map[*TypeParam]*TypeParam
+
+ // nameIdx maps a defined type or (canonical) type parameter to its
+ // vertex index.
+ nameIdx map[*TypeName]int
+}
+
+type monoVertex struct {
+ weight int // weight of heaviest known path to this vertex
+ pre int // previous edge (if any) in the above path
+ len int // length of the above path
+
+ // obj is the defined type or type parameter represented by this
+ // vertex.
+ obj *TypeName
+}
+
+type monoEdge struct {
+ dst, src int
+ weight int
+
+ pos token.Pos
+ typ Type
+}
+
+func (check *Checker) monomorph() {
+ // We detect unbounded instantiation cycles using a variant of
+ // Bellman-Ford's algorithm. Namely, instead of always running |V|
+ // iterations, we run until we either reach a fixed point or we've
+ // found a path of length |V|. This allows us to terminate earlier
+ // when there are no cycles, which should be the common case.
+
+ again := true
+ for again {
+ again = false
+
+ for i, edge := range check.mono.edges {
+ src := &check.mono.vertices[edge.src]
+ dst := &check.mono.vertices[edge.dst]
+
+ // N.B., we're looking for the greatest weight paths, unlike
+ // typical Bellman-Ford.
+ w := src.weight + edge.weight
+ if w <= dst.weight {
+ continue
+ }
+
+ dst.pre = i
+ dst.len = src.len + 1
+ if dst.len == len(check.mono.vertices) {
+ check.reportInstanceLoop(edge.dst)
+ return
+ }
+
+ dst.weight = w
+ again = true
+ }
+ }
+}
+
+func (check *Checker) reportInstanceLoop(v int) {
+ var stack []int
+ seen := make([]bool, len(check.mono.vertices))
+
+ // We have a path that contains a cycle and ends at v, but v may
+ // only be reachable from the cycle, not on the cycle itself. We
+ // start by walking backwards along the path until we find a vertex
+ // that appears twice.
+ for !seen[v] {
+ stack = append(stack, v)
+ seen[v] = true
+ v = check.mono.edges[check.mono.vertices[v].pre].src
+ }
+
+ // Trim any vertices we visited before visiting v the first
+ // time. Since v is the first vertex we found within the cycle, any
+ // vertices we visited earlier cannot be part of the cycle.
+ for stack[0] != v {
+ stack = stack[1:]
+ }
+
+ // TODO(mdempsky): Pivot stack so we report the cycle from the top?
+
+ obj0 := check.mono.vertices[v].obj
+ check.errorf(obj0, _InvalidInstanceCycle, "instantiation cycle:")
+
+ qf := RelativeTo(check.pkg)
+ for _, v := range stack {
+ edge := check.mono.edges[check.mono.vertices[v].pre]
+ obj := check.mono.vertices[edge.dst].obj
+
+ switch obj.Type().(type) {
+ default:
+ panic("unexpected type")
+ case *Named:
+ check.errorf(atPos(edge.pos), _InvalidInstanceCycle, "\t%s implicitly parameterized by %s", obj.Name(), TypeString(edge.typ, qf)) // secondary error, \t indented
+ case *TypeParam:
+ check.errorf(atPos(edge.pos), _InvalidInstanceCycle, "\t%s instantiated as %s", obj.Name(), TypeString(edge.typ, qf)) // secondary error, \t indented
+ }
+ }
+}
+
+// recordCanon records that tpar is the canonical type parameter
+// corresponding to method type parameter mpar.
+func (w *monoGraph) recordCanon(mpar, tpar *TypeParam) {
+ if w.canon == nil {
+ w.canon = make(map[*TypeParam]*TypeParam)
+ }
+ w.canon[mpar] = tpar
+}
+
+// recordInstance records that the given type parameters were
+// instantiated with the corresponding type arguments.
+func (w *monoGraph) recordInstance(pkg *Package, pos token.Pos, tparams []*TypeParam, targs []Type, xlist []ast.Expr) {
+ for i, tpar := range tparams {
+ pos := pos
+ if i < len(xlist) {
+ pos = xlist[i].Pos()
+ }
+ w.assign(pkg, pos, tpar, targs[i])
+ }
+}
+
+// assign records that tpar was instantiated as targ at pos.
+func (w *monoGraph) assign(pkg *Package, pos token.Pos, tpar *TypeParam, targ Type) {
+ // Go generics do not have an analog to C++`s template-templates,
+ // where a template parameter can itself be an instantiable
+ // template. So any instantiation cycles must occur within a single
+ // package. Accordingly, we can ignore instantiations of imported
+ // type parameters.
+ //
+ // TODO(mdempsky): Push this check up into recordInstance? All type
+ // parameters in a list will appear in the same package.
+ if tpar.Obj().Pkg() != pkg {
+ return
+ }
+
+ // flow adds an edge from vertex src representing that typ flows to tpar.
+ flow := func(src int, typ Type) {
+ weight := 1
+ if typ == targ {
+ weight = 0
+ }
+
+ w.addEdge(w.typeParamVertex(tpar), src, weight, pos, targ)
+ }
+
+ // Recursively walk the type argument to find any defined types or
+ // type parameters.
+ var do func(typ Type)
+ do = func(typ Type) {
+ switch typ := typ.(type) {
+ default:
+ panic("unexpected type")
+
+ case *TypeParam:
+ assert(typ.Obj().Pkg() == pkg)
+ flow(w.typeParamVertex(typ), typ)
+
+ case *Named:
+ if src := w.localNamedVertex(pkg, typ.Origin()); src >= 0 {
+ flow(src, typ)
+ }
+
+ targs := typ.TypeArgs()
+ for i := 0; i < targs.Len(); i++ {
+ do(targs.At(i))
+ }
+
+ case *Array:
+ do(typ.Elem())
+ case *Basic:
+ // ok
+ case *Chan:
+ do(typ.Elem())
+ case *Map:
+ do(typ.Key())
+ do(typ.Elem())
+ case *Pointer:
+ do(typ.Elem())
+ case *Slice:
+ do(typ.Elem())
+
+ case *Interface:
+ for i := 0; i < typ.NumMethods(); i++ {
+ do(typ.Method(i).Type())
+ }
+ case *Signature:
+ tuple := func(tup *Tuple) {
+ for i := 0; i < tup.Len(); i++ {
+ do(tup.At(i).Type())
+ }
+ }
+ tuple(typ.Params())
+ tuple(typ.Results())
+ case *Struct:
+ for i := 0; i < typ.NumFields(); i++ {
+ do(typ.Field(i).Type())
+ }
+ }
+ }
+ do(targ)
+}
+
+// localNamedVertex returns the index of the vertex representing
+// named, or -1 if named doesn't need representation.
+func (w *monoGraph) localNamedVertex(pkg *Package, named *Named) int {
+ obj := named.Obj()
+ if obj.Pkg() != pkg {
+ return -1 // imported type
+ }
+
+ root := pkg.Scope()
+ if obj.Parent() == root {
+ return -1 // package scope, no ambient type parameters
+ }
+
+ if idx, ok := w.nameIdx[obj]; ok {
+ return idx
+ }
+
+ idx := -1
+
+ // Walk the type definition's scope to find any ambient type
+ // parameters that it's implicitly parameterized by.
+ for scope := obj.Parent(); scope != root; scope = scope.Parent() {
+ for _, elem := range scope.elems {
+ if elem, ok := elem.(*TypeName); ok && !elem.IsAlias() && elem.Pos() < obj.Pos() {
+ if tpar, ok := elem.Type().(*TypeParam); ok {
+ if idx < 0 {
+ idx = len(w.vertices)
+ w.vertices = append(w.vertices, monoVertex{obj: obj})
+ }
+
+ w.addEdge(idx, w.typeParamVertex(tpar), 1, obj.Pos(), tpar)
+ }
+ }
+ }
+ }
+
+ if w.nameIdx == nil {
+ w.nameIdx = make(map[*TypeName]int)
+ }
+ w.nameIdx[obj] = idx
+ return idx
+}
+
+// typeParamVertex returns the index of the vertex representing tpar.
+func (w *monoGraph) typeParamVertex(tpar *TypeParam) int {
+ if x, ok := w.canon[tpar]; ok {
+ tpar = x
+ }
+
+ obj := tpar.Obj()
+
+ if idx, ok := w.nameIdx[obj]; ok {
+ return idx
+ }
+
+ if w.nameIdx == nil {
+ w.nameIdx = make(map[*TypeName]int)
+ }
+
+ idx := len(w.vertices)
+ w.vertices = append(w.vertices, monoVertex{obj: obj})
+ w.nameIdx[obj] = idx
+ return idx
+}
+
+func (w *monoGraph) addEdge(dst, src, weight int, pos token.Pos, typ Type) {
+ // TODO(mdempsky): Deduplicate redundant edges?
+ w.edges = append(w.edges, monoEdge{
+ dst: dst,
+ src: src,
+ weight: weight,
+
+ pos: pos,
+ typ: typ,
+ })
+}
diff --git a/libgo/go/go/types/mono_test.go b/libgo/go/go/types/mono_test.go
new file mode 100644
index 0000000..f9e72c3
--- /dev/null
+++ b/libgo/go/go/types/mono_test.go
@@ -0,0 +1,97 @@
+// 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.
+
+package types_test
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "go/token"
+ "go/types"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+func checkMono(t *testing.T, body string) error {
+ if runtime.Compiler == "gccgo" {
+ t.Skip("skipping for gofronted: fails to import unsafe")
+ }
+
+ fset := token.NewFileSet()
+ file, err := parser.ParseFile(fset, "x.go", "package x; import `unsafe`; var _ unsafe.Pointer;\n"+body, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ files := []*ast.File{file}
+
+ var buf bytes.Buffer
+ conf := types.Config{
+ Error: func(err error) { fmt.Fprintln(&buf, err) },
+ Importer: importer.Default(),
+ }
+ conf.Check("x", fset, files, nil)
+ if buf.Len() == 0 {
+ return nil
+ }
+ return errors.New(strings.TrimRight(buf.String(), "\n"))
+}
+
+func TestMonoGood(t *testing.T) {
+ for i, good := range goods {
+ if err := checkMono(t, good); err != nil {
+ t.Errorf("%d: unexpected failure: %v", i, err)
+ }
+ }
+}
+
+func TestMonoBad(t *testing.T) {
+ for i, bad := range bads {
+ if err := checkMono(t, bad); err == nil {
+ t.Errorf("%d: unexpected success", i)
+ } else {
+ t.Log(err)
+ }
+ }
+}
+
+var goods = []string{
+ "func F[T any](x T) { F(x) }",
+ "func F[T, U, V any]() { F[U, V, T](); F[V, T, U]() }",
+ "type Ring[A, B, C any] struct { L *Ring[B, C, A]; R *Ring[C, A, B] }",
+ "func F[T any]() { type U[T any] [unsafe.Sizeof(F[*T])]byte }",
+ "func F[T any]() { type U[T any] [unsafe.Sizeof(F[*T])]byte; var _ U[int] }",
+ "type U[T any] [unsafe.Sizeof(F[*T])]byte; func F[T any]() { var _ U[U[int]] }",
+ "func F[T any]() { type A = int; F[A]() }",
+}
+
+// TODO(mdempsky): Validate specific error messages and positioning.
+
+var bads = []string{
+ "func F[T any](x T) { F(&x) }",
+ "func F[T any]() { F[*T]() }",
+ "func F[T any]() { F[[]T]() }",
+ "func F[T any]() { F[[1]T]() }",
+ "func F[T any]() { F[chan T]() }",
+ "func F[T any]() { F[map[*T]int]() }",
+ "func F[T any]() { F[map[error]T]() }",
+ "func F[T any]() { F[func(T)]() }",
+ "func F[T any]() { F[func() T]() }",
+ "func F[T any]() { F[struct{ t T }]() }",
+ "func F[T any]() { F[interface{ t() T }]() }",
+ "type U[_ any] int; func F[T any]() { F[U[T]]() }",
+ "func F[T any]() { type U int; F[U]() }",
+ "func F[T any]() { type U int; F[*U]() }",
+ "type U[T any] int; func (U[T]) m() { var _ U[*T] }",
+ "type U[T any] int; func (*U[T]) m() { var _ U[*T] }",
+ "type U[T1 any] [unsafe.Sizeof(F[*T1])]byte; func F[T2 any]() { var _ U[T2] }",
+ "func F[A, B, C, D, E any]() { F[B, C, D, E, *A]() }",
+ "type U[_ any] int; const X = unsafe.Sizeof(func() { type A[T any] U[A[*T]] })",
+ "func F[T any]() { type A = *T; F[A]() }",
+ "type A[T any] struct { _ A[*T] }",
+}
diff --git a/libgo/go/go/types/named.go b/libgo/go/go/types/named.go
new file mode 100644
index 0000000..28db260
--- /dev/null
+++ b/libgo/go/go/types/named.go
@@ -0,0 +1,391 @@
+// Copyright 2011 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.
+
+package types
+
+import (
+ "go/token"
+ "sync"
+)
+
+// A Named represents a named (defined) type.
+type Named struct {
+ check *Checker
+ obj *TypeName // corresponding declared object for declared types; placeholder for instantiated types
+ orig *Named // original, uninstantiated type
+ fromRHS Type // type (on RHS of declaration) this *Named type is derived of (for cycle reporting)
+ underlying Type // possibly a *Named during setup; never a *Named once set up completely
+ tparams *TypeParamList // type parameters, or nil
+ targs *TypeList // type arguments (after instantiation), or nil
+
+ // methods declared for this type (not the method set of this type).
+ // Signatures are type-checked lazily.
+ // For non-instantiated types, this is a fully populated list of methods. For
+ // instantiated types, this is a 'lazy' list, and methods are instantiated
+ // when they are first accessed.
+ methods *methodList
+
+ // resolver may be provided to lazily resolve type parameters, underlying, and methods.
+ resolver func(*Context, *Named) (tparams *TypeParamList, underlying Type, methods *methodList)
+ once sync.Once // ensures that tparams, underlying, and methods are resolved before accessing
+}
+
+// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
+// If the given type name obj doesn't have a type yet, its type is set to the returned named type.
+// The underlying type must not be a *Named.
+func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
+ if _, ok := underlying.(*Named); ok {
+ panic("underlying type must not be *Named")
+ }
+ return (*Checker)(nil).newNamed(obj, nil, underlying, nil, newMethodList(methods))
+}
+
+func (t *Named) resolve(ctxt *Context) *Named {
+ if t.resolver == nil {
+ return t
+ }
+
+ t.once.Do(func() {
+ // TODO(mdempsky): Since we're passing t to the resolver anyway
+ // (necessary because types2 expects the receiver type for methods
+ // on defined interface types to be the Named rather than the
+ // underlying Interface), maybe it should just handle calling
+ // SetTypeParams, SetUnderlying, and AddMethod instead? Those
+ // methods would need to support reentrant calls though. It would
+ // also make the API more future-proof towards further extensions
+ // (like SetTypeParams).
+ t.tparams, t.underlying, t.methods = t.resolver(ctxt, t)
+ t.fromRHS = t.underlying // for cycle detection
+ })
+ return t
+}
+
+// newNamed is like NewNamed but with a *Checker receiver and additional orig argument.
+func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tparams *TypeParamList, methods *methodList) *Named {
+ typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, tparams: tparams, methods: methods}
+ if typ.orig == nil {
+ typ.orig = typ
+ }
+ if obj.typ == nil {
+ obj.typ = typ
+ }
+ // Ensure that typ is always expanded and sanity-checked.
+ if check != nil {
+ check.defTypes = append(check.defTypes, typ)
+ }
+ return typ
+}
+
+// Obj returns the type name for the declaration defining the named type t. For
+// instantiated types, this is the type name of the base type.
+func (t *Named) Obj() *TypeName {
+ return t.orig.obj // for non-instances this is the same as t.obj
+}
+
+// Origin returns the parameterized type from which the named type t is
+// instantiated. If t is not an instantiated type, the result is t.
+func (t *Named) Origin() *Named { return t.orig }
+
+// TODO(gri) Come up with a better representation and API to distinguish
+// between parameterized instantiated and non-instantiated types.
+
+// TypeParams returns the type parameters of the named type t, or nil.
+// The result is non-nil for an (originally) parameterized type even if it is instantiated.
+func (t *Named) TypeParams() *TypeParamList { return t.resolve(nil).tparams }
+
+// SetTypeParams sets the type parameters of the named type t.
+// t must not have type arguments.
+func (t *Named) SetTypeParams(tparams []*TypeParam) {
+ assert(t.targs.Len() == 0)
+ t.resolve(nil).tparams = bindTParams(tparams)
+}
+
+// TypeArgs returns the type arguments used to instantiate the named type t.
+func (t *Named) TypeArgs() *TypeList { return t.targs }
+
+// NumMethods returns the number of explicit methods whose receiver is named type t.
+func (t *Named) NumMethods() int { return t.resolve(nil).methods.Len() }
+
+// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
+func (t *Named) Method(i int) *Func {
+ t.resolve(nil)
+ return t.methods.At(i, func() *Func {
+ return t.instantiateMethod(i)
+ })
+}
+
+// instiateMethod instantiates the i'th method for an instantiated receiver.
+func (t *Named) instantiateMethod(i int) *Func {
+ assert(t.TypeArgs().Len() > 0) // t must be an instance
+
+ // t.orig.methods is not lazy. origm is the method instantiated with its
+ // receiver type parameters (the "origin" method).
+ origm := t.orig.Method(i)
+ assert(origm != nil)
+
+ check := t.check
+ // Ensure that the original method is type-checked.
+ if check != nil {
+ check.objDecl(origm, nil)
+ }
+
+ origSig := origm.typ.(*Signature)
+ rbase, _ := deref(origSig.Recv().Type())
+
+ // If rbase is t, then origm is already the instantiated method we're looking
+ // for. In this case, we return origm to preserve the invariant that
+ // traversing Method->Receiver Type->Method should get back to the same
+ // method.
+ //
+ // This occurs if t is instantiated with the receiver type parameters, as in
+ // the use of m in func (r T[_]) m() { r.m() }.
+ if rbase == t {
+ return origm
+ }
+
+ sig := origSig
+ // We can only substitute if we have a correspondence between type arguments
+ // and type parameters. This check is necessary in the presence of invalid
+ // code.
+ if origSig.RecvTypeParams().Len() == t.targs.Len() {
+ ctxt := check.bestContext(nil)
+ smap := makeSubstMap(origSig.RecvTypeParams().list(), t.targs.list())
+ sig = check.subst(origm.pos, origSig, smap, ctxt).(*Signature)
+ }
+
+ if sig == origSig {
+ // No substitution occurred, but we still need to create a new signature to
+ // hold the instantiated receiver.
+ copy := *origSig
+ sig = &copy
+ }
+
+ var rtyp Type
+ if origm.hasPtrRecv() {
+ rtyp = NewPointer(t)
+ } else {
+ rtyp = t
+ }
+
+ sig.recv = NewParam(origSig.recv.pos, origSig.recv.pkg, origSig.recv.name, rtyp)
+ return NewFunc(origm.pos, origm.pkg, origm.name, sig)
+}
+
+// SetUnderlying sets the underlying type and marks t as complete.
+// t must not have type arguments.
+func (t *Named) SetUnderlying(underlying Type) {
+ assert(t.targs.Len() == 0)
+ if underlying == nil {
+ panic("underlying type must not be nil")
+ }
+ if _, ok := underlying.(*Named); ok {
+ panic("underlying type must not be *Named")
+ }
+ t.resolve(nil).underlying = underlying
+ if t.fromRHS == nil {
+ t.fromRHS = underlying // for cycle detection
+ }
+}
+
+// AddMethod adds method m unless it is already in the method list.
+// t must not have type arguments.
+func (t *Named) AddMethod(m *Func) {
+ assert(t.targs.Len() == 0)
+ t.resolve(nil)
+ if t.methods == nil {
+ t.methods = newMethodList(nil)
+ }
+ t.methods.Add(m)
+}
+
+func (t *Named) Underlying() Type { return t.resolve(nil).underlying }
+func (t *Named) String() string { return TypeString(t, nil) }
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+// under returns the expanded underlying type of n0; possibly by following
+// forward chains of named types. If an underlying type is found, resolve
+// the chain by setting the underlying type for each defined type in the
+// chain before returning it. If no underlying type is found or a cycle
+// is detected, the result is Typ[Invalid]. If a cycle is detected and
+// n0.check != nil, the cycle is reported.
+//
+// This is necessary because the underlying type of named may be itself a
+// named type that is incomplete:
+//
+// type (
+// A B
+// B *C
+// C A
+// )
+//
+// The type of C is the (named) type of A which is incomplete,
+// and which has as its underlying type the named type B.
+func (n0 *Named) under() Type {
+ u := n0.Underlying()
+
+ // If the underlying type of a defined type is not a defined
+ // (incl. instance) type, then that is the desired underlying
+ // type.
+ var n1 *Named
+ switch u1 := u.(type) {
+ case nil:
+ // After expansion via Underlying(), we should never encounter a nil
+ // underlying.
+ panic("nil underlying")
+ default:
+ // common case
+ return u
+ case *Named:
+ // handled below
+ n1 = u1
+ }
+
+ if n0.check == nil {
+ panic("Named.check == nil but type is incomplete")
+ }
+
+ // Invariant: after this point n0 as well as any named types in its
+ // underlying chain should be set up when this function exits.
+ check := n0.check
+ n := n0
+
+ seen := make(map[*Named]int) // types that need their underlying resolved
+ var path []Object // objects encountered, for cycle reporting
+
+loop:
+ for {
+ seen[n] = len(seen)
+ path = append(path, n.obj)
+ n = n1
+ if i, ok := seen[n]; ok {
+ // cycle
+ check.cycleError(path[i:])
+ u = Typ[Invalid]
+ break
+ }
+ u = n.Underlying()
+ switch u1 := u.(type) {
+ case nil:
+ u = Typ[Invalid]
+ break loop
+ default:
+ break loop
+ case *Named:
+ // Continue collecting *Named types in the chain.
+ n1 = u1
+ }
+ }
+
+ for n := range seen {
+ // We should never have to update the underlying type of an imported type;
+ // those underlying types should have been resolved during the import.
+ // Also, doing so would lead to a race condition (was issue #31749).
+ // Do this check always, not just in debug mode (it's cheap).
+ if n.obj.pkg != check.pkg {
+ panic("imported type with unresolved underlying type")
+ }
+ n.underlying = u
+ }
+
+ return u
+}
+
+func (n *Named) setUnderlying(typ Type) {
+ if n != nil {
+ n.underlying = typ
+ }
+}
+
+func (n *Named) lookupMethod(pkg *Package, name string) (int, *Func) {
+ n.resolve(nil)
+ // If n is an instance, we may not have yet instantiated all of its methods.
+ // Look up the method index in orig, and only instantiate method at the
+ // matching index (if any).
+ i, _ := n.orig.methods.Lookup(pkg, name)
+ if i < 0 {
+ return -1, nil
+ }
+ // For instances, m.Method(i) will be different from the orig method.
+ return i, n.Method(i)
+}
+
+// bestContext returns the best available context. In order of preference:
+// - the given ctxt, if non-nil
+// - check.ctxt, if check is non-nil
+// - a new Context
+func (check *Checker) bestContext(ctxt *Context) *Context {
+ if ctxt != nil {
+ return ctxt
+ }
+ if check != nil {
+ if check.ctxt == nil {
+ check.ctxt = NewContext()
+ }
+ return check.ctxt
+ }
+ return NewContext()
+}
+
+// expandNamed ensures that the underlying type of n is instantiated.
+// The underlying type will be Typ[Invalid] if there was an error.
+func expandNamed(ctxt *Context, n *Named, instPos token.Pos) (tparams *TypeParamList, underlying Type, methods *methodList) {
+ n.orig.resolve(ctxt)
+ assert(n.orig.underlying != nil)
+
+ check := n.check
+
+ if _, unexpanded := n.orig.underlying.(*Named); unexpanded {
+ // We should only get an unexpanded underlying here during type checking
+ // (for example, in recursive type declarations).
+ assert(check != nil)
+ }
+
+ // Mismatching arg and tparam length may be checked elsewhere.
+ if n.orig.tparams.Len() == n.targs.Len() {
+ // We must always have a context, to avoid infinite recursion.
+ ctxt = check.bestContext(ctxt)
+ h := ctxt.instanceHash(n.orig, n.targs.list())
+ // ensure that an instance is recorded for h to avoid infinite recursion.
+ ctxt.update(h, n.orig, n.TypeArgs().list(), n)
+
+ smap := makeSubstMap(n.orig.tparams.list(), n.targs.list())
+ underlying = n.check.subst(instPos, n.orig.underlying, smap, ctxt)
+ // If the underlying of n is an interface, we need to set the receiver of
+ // its methods accurately -- we set the receiver of interface methods on
+ // the RHS of a type declaration to the defined type.
+ if iface, _ := underlying.(*Interface); iface != nil {
+ if methods, copied := replaceRecvType(iface.methods, n.orig, n); copied {
+ // If the underlying doesn't actually use type parameters, it's possible
+ // that it wasn't substituted. In this case we need to create a new
+ // *Interface before modifying receivers.
+ if iface == n.orig.underlying {
+ iface = &Interface{
+ embeddeds: iface.embeddeds,
+ complete: iface.complete,
+ implicit: iface.implicit, // should be false but be conservative
+ }
+ underlying = iface
+ }
+ iface.methods = methods
+ }
+ }
+ } else {
+ underlying = Typ[Invalid]
+ }
+
+ return n.orig.tparams, underlying, newLazyMethodList(n.orig.methods.Len())
+}
+
+// safeUnderlying returns the underlying of typ without expanding instances, to
+// avoid infinite recursion.
+//
+// TODO(rfindley): eliminate this function or give it a better name.
+func safeUnderlying(typ Type) Type {
+ if t, _ := typ.(*Named); t != nil {
+ return t.underlying
+ }
+ return typ.Underlying()
+}
diff --git a/libgo/go/go/types/object.go b/libgo/go/go/types/object.go
index 50346ec..fb37700 100644
--- a/libgo/go/go/types/object.go
+++ b/libgo/go/go/types/object.go
@@ -230,6 +230,26 @@ func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}}
}
+// _NewTypeNameLazy returns a new defined type like NewTypeName, but it
+// lazily calls resolve to finish constructing the Named object.
+func _NewTypeNameLazy(pos token.Pos, pkg *Package, name string, load func(named *Named) (tparams []*TypeParam, underlying Type, methods []*Func)) *TypeName {
+ obj := NewTypeName(pos, pkg, name, nil)
+
+ resolve := func(_ *Context, t *Named) (*TypeParamList, Type, *methodList) {
+ tparams, underlying, methods := load(t)
+
+ switch underlying.(type) {
+ case nil, *Named:
+ panic(fmt.Sprintf("invalid underlying type %T", t.underlying))
+ }
+
+ return bindTParams(tparams), underlying, newMethodList(methods)
+ }
+
+ NewNamed(obj, nil, nil).resolver = resolve
+ return obj
+}
+
// IsAlias reports whether obj is an alias name for a type.
func (obj *TypeName) IsAlias() bool {
switch t := obj.typ.(type) {
@@ -249,6 +269,8 @@ func (obj *TypeName) IsAlias() bool {
return obj.pkg != nil || t.name != obj.name || t == universeByte || t == universeRune
case *Named:
return obj != t.obj
+ case *TypeParam:
+ return obj != t.obj
default:
return true
}
@@ -297,7 +319,7 @@ func (*Var) isDependency() {} // a variable may be a dependency of an initializa
// An abstract method may belong to many interfaces due to embedding.
type Func struct {
object
- hasPtrRecv bool // only valid for methods that don't have a type yet
+ hasPtrRecv_ bool // only valid for methods that don't have a type yet; use hasPtrRecv() to read
}
// NewFunc returns a new function with the given signature, representing
@@ -320,8 +342,29 @@ func (obj *Func) FullName() string {
}
// Scope returns the scope of the function's body block.
+// The result is nil for imported or instantiated functions and methods
+// (but there is also no mechanism to get to an instantiated function).
func (obj *Func) Scope() *Scope { return obj.typ.(*Signature).scope }
+// hasPtrRecv reports whether the receiver is of the form *T for the given method obj.
+func (obj *Func) hasPtrRecv() bool {
+ // If a method's receiver type is set, use that as the source of truth for the receiver.
+ // Caution: Checker.funcDecl (decl.go) marks a function by setting its type to an empty
+ // signature. We may reach here before the signature is fully set up: we must explicitly
+ // check if the receiver is set (we cannot just look for non-nil obj.typ).
+ if sig, _ := obj.typ.(*Signature); sig != nil && sig.recv != nil {
+ _, isPtr := deref(sig.recv.typ)
+ return isPtr
+ }
+
+ // If a method's type is not set it may be a method/function that is:
+ // 1) client-supplied (via NewFunc with no signature), or
+ // 2) internally created but not yet type-checked.
+ // For case 1) we can't do anything; the client must know what they are doing.
+ // For case 2) we can use the information gathered by the resolver.
+ return obj.hasPtrRecv_
+}
+
func (*Func) isDependency() {} // a function may be a dependency of an initialization expression
// A Label represents a declared label.
@@ -370,6 +413,9 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
case *TypeName:
tname = obj
buf.WriteString("type")
+ if isTypeParam(typ) {
+ buf.WriteString(" parameter")
+ }
case *Var:
if obj.isField {
@@ -415,19 +461,34 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
}
if tname != nil {
- // We have a type object: Don't print anything more for
- // basic types since there's no more information (names
- // are the same; see also comment in TypeName.IsAlias).
- if _, ok := typ.(*Basic); ok {
+ switch t := typ.(type) {
+ case *Basic:
+ // Don't print anything more for basic types since there's
+ // no more information.
return
+ case *Named:
+ if t.TypeParams().Len() > 0 {
+ newTypeWriter(buf, qf).tParamList(t.TypeParams().list())
+ }
}
if tname.IsAlias() {
buf.WriteString(" =")
+ } else if t, _ := typ.(*TypeParam); t != nil {
+ typ = t.bound
} else {
+ // TODO(gri) should this be fromRHS for *Named?
typ = under(typ)
}
}
+ // Special handling for any: because WriteType will format 'any' as 'any',
+ // resulting in the object string `type any = any` rather than `type any =
+ // interface{}`. To avoid this, swap in a different empty interface.
+ if obj == universeAny {
+ assert(Identical(typ, &emptyInterface))
+ typ = &emptyInterface
+ }
+
buf.WriteByte(' ')
WriteType(buf, typ, qf)
}
diff --git a/libgo/go/go/types/object_test.go b/libgo/go/go/types/object_test.go
index 2b6057b..47c7fcd 100644
--- a/libgo/go/go/types/object_test.go
+++ b/libgo/go/go/types/object_test.go
@@ -2,13 +2,17 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package types
+package types_test
import (
"go/ast"
"go/parser"
"go/token"
+ "internal/testenv"
+ "strings"
"testing"
+
+ . "go/types"
)
func TestIsAlias(t *testing.T) {
@@ -22,7 +26,7 @@ func TestIsAlias(t *testing.T) {
check(Unsafe.Scope().Lookup("Pointer").(*TypeName), false)
for _, name := range Universe.Names() {
if obj, _ := Universe.Lookup(name).(*TypeName); obj != nil {
- check(obj, name == "byte" || name == "rune")
+ check(obj, name == "any" || name == "byte" || name == "rune")
}
}
@@ -30,19 +34,22 @@ func TestIsAlias(t *testing.T) {
pkg := NewPackage("p", "p")
t1 := NewTypeName(0, pkg, "t1", nil)
n1 := NewNamed(t1, new(Struct), nil)
+ t5 := NewTypeName(0, pkg, "t5", nil)
+ NewTypeParam(t5, nil)
for _, test := range []struct {
name *TypeName
alias bool
}{
- {NewTypeName(0, nil, "t0", nil), false}, // no type yet
- {NewTypeName(0, pkg, "t0", nil), false}, // no type yet
- {t1, false}, // type name refers to named type and vice versa
- {NewTypeName(0, nil, "t2", &emptyInterface), true}, // type name refers to unnamed type
- {NewTypeName(0, pkg, "t3", n1), true}, // type name refers to named type with different type name
- {NewTypeName(0, nil, "t4", Typ[Int32]), true}, // type name refers to basic type with different name
- {NewTypeName(0, nil, "int32", Typ[Int32]), false}, // type name refers to basic type with same name
- {NewTypeName(0, pkg, "int32", Typ[Int32]), true}, // type name is declared in user-defined package (outside Universe)
- {NewTypeName(0, nil, "rune", Typ[Rune]), true}, // type name refers to basic type rune which is an alias already
+ {NewTypeName(0, nil, "t0", nil), false}, // no type yet
+ {NewTypeName(0, pkg, "t0", nil), false}, // no type yet
+ {t1, false}, // type name refers to named type and vice versa
+ {NewTypeName(0, nil, "t2", NewInterfaceType(nil, nil)), true}, // type name refers to unnamed type
+ {NewTypeName(0, pkg, "t3", n1), true}, // type name refers to named type with different type name
+ {NewTypeName(0, nil, "t4", Typ[Int32]), true}, // type name refers to basic type with different name
+ {NewTypeName(0, nil, "int32", Typ[Int32]), false}, // type name refers to basic type with same name
+ {NewTypeName(0, pkg, "int32", Typ[Int32]), true}, // type name is declared in user-defined package (outside Universe)
+ {NewTypeName(0, nil, "rune", Typ[Rune]), true}, // type name refers to basic type rune which is an alias already
+ {t5, false}, // type name refers to type parameter and vice versa
} {
check(test.name, test.alias)
}
@@ -84,3 +91,80 @@ func TestEmbeddedMethod(t *testing.T) {
t.Fatalf("%s (%p) != %s (%p)", orig, orig, embed, embed)
}
}
+
+var testObjects = []struct {
+ src string
+ obj string
+ want string
+}{
+ {"import \"io\"; var r io.Reader", "r", "var p.r io.Reader"},
+
+ {"const c = 1.2", "c", "const p.c untyped float"},
+ {"const c float64 = 3.14", "c", "const p.c float64"},
+
+ {"type t struct{f int}", "t", "type p.t struct{f int}"},
+ {"type t func(int)", "t", "type p.t func(int)"},
+ {"type t[P any] struct{f P}", "t", "type p.t[P any] struct{f P}"},
+ {"type t[P any] struct{f P}", "t.P", "type parameter P any"},
+ {"type C interface{m()}; type t[P C] struct{}", "t.P", "type parameter P p.C"},
+
+ {"type t = struct{f int}", "t", "type p.t = struct{f int}"},
+ {"type t = func(int)", "t", "type p.t = func(int)"},
+
+ {"var v int", "v", "var p.v int"},
+
+ {"func f(int) string", "f", "func p.f(int) string"},
+ {"func g[P any](x P){}", "g", "func p.g[P any](x P)"},
+ {"func g[P interface{~int}](x P){}", "g.P", "type parameter P interface{~int}"},
+ {"", "any", "type any = interface{}"},
+}
+
+func TestObjectString(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ for _, test := range testObjects {
+ src := "package p; " + test.src
+ pkg, err := makePkg(src)
+ if err != nil {
+ t.Errorf("%s: %s", src, err)
+ continue
+ }
+
+ names := strings.Split(test.obj, ".")
+ if len(names) != 1 && len(names) != 2 {
+ t.Errorf("%s: invalid object path %s", test.src, test.obj)
+ continue
+ }
+ _, obj := pkg.Scope().LookupParent(names[0], token.NoPos)
+ if obj == nil {
+ t.Errorf("%s: %s not found", test.src, names[0])
+ continue
+ }
+ if len(names) == 2 {
+ if typ, ok := obj.Type().(interface{ TypeParams() *TypeParamList }); ok {
+ obj = lookupTypeParamObj(typ.TypeParams(), names[1])
+ if obj == nil {
+ t.Errorf("%s: %s not found", test.src, test.obj)
+ continue
+ }
+ } else {
+ t.Errorf("%s: %s has no type parameters", test.src, names[0])
+ continue
+ }
+ }
+
+ if got := obj.String(); got != test.want {
+ t.Errorf("%s: got %s, want %s", test.src, got, test.want)
+ }
+ }
+}
+
+func lookupTypeParamObj(list *TypeParamList, name string) Object {
+ for i := 0; i < list.Len(); i++ {
+ tpar := list.At(i)
+ if tpar.Obj().Name() == name {
+ return tpar.Obj()
+ }
+ }
+ return nil
+}
diff --git a/libgo/go/go/types/operand.go b/libgo/go/go/types/operand.go
index 6463728..4d7f1e3 100644
--- a/libgo/go/go/types/operand.go
+++ b/libgo/go/go/types/operand.go
@@ -8,7 +8,6 @@ package types
import (
"bytes"
- "fmt"
"go/ast"
"go/constant"
"go/token"
@@ -105,6 +104,11 @@ func (x *operand) Pos() token.Pos {
// cgofunc <expr> ( <mode> of type <typ>)
//
func operandString(x *operand, qf Qualifier) string {
+ // special-case nil
+ if x.mode == value && x.typ == Typ[UntypedNil] {
+ return "nil"
+ }
+
var buf bytes.Buffer
var expr string
@@ -159,16 +163,17 @@ func operandString(x *operand, qf Qualifier) string {
if hasType {
if x.typ != Typ[Invalid] {
var intro string
- switch {
- case isGeneric(x.typ):
- intro = " of generic type "
- case asTypeParam(x.typ) != nil:
- intro = " of type parameter type "
- default:
+ if isGeneric(x.typ) {
+ intro = " of parameterized type "
+ } else {
intro = " of type "
}
buf.WriteString(intro)
WriteType(&buf, x.typ, qf)
+ if tpar, _ := x.typ.(*TypeParam); tpar != nil {
+ buf.WriteString(" constrained by ")
+ WriteType(&buf, tpar.bound, qf) // do not compute interface type sets here
+ }
} else {
buf.WriteString(" with invalid type")
}
@@ -234,61 +239,131 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er
V := x.typ
// x's type is identical to T
- if check.identical(V, T) {
+ if Identical(V, T) {
return true, 0
}
- Vu := optype(V)
- Tu := optype(T)
+ Vu := under(V)
+ Tu := under(T)
+ Vp, _ := V.(*TypeParam)
+ Tp, _ := T.(*TypeParam)
// x is an untyped value representable by a value of type T.
if isUntyped(Vu) {
- if t, ok := Tu.(*_Sum); ok {
- return t.is(func(t Type) bool {
- // TODO(gri) this could probably be more efficient
- ok, _ := x.assignableTo(check, t, reason)
- return ok
+ assert(Vp == nil)
+ if Tp != nil {
+ // T is a type parameter: x is assignable to T if it is
+ // representable by each specific type in the type set of T.
+ return Tp.is(func(t *term) bool {
+ if t == nil {
+ return false
+ }
+ // A term may be a tilde term but the underlying
+ // type of an untyped value doesn't change so we
+ // don't need to do anything special.
+ newType, _, _ := check.implicitTypeAndValue(x, t.typ)
+ return newType != nil
}), _IncompatibleAssign
}
- newType, _, _ := check.implicitTypeAndValue(x, Tu)
+ newType, _, _ := check.implicitTypeAndValue(x, T)
return newType != nil, _IncompatibleAssign
}
// Vu is typed
// x's type V and T have identical underlying types
// and at least one of V or T is not a named type
- if check.identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
+ // and neither V nor T is a type parameter.
+ if Identical(Vu, Tu) && (!hasName(V) || !hasName(T)) && Vp == nil && Tp == nil {
return true, 0
}
- // T is an interface type and x implements T
- if Ti, ok := Tu.(*Interface); ok {
- if m, wrongType := check.missingMethod(V, Ti, true); m != nil /* Implements(V, Ti) */ {
+ // T is an interface type and x implements T and T is not a type parameter.
+ // Also handle the case where T is a pointer to an interface.
+ if _, ok := Tu.(*Interface); ok && Tp == nil || isInterfacePtr(Tu) {
+ if err := check.implements(V, T); err != nil {
if reason != nil {
- if wrongType != nil {
- if check.identical(m.typ, wrongType.typ) {
- *reason = fmt.Sprintf("missing method %s (%s has pointer receiver)", m.name, m.name)
- } else {
- *reason = fmt.Sprintf("wrong type for method %s (have %s, want %s)", m.Name(), wrongType.typ, m.typ)
- }
-
- } else {
- *reason = "missing method " + m.Name()
- }
+ *reason = err.Error()
}
return false, _InvalidIfaceAssign
}
return true, 0
}
+ // If V is an interface, check if a missing type assertion is the problem.
+ if Vi, _ := Vu.(*Interface); Vi != nil && Vp == nil {
+ if check.implements(T, V) == nil {
+ // T implements V, so give hint about type assertion.
+ if reason != nil {
+ *reason = "need type assertion"
+ }
+ return false, _IncompatibleAssign
+ }
+ }
+
// x is a bidirectional channel value, T is a channel
// type, x's type V and T have identical element types,
- // and at least one of V or T is not a named type
+ // and at least one of V or T is not a named type.
if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv {
- if Tc, ok := Tu.(*Chan); ok && check.identical(Vc.elem, Tc.elem) {
- return !isNamed(V) || !isNamed(T), _InvalidChanAssign
+ if Tc, ok := Tu.(*Chan); ok && Identical(Vc.elem, Tc.elem) {
+ return !hasName(V) || !hasName(T), _InvalidChanAssign
}
}
+ // optimization: if we don't have type parameters, we're done
+ if Vp == nil && Tp == nil {
+ return false, _IncompatibleAssign
+ }
+
+ errorf := func(format string, args ...any) {
+ if check != nil && reason != nil {
+ msg := check.sprintf(format, args...)
+ if *reason != "" {
+ msg += "\n\t" + *reason
+ }
+ *reason = msg
+ }
+ }
+
+ // x's type V is not a named type and T is a type parameter, and
+ // x is assignable to each specific type in T's type set.
+ if !hasName(V) && Tp != nil {
+ ok := false
+ code := _IncompatibleAssign
+ Tp.is(func(T *term) bool {
+ if T == nil {
+ return false // no specific types
+ }
+ ok, code = x.assignableTo(check, T.typ, reason)
+ if !ok {
+ errorf("cannot assign %s to %s (in %s)", x.typ, T.typ, Tp)
+ return false
+ }
+ return true
+ })
+ return ok, code
+ }
+
+ // x's type V is a type parameter and T is not a named type,
+ // and values x' of each specific type in V's type set are
+ // assignable to T.
+ if Vp != nil && !hasName(T) {
+ x := *x // don't clobber outer x
+ ok := false
+ code := _IncompatibleAssign
+ Vp.is(func(V *term) bool {
+ if V == nil {
+ return false // no specific types
+ }
+ x.typ = V.typ
+ ok, code = x.assignableTo(check, T, reason)
+ if !ok {
+ errorf("cannot assign %s (in %s) to %s", V.typ, Vp, T)
+ return false
+ }
+ return true
+ })
+ return ok, code
+ }
+
return false, _IncompatibleAssign
}
diff --git a/libgo/go/go/types/pointer.go b/libgo/go/go/types/pointer.go
new file mode 100644
index 0000000..6352ee5
--- /dev/null
+++ b/libgo/go/go/types/pointer.go
@@ -0,0 +1,19 @@
+// Copyright 2011 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.
+
+package types
+
+// A Pointer represents a pointer type.
+type Pointer struct {
+ base Type // element type
+}
+
+// NewPointer returns a new pointer type for the given element (base) type.
+func NewPointer(elem Type) *Pointer { return &Pointer{base: elem} }
+
+// Elem returns the element type for the given pointer p.
+func (p *Pointer) Elem() Type { return p.base }
+
+func (t *Pointer) Underlying() Type { return t }
+func (t *Pointer) String() string { return TypeString(t, nil) }
diff --git a/libgo/go/go/types/predicates.go b/libgo/go/go/types/predicates.go
index 7bb0264..1ba0043 100644
--- a/libgo/go/go/types/predicates.go
+++ b/libgo/go/go/types/predicates.go
@@ -6,84 +6,100 @@
package types
-import (
- "go/token"
-)
-
-// isNamed reports whether typ has a name.
-// isNamed may be called with types that are not fully set up.
-func isNamed(typ Type) bool {
- switch typ.(type) {
- case *Basic, *Named, *_TypeParam, *instance:
- return true
- }
- return false
+import "go/token"
+
+// The isX predicates below report whether t is an X.
+// If t is a type parameter the result is false; i.e.,
+// these predicates don't look inside a type parameter.
+
+func isBoolean(t Type) bool { return isBasic(t, IsBoolean) }
+func isInteger(t Type) bool { return isBasic(t, IsInteger) }
+func isUnsigned(t Type) bool { return isBasic(t, IsUnsigned) }
+func isFloat(t Type) bool { return isBasic(t, IsFloat) }
+func isComplex(t Type) bool { return isBasic(t, IsComplex) }
+func isNumeric(t Type) bool { return isBasic(t, IsNumeric) }
+func isString(t Type) bool { return isBasic(t, IsString) }
+func isIntegerOrFloat(t Type) bool { return isBasic(t, IsInteger|IsFloat) }
+func isConstType(t Type) bool { return isBasic(t, IsConstType) }
+
+// isBasic reports whether under(t) is a basic type with the specified info.
+// If t is a type parameter the result is false; i.e.,
+// isBasic does not look inside a type parameter.
+func isBasic(t Type, info BasicInfo) bool {
+ u, _ := under(t).(*Basic)
+ return u != nil && u.info&info != 0
}
-// isGeneric reports whether a type is a generic, uninstantiated type (generic
-// signatures are not included).
-func isGeneric(typ Type) bool {
- // A parameterized type is only instantiated if it doesn't have an instantiation already.
- named, _ := typ.(*Named)
- return named != nil && named.obj != nil && named.tparams != nil && named.targs == nil
+// The allX predicates below report whether t is an X.
+// If t is a type parameter the result is true if isX is true
+// for all specified types of the type parameter's type set.
+// allX is an optimized version of isX(structuralType(t)) (which
+// is the same as underIs(t, isX)).
+
+func allBoolean(typ Type) bool { return allBasic(typ, IsBoolean) }
+func allInteger(typ Type) bool { return allBasic(typ, IsInteger) }
+func allUnsigned(typ Type) bool { return allBasic(typ, IsUnsigned) }
+func allNumeric(typ Type) bool { return allBasic(typ, IsNumeric) }
+func allString(typ Type) bool { return allBasic(typ, IsString) }
+func allOrdered(typ Type) bool { return allBasic(typ, IsOrdered) }
+func allNumericOrString(typ Type) bool { return allBasic(typ, IsNumeric|IsString) }
+
+// allBasic reports whether under(t) is a basic type with the specified info.
+// If t is a type parameter, the result is true if isBasic(t, info) is true
+// for all specific types of the type parameter's type set.
+// allBasic(t, info) is an optimized version of isBasic(structuralType(t), info).
+func allBasic(t Type, info BasicInfo) bool {
+ if tpar, _ := t.(*TypeParam); tpar != nil {
+ return tpar.is(func(t *term) bool { return t != nil && isBasic(t.typ, info) })
+ }
+ return isBasic(t, info)
}
-func is(typ Type, what BasicInfo) bool {
- switch t := optype(typ).(type) {
- case *Basic:
- return t.info&what != 0
- case *_Sum:
- return t.is(func(typ Type) bool { return is(typ, what) })
+// hasName reports whether t has a name. This includes
+// predeclared types, defined types, and type parameters.
+// hasName may be called with types that are not fully set up.
+func hasName(t Type) bool {
+ switch t.(type) {
+ case *Basic, *Named, *TypeParam:
+ return true
}
return false
}
-func isBoolean(typ Type) bool { return is(typ, IsBoolean) }
-func isInteger(typ Type) bool { return is(typ, IsInteger) }
-func isUnsigned(typ Type) bool { return is(typ, IsUnsigned) }
-func isFloat(typ Type) bool { return is(typ, IsFloat) }
-func isComplex(typ Type) bool { return is(typ, IsComplex) }
-func isNumeric(typ Type) bool { return is(typ, IsNumeric) }
-func isString(typ Type) bool { return is(typ, IsString) }
-
-// Note that if typ is a type parameter, isInteger(typ) || isFloat(typ) does not
-// produce the expected result because a type list that contains both an integer
-// and a floating-point type is neither (all) integers, nor (all) floats.
-// Use isIntegerOrFloat instead.
-func isIntegerOrFloat(typ Type) bool { return is(typ, IsInteger|IsFloat) }
-
-// isNumericOrString is the equivalent of isIntegerOrFloat for isNumeric(typ) || isString(typ).
-func isNumericOrString(typ Type) bool { return is(typ, IsNumeric|IsString) }
-
-// isTyped reports whether typ is typed; i.e., not an untyped
+// isTyped reports whether t is typed; i.e., not an untyped
// constant or boolean. isTyped may be called with types that
// are not fully set up.
-func isTyped(typ Type) bool {
+func isTyped(t Type) bool {
// isTyped is called with types that are not fully
- // set up. Must not call asBasic()!
- // A *Named or *instance type is always typed, so
- // we only need to check if we have a true *Basic
- // type.
- t, _ := typ.(*Basic)
- return t == nil || t.info&IsUntyped == 0
+ // set up. Must not call under()!
+ b, _ := t.(*Basic)
+ return b == nil || b.info&IsUntyped == 0
}
-// isUntyped(typ) is the same as !isTyped(typ).
-func isUntyped(typ Type) bool {
- return !isTyped(typ)
+// isUntyped(t) is the same as !isTyped(t).
+func isUntyped(t Type) bool {
+ return !isTyped(t)
}
-func isOrdered(typ Type) bool { return is(typ, IsOrdered) }
+// IsInterface reports whether t is an interface type.
+func IsInterface(t Type) bool {
+ _, ok := under(t).(*Interface)
+ return ok
+}
-func isConstType(typ Type) bool {
- // Type parameters are never const types.
- t, _ := under(typ).(*Basic)
- return t != nil && t.info&IsConstType != 0
+// isTypeParam reports whether t is a type parameter.
+func isTypeParam(t Type) bool {
+ _, ok := t.(*TypeParam)
+ return ok
}
-// IsInterface reports whether typ is an interface type.
-func IsInterface(typ Type) bool {
- return asInterface(typ) != nil
+// isGeneric reports whether a type is a generic, uninstantiated type
+// (generic signatures are not included).
+// TODO(gri) should we include signatures or assert that they are not present?
+func isGeneric(t Type) bool {
+ // A parameterized type is only generic if it doesn't have an instantiation already.
+ named, _ := t.(*Named)
+ return named != nil && named.obj != nil && named.targs == nil && named.TypeParams() != nil
}
// Comparable reports whether values of type T are comparable.
@@ -100,24 +116,12 @@ func comparable(T Type, seen map[Type]bool) bool {
}
seen[T] = true
- // If T is a type parameter not constrained by any type
- // list (i.e., it's underlying type is the top type),
- // T is comparable if it has the == method. Otherwise,
- // the underlying type "wins". For instance
- //
- // interface{ comparable; type []byte }
- //
- // is not comparable because []byte is not comparable.
- if t := asTypeParam(T); t != nil && optype(t) == theTop {
- return t.Bound()._IsComparable()
- }
-
- switch t := optype(T).(type) {
+ switch t := under(T).(type) {
case *Basic:
// assume invalid types to be comparable
// to avoid follow-up errors
return t.kind != UntypedNil
- case *Pointer, *Interface, *Chan:
+ case *Pointer, *Chan:
return true
case *Struct:
for _, f := range t.fields {
@@ -128,42 +132,27 @@ func comparable(T Type, seen map[Type]bool) bool {
return true
case *Array:
return comparable(t.elem, seen)
- case *_Sum:
- pred := func(t Type) bool {
- return comparable(t, seen)
- }
- return t.is(pred)
- case *_TypeParam:
- return t.Bound()._IsComparable()
+ case *Interface:
+ return !isTypeParam(T) || t.typeSet().IsComparable(seen)
}
return false
}
-// hasNil reports whether a type includes the nil value.
-func hasNil(typ Type) bool {
- switch t := optype(typ).(type) {
+// hasNil reports whether type t includes the nil value.
+func hasNil(t Type) bool {
+ switch u := under(t).(type) {
case *Basic:
- return t.kind == UnsafePointer
- case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
+ return u.kind == UnsafePointer
+ case *Slice, *Pointer, *Signature, *Map, *Chan:
return true
- case *_Sum:
- return t.is(hasNil)
+ case *Interface:
+ return !isTypeParam(t) || u.typeSet().underIs(func(u Type) bool {
+ return u != nil && hasNil(u)
+ })
}
return false
}
-// identical reports whether x and y are identical types.
-// Receivers of Signature types are ignored.
-func (check *Checker) identical(x, y Type) bool {
- return check.identical0(x, y, true, nil)
-}
-
-// identicalIgnoreTags reports whether x and y are identical types if tags are ignored.
-// Receivers of Signature types are ignored.
-func (check *Checker) identicalIgnoreTags(x, y Type) bool {
- return check.identical0(x, y, false, nil)
-}
-
// An ifacePair is a node in a stack of interface type pairs compared for identity.
type ifacePair struct {
x, y *Interface
@@ -175,11 +164,7 @@ func (p *ifacePair) identical(q *ifacePair) bool {
}
// For changes to this code the corresponding changes should be made to unifier.nify.
-func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
- // types must be expanded for comparison
- x = expandf(x)
- y = expandf(y)
-
+func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
if x == y {
return true
}
@@ -199,13 +184,13 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
if y, ok := y.(*Array); ok {
// If one or both array lengths are unknown (< 0) due to some error,
// assume they are the same to avoid spurious follow-on errors.
- return (x.len < 0 || y.len < 0 || x.len == y.len) && check.identical0(x.elem, y.elem, cmpTags, p)
+ return (x.len < 0 || y.len < 0 || x.len == y.len) && identical(x.elem, y.elem, cmpTags, p)
}
case *Slice:
// Two slice types are identical if they have identical element types.
if y, ok := y.(*Slice); ok {
- return check.identical0(x.elem, y.elem, cmpTags, p)
+ return identical(x.elem, y.elem, cmpTags, p)
}
case *Struct:
@@ -220,7 +205,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
if f.embedded != g.embedded ||
cmpTags && x.Tag(i) != y.Tag(i) ||
!f.sameId(g.pkg, g.name) ||
- !check.identical0(f.typ, g.typ, cmpTags, p) {
+ !identical(f.typ, g.typ, cmpTags, p) {
return false
}
}
@@ -231,7 +216,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
case *Pointer:
// Two pointer types are identical if they have identical base types.
if y, ok := y.(*Pointer); ok {
- return check.identical0(x.base, y.base, cmpTags, p)
+ return identical(x.base, y.base, cmpTags, p)
}
case *Tuple:
@@ -242,7 +227,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
if x != nil {
for i, v := range x.vars {
w := y.vars[i]
- if !check.identical0(v.typ, w.typ, cmpTags, p) {
+ if !identical(v.typ, w.typ, cmpTags, p) {
return false
}
}
@@ -252,57 +237,82 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
}
case *Signature:
- // Two function types are identical if they have the same number of parameters
- // and result values, corresponding parameter and result types are identical,
- // and either both functions are variadic or neither is. Parameter and result
- // names are not required to match.
- // Generic functions must also have matching type parameter lists, but for the
- // parameter names.
- if y, ok := y.(*Signature); ok {
- return x.variadic == y.variadic &&
- check.identicalTParams(x.tparams, y.tparams, cmpTags, p) &&
- check.identical0(x.params, y.params, cmpTags, p) &&
- check.identical0(x.results, y.results, cmpTags, p)
+ y, _ := y.(*Signature)
+ if y == nil {
+ return false
}
- case *_Sum:
- // Two sum types are identical if they contain the same types.
- // (Sum types always consist of at least two types. Also, the
- // the set (list) of types in a sum type consists of unique
- // types - each type appears exactly once. Thus, two sum types
- // must contain the same number of types to have chance of
- // being equal.
- if y, ok := y.(*_Sum); ok && len(x.types) == len(y.types) {
- // Every type in x.types must be in y.types.
- // Quadratic algorithm, but probably good enough for now.
- // TODO(gri) we need a fast quick type ID/hash for all types.
- L:
- for _, x := range x.types {
- for _, y := range y.types {
- if Identical(x, y) {
- continue L // x is in y.types
- }
+ // Two function types are identical if they have the same number of
+ // parameters and result values, corresponding parameter and result types
+ // are identical, and either both functions are variadic or neither is.
+ // Parameter and result names are not required to match, and type
+ // parameters are considered identical modulo renaming.
+
+ if x.TypeParams().Len() != y.TypeParams().Len() {
+ return false
+ }
+
+ // In the case of generic signatures, we will substitute in yparams and
+ // yresults.
+ yparams := y.params
+ yresults := y.results
+
+ if x.TypeParams().Len() > 0 {
+ // We must ignore type parameter names when comparing x and y. The
+ // easiest way to do this is to substitute x's type parameters for y's.
+ xtparams := x.TypeParams().list()
+ ytparams := y.TypeParams().list()
+
+ var targs []Type
+ for i := range xtparams {
+ targs = append(targs, x.TypeParams().At(i))
+ }
+ smap := makeSubstMap(ytparams, targs)
+
+ var check *Checker // ok to call subst on a nil *Checker
+
+ // Constraints must be pair-wise identical, after substitution.
+ for i, xtparam := range xtparams {
+ ybound := check.subst(token.NoPos, ytparams[i].bound, smap, nil)
+ if !identical(xtparam.bound, ybound, cmpTags, p) {
+ return false
}
- return false // x is not in y.types
}
- return true
+
+ yparams = check.subst(token.NoPos, y.params, smap, nil).(*Tuple)
+ yresults = check.subst(token.NoPos, y.results, smap, nil).(*Tuple)
+ }
+
+ return x.variadic == y.variadic &&
+ identical(x.params, yparams, cmpTags, p) &&
+ identical(x.results, yresults, cmpTags, p)
+
+ case *Union:
+ if y, _ := y.(*Union); y != nil {
+ // TODO(rfindley): can this be reached during type checking? If so,
+ // consider passing a type set map.
+ unionSets := make(map[*Union]*_TypeSet)
+ xset := computeUnionTypeSet(nil, unionSets, token.NoPos, x)
+ yset := computeUnionTypeSet(nil, unionSets, token.NoPos, y)
+ return xset.terms.equal(yset.terms)
}
case *Interface:
+ // Two interface types are identical if they describe the same type sets.
+ // With the existing implementation restriction, this simplifies to:
+ //
// Two interface types are identical if they have the same set of methods with
- // the same names and identical function types. Lower-case method names from
- // different packages are always different. The order of the methods is irrelevant.
+ // the same names and identical function types, and if any type restrictions
+ // are the same. Lower-case method names from different packages are always
+ // different. The order of the methods is irrelevant.
if y, ok := y.(*Interface); ok {
- // If identical0 is called (indirectly) via an external API entry point
- // (such as Identical, IdenticalIgnoreTags, etc.), check is nil. But in
- // that case, interfaces are expected to be complete and lazy completion
- // here is not needed.
- if check != nil {
- check.completeInterface(token.NoPos, x)
- check.completeInterface(token.NoPos, y)
+ xset := x.typeSet()
+ yset := y.typeSet()
+ if !xset.terms.equal(yset.terms) {
+ return false
}
- a := x.allMethods
- b := y.allMethods
+ a := xset.methods
+ b := yset.methods
if len(a) == len(b) {
// Interface types are the only types where cycles can occur
// that are not "terminated" via named types; and such cycles
@@ -339,7 +349,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
}
for i, f := range a {
g := b[i]
- if f.Id() != g.Id() || !check.identical0(f.typ, g.typ, cmpTags, q) {
+ if f.Id() != g.Id() || !identical(f.typ, g.typ, cmpTags, q) {
return false
}
}
@@ -350,37 +360,50 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
case *Map:
// Two map types are identical if they have identical key and value types.
if y, ok := y.(*Map); ok {
- return check.identical0(x.key, y.key, cmpTags, p) && check.identical0(x.elem, y.elem, cmpTags, p)
+ return identical(x.key, y.key, cmpTags, p) && identical(x.elem, y.elem, cmpTags, p)
}
case *Chan:
// Two channel types are identical if they have identical value types
// and the same direction.
if y, ok := y.(*Chan); ok {
- return x.dir == y.dir && check.identical0(x.elem, y.elem, cmpTags, p)
+ return x.dir == y.dir && identical(x.elem, y.elem, cmpTags, p)
}
case *Named:
// Two named types are identical if their type names originate
// in the same type declaration.
if y, ok := y.(*Named); ok {
+ xargs := x.TypeArgs().list()
+ yargs := y.TypeArgs().list()
+
+ if len(xargs) != len(yargs) {
+ return false
+ }
+
+ if len(xargs) > 0 {
+ // Instances are identical if their original type and type arguments
+ // are identical.
+ if !Identical(x.orig, y.orig) {
+ return false
+ }
+ for i, xa := range xargs {
+ if !Identical(xa, yargs[i]) {
+ return false
+ }
+ }
+ return true
+ }
+
// TODO(gri) Why is x == y not sufficient? And if it is,
// we can just return false here because x == y
// is caught in the very beginning of this function.
return x.obj == y.obj
}
- case *_TypeParam:
+ case *TypeParam:
// nothing to do (x and y being equal is caught in the very beginning of this function)
- // case *instance:
- // unreachable since types are expanded
-
- case *bottom, *top:
- // Either both types are theBottom, or both are theTop in which
- // case the initial x == y check will have caught them. Otherwise
- // they are not identical.
-
case nil:
// avoid a crash in case of nil type
@@ -391,25 +414,28 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
return false
}
-func (check *Checker) identicalTParams(x, y []*TypeName, cmpTags bool, p *ifacePair) bool {
- if len(x) != len(y) {
+// identicalInstance reports if two type instantiations are identical.
+// Instantiations are identical if their origin and type arguments are
+// identical.
+func identicalInstance(xorig Type, xargs []Type, yorig Type, yargs []Type) bool {
+ if len(xargs) != len(yargs) {
return false
}
- for i, x := range x {
- y := y[i]
- if !check.identical0(x.typ.(*_TypeParam).bound, y.typ.(*_TypeParam).bound, cmpTags, p) {
+
+ for i, xa := range xargs {
+ if !Identical(xa, yargs[i]) {
return false
}
}
- return true
+
+ return Identical(xorig, yorig)
}
// Default returns the default "typed" type for an "untyped" type;
// it returns the incoming type for all other types. The default type
// for untyped nil is untyped nil.
-//
-func Default(typ Type) Type {
- if t, ok := typ.(*Basic); ok {
+func Default(t Type) Type {
+ if t, ok := t.(*Basic); ok {
switch t.kind {
case UntypedBool:
return Typ[Bool]
@@ -425,5 +451,5 @@ func Default(typ Type) Type {
return Typ[String]
}
}
- return typ
+ return t
}
diff --git a/libgo/go/go/types/resolver.go b/libgo/go/go/types/resolver.go
index 4892218..9edf41b 100644
--- a/libgo/go/go/types/resolver.go
+++ b/libgo/go/go/types/resolver.go
@@ -309,20 +309,24 @@ func (check *Checker) collectObjects() {
check.dotImportMap = make(map[dotImportKey]*PkgName)
}
// merge imported scope with file scope
- for _, obj := range imp.scope.elems {
+ for name, obj := range imp.scope.elems {
+ // Note: Avoid eager resolve(name, obj) here, so we only
+ // resolve dot-imported objects as needed.
+
// A package scope may contain non-exported objects,
// do not import them!
- if obj.Exported() {
+ if token.IsExported(name) {
// declare dot-imported object
// (Do not use check.declare because it modifies the object
// via Object.setScopePos, which leads to a race condition;
// the object may be imported into more than one file scope
// concurrently. See issue #32154.)
- if alt := fileScope.Insert(obj); alt != nil {
- check.errorf(d.spec.Name, _DuplicateDecl, "%s redeclared in this block", obj.Name())
+ if alt := fileScope.Lookup(name); alt != nil {
+ check.errorf(d.spec.Name, _DuplicateDecl, "%s redeclared in this block", alt.Name())
check.reportAltDecl(alt)
} else {
- check.dotImportMap[dotImportKey{fileScope, obj}] = pkgName
+ fileScope.insert(name, obj)
+ check.dotImportMap[dotImportKey{fileScope, name}] = pkgName
}
}
}
@@ -377,12 +381,15 @@ func (check *Checker) collectObjects() {
check.declarePkgObj(name, obj, di)
}
case typeDecl:
+ if d.spec.TypeParams.NumFields() != 0 && !check.allowVersion(pkg, 1, 18) {
+ check.softErrorf(d.spec.TypeParams.List[0], _UnsupportedFeature, "type parameters require go1.18 or later")
+ }
obj := NewTypeName(d.spec.Name.Pos(), pkg, d.spec.Name.Name, nil)
check.declarePkgObj(d.spec.Name, obj, &declInfo{file: fileScope, tdecl: d.spec})
case funcDecl:
- info := &declInfo{file: fileScope, fdecl: d.decl}
name := d.decl.Name.Name
obj := NewFunc(d.decl.Name.Pos(), pkg, name, nil)
+ hasTParamError := false // avoid duplicate type parameter errors
if d.decl.Recv.NumFields() == 0 {
// regular function
if d.decl.Recv != nil {
@@ -394,8 +401,9 @@ func (check *Checker) collectObjects() {
if name == "main" {
code = _InvalidMainDecl
}
- if tparams := typeparams.Get(d.decl.Type); tparams != nil {
- check.softErrorf(tparams, code, "func %s must have no type parameters", name)
+ if d.decl.Type.TypeParams.NumFields() != 0 {
+ check.softErrorf(d.decl.Type.TypeParams.List[0], code, "func %s must have no type parameters", name)
+ hasTParamError = true
}
if t := d.decl.Type; t.Params.NumFields() != 0 || t.Results != nil {
// TODO(rFindley) Should this be a hard error?
@@ -431,6 +439,10 @@ func (check *Checker) collectObjects() {
}
check.recordDef(d.decl.Name, obj)
}
+ if d.decl.Type.TypeParams.NumFields() != 0 && !check.allowVersion(pkg, 1, 18) && !hasTParamError {
+ check.softErrorf(d.decl.Type.TypeParams.List[0], _UnsupportedFeature, "type parameters require go1.18 or later")
+ }
+ info := &declInfo{file: fileScope, fdecl: d.decl}
// 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
@@ -443,8 +455,9 @@ func (check *Checker) collectObjects() {
// verify that objects in package and file scopes have different names
for _, scope := range fileScopes {
- for _, obj := range scope.elems {
- if alt := pkg.scope.Lookup(obj.Name()); alt != nil {
+ for name, obj := range scope.elems {
+ if alt := pkg.scope.Lookup(name); alt != nil {
+ obj = resolve(name, obj)
if pkg, ok := obj.(*PkgName); ok {
check.errorf(alt, _DuplicateDecl, "%s already declared through import of %s", alt.Name(), pkg.Imported())
check.reportAltDecl(pkg)
@@ -470,7 +483,7 @@ func (check *Checker) collectObjects() {
// Determine the receiver base type and associate m with it.
ptr, base := check.resolveBaseTypeName(m.ptr, m.recv)
if base != nil {
- m.obj.hasPtrRecv = ptr
+ m.obj.hasPtrRecv_ = ptr
check.methods[base] = append(check.methods[base], m.obj)
}
}
@@ -499,10 +512,12 @@ L: // unpack receiver type
}
// unpack type parameters, if any
- if ptyp, _ := rtyp.(*ast.IndexExpr); ptyp != nil {
- rtyp = ptyp.X
+ switch rtyp.(type) {
+ case *ast.IndexExpr, *ast.IndexListExpr:
+ ix := typeparams.UnpackIndexExpr(rtyp)
+ rtyp = ix.X
if unpackParams {
- for _, arg := range typeparams.UnpackExpr(ptyp.Index) {
+ for _, arg := range ix.Indices {
var par *ast.Ident
switch arg := arg.(type) {
case *ast.Ident:
@@ -510,9 +525,9 @@ L: // unpack receiver type
case *ast.BadExpr:
// ignore - error already reported by parser
case nil:
- check.invalidAST(ptyp, "parameterized receiver contains nil parameters")
+ check.invalidAST(ix.Orig, "parameterized receiver contains nil parameters")
default:
- check.errorf(arg, _Todo, "receiver type parameter %s must be an identifier", arg)
+ check.errorf(arg, _BadDecl, "receiver type parameter %s must be an identifier", arg)
}
if par == nil {
par = &ast.Ident{NamePos: arg.Pos(), Name: "_"}
@@ -614,27 +629,33 @@ func (check *Checker) packageObjects() {
}
}
- // We process non-alias declarations first, in order to avoid situations where
- // the type of an alias declaration is needed before it is available. In general
- // this is still not enough, as it is possible to create sufficiently convoluted
- // recursive type definitions that will cause a type alias to be needed before it
- // is available (see issue #25838 for examples).
- // As an aside, the cmd/compiler suffers from the same problem (#25838).
+ // We process non-alias type declarations first, followed by alias declarations,
+ // and then everything else. This appears to avoid most situations where the type
+ // of an alias is needed before it is available.
+ // There may still be cases where this is not good enough (see also issue #25838).
+ // In those cases Checker.ident will report an error ("invalid use of type alias").
var aliasList []*TypeName
- // phase 1
+ var othersList []Object // everything that's not a type
+ // phase 1: non-alias type declarations
for _, obj := range objList {
- // If we have a type alias, collect it for the 2nd phase.
- if tname, _ := obj.(*TypeName); tname != nil && check.objMap[tname].tdecl.Assign.IsValid() {
- aliasList = append(aliasList, tname)
- continue
+ if tname, _ := obj.(*TypeName); tname != nil {
+ if check.objMap[tname].tdecl.Assign.IsValid() {
+ aliasList = append(aliasList, tname)
+ } else {
+ check.objDecl(obj, nil)
+ }
+ } else {
+ othersList = append(othersList, obj)
}
-
- check.objDecl(obj, nil)
}
- // phase 2
+ // phase 2: alias type declarations
for _, obj := range aliasList {
check.objDecl(obj, nil)
}
+ // phase 3: all other declarations
+ for _, obj := range othersList {
+ check.objDecl(obj, nil)
+ }
// At this point we may have a non-empty check.methods map; this means that not all
// entries were deleted at the end of typeDecl because the respective receiver base
diff --git a/libgo/go/go/types/sanitize.go b/libgo/go/go/types/sanitize.go
deleted file mode 100644
index 727ec17..0000000
--- a/libgo/go/go/types/sanitize.go
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2020 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.
-
-package types
-
-// sanitizeInfo walks the types contained in info to ensure that all instances
-// are expanded.
-//
-// This includes some objects that may be shared across concurrent
-// type-checking passes (such as those in the universe scope), so we are
-// careful here not to write types that are already sanitized. This avoids a
-// data race as any shared types should already be sanitized.
-func sanitizeInfo(info *Info) {
- var s sanitizer = make(map[Type]Type)
-
- // Note: Some map entries are not references.
- // If modified, they must be assigned back.
-
- for e, tv := range info.Types {
- if typ := s.typ(tv.Type); typ != tv.Type {
- tv.Type = typ
- info.Types[e] = tv
- }
- }
-
- inferred := getInferred(info)
- for e, inf := range inferred {
- changed := false
- for i, targ := range inf.Targs {
- if typ := s.typ(targ); typ != targ {
- inf.Targs[i] = typ
- changed = true
- }
- }
- if typ := s.typ(inf.Sig); typ != inf.Sig {
- inf.Sig = typ.(*Signature)
- changed = true
- }
- if changed {
- inferred[e] = inf
- }
- }
-
- for _, obj := range info.Defs {
- if obj != nil {
- if typ := s.typ(obj.Type()); typ != obj.Type() {
- obj.setType(typ)
- }
- }
- }
-
- for _, obj := range info.Uses {
- if obj != nil {
- if typ := s.typ(obj.Type()); typ != obj.Type() {
- obj.setType(typ)
- }
- }
- }
-
- // TODO(gri) sanitize as needed
- // - info.Implicits
- // - info.Selections
- // - info.Scopes
- // - info.InitOrder
-}
-
-type sanitizer map[Type]Type
-
-func (s sanitizer) typ(typ Type) Type {
- if typ == nil {
- return nil
- }
-
- if t, found := s[typ]; found {
- return t
- }
- s[typ] = typ
-
- switch t := typ.(type) {
- case *Basic, *bottom, *top:
- // nothing to do
-
- case *Array:
- if elem := s.typ(t.elem); elem != t.elem {
- t.elem = elem
- }
-
- case *Slice:
- if elem := s.typ(t.elem); elem != t.elem {
- t.elem = elem
- }
-
- case *Struct:
- s.varList(t.fields)
-
- case *Pointer:
- if base := s.typ(t.base); base != t.base {
- t.base = base
- }
-
- case *Tuple:
- s.tuple(t)
-
- case *Signature:
- s.var_(t.recv)
- s.tuple(t.params)
- s.tuple(t.results)
-
- case *_Sum:
- s.typeList(t.types)
-
- case *Interface:
- s.funcList(t.methods)
- if types := s.typ(t.types); types != t.types {
- t.types = types
- }
- s.typeList(t.embeddeds)
- s.funcList(t.allMethods)
- if allTypes := s.typ(t.allTypes); allTypes != t.allTypes {
- t.allTypes = allTypes
- }
-
- case *Map:
- if key := s.typ(t.key); key != t.key {
- t.key = key
- }
- if elem := s.typ(t.elem); elem != t.elem {
- t.elem = elem
- }
-
- case *Chan:
- if elem := s.typ(t.elem); elem != t.elem {
- t.elem = elem
- }
-
- case *Named:
- if debug && t.check != nil {
- panic("internal error: Named.check != nil")
- }
- if orig := s.typ(t.orig); orig != t.orig {
- t.orig = orig
- }
- if under := s.typ(t.underlying); under != t.underlying {
- t.underlying = under
- }
- s.typeList(t.targs)
- s.funcList(t.methods)
-
- case *_TypeParam:
- if bound := s.typ(t.bound); bound != t.bound {
- t.bound = bound
- }
-
- case *instance:
- typ = t.expand()
- s[t] = typ
-
- default:
- panic("unimplemented")
- }
-
- return typ
-}
-
-func (s sanitizer) var_(v *Var) {
- if v != nil {
- if typ := s.typ(v.typ); typ != v.typ {
- v.typ = typ
- }
- }
-}
-
-func (s sanitizer) varList(list []*Var) {
- for _, v := range list {
- s.var_(v)
- }
-}
-
-func (s sanitizer) tuple(t *Tuple) {
- if t != nil {
- s.varList(t.vars)
- }
-}
-
-func (s sanitizer) func_(f *Func) {
- if f != nil {
- if typ := s.typ(f.typ); typ != f.typ {
- f.typ = typ
- }
- }
-}
-
-func (s sanitizer) funcList(list []*Func) {
- for _, f := range list {
- s.func_(f)
- }
-}
-
-func (s sanitizer) typeList(list []Type) {
- for i, t := range list {
- if typ := s.typ(t); typ != t {
- list[i] = typ
- }
- }
-}
diff --git a/libgo/go/go/types/scope.go b/libgo/go/go/types/scope.go
index 26c28d1..010727e 100644
--- a/libgo/go/go/types/scope.go
+++ b/libgo/go/go/types/scope.go
@@ -13,6 +13,7 @@ import (
"io"
"sort"
"strings"
+ "sync"
)
// A Scope maintains a set of objects and links to its containing
@@ -22,6 +23,7 @@ import (
type Scope struct {
parent *Scope
children []*Scope
+ number int // parent.children[number-1] is this scope; 0 if there is no parent
elems map[string]Object // lazily allocated
pos, end token.Pos // scope extent; may be invalid
comment string // for debugging only
@@ -31,10 +33,11 @@ type Scope struct {
// NewScope returns a new, empty scope contained in the given parent
// scope, if any. The comment is for debugging only.
func NewScope(parent *Scope, pos, end token.Pos, comment string) *Scope {
- s := &Scope{parent, nil, nil, pos, end, comment, false}
+ s := &Scope{parent, nil, 0, nil, pos, end, comment, false}
// don't add children to Universe scope!
if parent != nil && parent != Universe {
parent.children = append(parent.children, s)
+ s.number = len(parent.children)
}
return s
}
@@ -66,7 +69,7 @@ func (s *Scope) Child(i int) *Scope { return s.children[i] }
// Lookup returns the object in scope s with the given name if such an
// object exists; otherwise the result is nil.
func (s *Scope) Lookup(name string) Object {
- return s.elems[name]
+ return resolve(name, s.elems[name])
}
// LookupParent follows the parent chain of scopes starting with s until
@@ -81,7 +84,7 @@ func (s *Scope) Lookup(name string) Object {
// whose scope is the scope of the package that exported them.
func (s *Scope) LookupParent(name string, pos token.Pos) (*Scope, Object) {
for ; s != nil; s = s.parent {
- if obj := s.elems[name]; obj != nil && (!pos.IsValid() || obj.scopePos() <= pos) {
+ if obj := s.Lookup(name); obj != nil && (!pos.IsValid() || obj.scopePos() <= pos) {
return s, obj
}
}
@@ -95,19 +98,38 @@ func (s *Scope) LookupParent(name string, pos token.Pos) (*Scope, Object) {
// if not already set, and returns nil.
func (s *Scope) Insert(obj Object) Object {
name := obj.Name()
- if alt := s.elems[name]; alt != nil {
+ if alt := s.Lookup(name); alt != nil {
return alt
}
- if s.elems == nil {
- s.elems = make(map[string]Object)
- }
- s.elems[name] = obj
+ s.insert(name, obj)
if obj.Parent() == nil {
obj.setParent(s)
}
return nil
}
+// _InsertLazy is like Insert, but allows deferring construction of the
+// inserted object until it's accessed with Lookup. The Object
+// returned by resolve must have the same name as given to _InsertLazy.
+// If s already contains an alternative object with the same name,
+// _InsertLazy leaves s unchanged and returns false. Otherwise it
+// records the binding and returns true. The object's parent scope
+// will be set to s after resolve is called.
+func (s *Scope) _InsertLazy(name string, resolve func() Object) bool {
+ if s.elems[name] != nil {
+ return false
+ }
+ s.insert(name, &lazyObject{parent: s, resolve: resolve})
+ return true
+}
+
+func (s *Scope) insert(name string, obj Object) {
+ if s.elems == nil {
+ s.elems = make(map[string]Object)
+ }
+ s.elems[name] = obj
+}
+
// squash merges s with its parent scope p by adding all
// objects of s to p, adding all children of s to the
// children of p, and removing s from p's children.
@@ -117,7 +139,8 @@ func (s *Scope) Insert(obj Object) Object {
func (s *Scope) squash(err func(obj, alt Object)) {
p := s.parent
assert(p != nil)
- for _, obj := range s.elems {
+ for name, obj := range s.elems {
+ obj = resolve(name, obj)
obj.setParent(nil)
if alt := p.Insert(obj); alt != nil {
err(obj, alt)
@@ -196,7 +219,7 @@ func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) {
indn1 := indn + ind
for _, name := range s.Names() {
- fmt.Fprintf(w, "%s%s\n", indn1, s.elems[name])
+ fmt.Fprintf(w, "%s%s\n", indn1, s.Lookup(name))
}
if recurse {
@@ -214,3 +237,57 @@ func (s *Scope) String() string {
s.WriteTo(&buf, 0, false)
return buf.String()
}
+
+// A lazyObject represents an imported Object that has not been fully
+// resolved yet by its importer.
+type lazyObject struct {
+ parent *Scope
+ resolve func() Object
+ obj Object
+ once sync.Once
+}
+
+// resolve returns the Object represented by obj, resolving lazy
+// objects as appropriate.
+func resolve(name string, obj Object) Object {
+ if lazy, ok := obj.(*lazyObject); ok {
+ lazy.once.Do(func() {
+ obj := lazy.resolve()
+
+ if _, ok := obj.(*lazyObject); ok {
+ panic("recursive lazy object")
+ }
+ if obj.Name() != name {
+ panic("lazy object has unexpected name")
+ }
+
+ if obj.Parent() == nil {
+ obj.setParent(lazy.parent)
+ }
+ lazy.obj = obj
+ })
+
+ obj = lazy.obj
+ }
+ return obj
+}
+
+// stub implementations so *lazyObject implements Object and we can
+// store them directly into Scope.elems.
+func (*lazyObject) Parent() *Scope { panic("unreachable") }
+func (*lazyObject) Pos() token.Pos { panic("unreachable") }
+func (*lazyObject) Pkg() *Package { panic("unreachable") }
+func (*lazyObject) Name() string { panic("unreachable") }
+func (*lazyObject) Type() Type { panic("unreachable") }
+func (*lazyObject) Exported() bool { panic("unreachable") }
+func (*lazyObject) Id() string { panic("unreachable") }
+func (*lazyObject) String() string { panic("unreachable") }
+func (*lazyObject) order() uint32 { panic("unreachable") }
+func (*lazyObject) color() color { panic("unreachable") }
+func (*lazyObject) setType(Type) { panic("unreachable") }
+func (*lazyObject) setOrder(uint32) { panic("unreachable") }
+func (*lazyObject) setColor(color color) { panic("unreachable") }
+func (*lazyObject) setParent(*Scope) { panic("unreachable") }
+func (*lazyObject) sameId(pkg *Package, name string) bool { panic("unreachable") }
+func (*lazyObject) scopePos() token.Pos { panic("unreachable") }
+func (*lazyObject) setScopePos(pos token.Pos) { panic("unreachable") }
diff --git a/libgo/go/go/types/self_test.go b/libgo/go/go/types/self_test.go
index 55a8312..f5fc321 100644
--- a/libgo/go/go/types/self_test.go
+++ b/libgo/go/go/types/self_test.go
@@ -29,12 +29,7 @@ func TestSelf(t *testing.T) {
conf := Config{Importer: importer.Default()}
_, err = conf.Check("go/types", fset, 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)
}
}
@@ -43,6 +38,7 @@ func BenchmarkCheck(b *testing.B) {
"net/http",
"go/parser",
"go/constant",
+ "runtime",
filepath.Join("go", "internal", "gcimporter"),
} {
b.Run(path.Base(p), func(b *testing.B) {
diff --git a/libgo/go/go/types/signature.go b/libgo/go/go/types/signature.go
new file mode 100644
index 0000000..8f89e93
--- /dev/null
+++ b/libgo/go/go/types/signature.go
@@ -0,0 +1,320 @@
+// 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.
+
+package types
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+// ----------------------------------------------------------------------------
+// API
+
+// A Signature represents a (non-builtin) function or method type.
+// The receiver is ignored when comparing signatures for identity.
+type Signature struct {
+ // We need to keep the scope in Signature (rather than passing it around
+ // and store it in the Func Object) because when type-checking a function
+ // literal we call the general type checker which returns a general Type.
+ // We then unpack the *Signature and use the scope for the literal body.
+ rparams *TypeParamList // receiver type parameters from left to right, or nil
+ tparams *TypeParamList // type parameters from left to right, or nil
+ scope *Scope // function scope for package-local and non-instantiated signatures; nil otherwise
+ recv *Var // nil if not a method
+ params *Tuple // (incoming) parameters from left to right; or nil
+ results *Tuple // (outgoing) results from left to right; or nil
+ variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only)
+}
+
+// NewSignature returns a new function type for the given receiver, parameters,
+// and results, either of which may be nil. If variadic is set, the function
+// is variadic, it must have at least one parameter, and the last parameter
+// must be of unnamed slice type.
+//
+// Deprecated: Use NewSignatureType instead which allows for type parameters.
+func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature {
+ return NewSignatureType(recv, nil, nil, params, results, variadic)
+}
+
+// NewSignatureType creates a new function type for the given receiver,
+// receiver type parameters, type parameters, parameters, and results. If
+// variadic is set, params must hold at least one parameter and the last
+// parameter must be of unnamed slice type. If recv is non-nil, typeParams must
+// be empty. If recvTypeParams is non-empty, recv must be non-nil.
+func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params, results *Tuple, variadic bool) *Signature {
+ if variadic {
+ n := params.Len()
+ if n == 0 {
+ panic("variadic function must have at least one parameter")
+ }
+ if _, ok := params.At(n - 1).typ.(*Slice); !ok {
+ panic("variadic parameter must be of unnamed slice type")
+ }
+ }
+ sig := &Signature{recv: recv, params: params, results: results, variadic: variadic}
+ if len(recvTypeParams) != 0 {
+ if recv == nil {
+ panic("function with receiver type parameters must have a receiver")
+ }
+ sig.rparams = bindTParams(recvTypeParams)
+ }
+ if len(typeParams) != 0 {
+ if recv != nil {
+ panic("function with type parameters cannot have a receiver")
+ }
+ sig.tparams = bindTParams(typeParams)
+ }
+ return sig
+}
+
+// Recv returns the receiver of signature s (if a method), or nil if a
+// function. It is ignored when comparing signatures for identity.
+//
+// For an abstract method, Recv returns the enclosing interface either
+// as a *Named or an *Interface. Due to embedding, an interface may
+// contain methods whose receiver type is a different interface.
+func (s *Signature) Recv() *Var { return s.recv }
+
+// TypeParams returns the type parameters of signature s, or nil.
+func (s *Signature) TypeParams() *TypeParamList { return s.tparams }
+
+// RecvTypeParams returns the receiver type parameters of signature s, or nil.
+func (s *Signature) RecvTypeParams() *TypeParamList { return s.rparams }
+
+// Params returns the parameters of signature s, or nil.
+func (s *Signature) Params() *Tuple { return s.params }
+
+// Results returns the results of signature s, or nil.
+func (s *Signature) Results() *Tuple { return s.results }
+
+// Variadic reports whether the signature s is variadic.
+func (s *Signature) Variadic() bool { return s.variadic }
+
+func (t *Signature) Underlying() Type { return t }
+func (t *Signature) String() string { return TypeString(t, nil) }
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+// funcType type-checks a function or method type.
+func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast.FuncType) {
+ check.openScope(ftyp, "function")
+ check.scope.isFunc = true
+ check.recordScope(ftyp, check.scope)
+ sig.scope = check.scope
+ defer check.closeScope()
+
+ if recvPar != nil && len(recvPar.List) > 0 {
+ // collect generic receiver type parameters, if any
+ // - a receiver type parameter is like any other type parameter, except that it is declared implicitly
+ // - the receiver specification acts as local declaration for its type parameters, which may be blank
+ _, rname, rparams := check.unpackRecv(recvPar.List[0].Type, true)
+ if len(rparams) > 0 {
+ sig.rparams = bindTParams(check.declareTypeParams(nil, rparams))
+ // Blank identifiers don't get declared, so naive type-checking of the
+ // receiver type expression would fail in Checker.collectParams below,
+ // when Checker.ident cannot resolve the _ to a type.
+ //
+ // Checker.recvTParamMap maps these blank identifiers to their type parameter
+ // types, so that they may be resolved in Checker.ident when they fail
+ // lookup in the scope.
+ for i, p := range rparams {
+ if p.Name == "_" {
+ tpar := sig.rparams.At(i)
+ if check.recvTParamMap == nil {
+ check.recvTParamMap = make(map[*ast.Ident]*TypeParam)
+ }
+ check.recvTParamMap[p] = tpar
+ }
+ }
+ // determine receiver type to get its type parameters
+ // and the respective type parameter bounds
+ var recvTParams []*TypeParam
+ if rname != nil {
+ // recv should be a Named type (otherwise an error is reported elsewhere)
+ // Also: Don't report an error via genericType since it will be reported
+ // again when we type-check the signature.
+ // TODO(gri) maybe the receiver should be marked as invalid instead?
+ if recv, _ := check.genericType(rname, nil).(*Named); recv != nil {
+ recvTParams = recv.TypeParams().list()
+ }
+ }
+ // provide type parameter bounds
+ // - only do this if we have the right number (otherwise an error is reported elsewhere)
+ if sig.RecvTypeParams().Len() == len(recvTParams) {
+ // We have a list of *TypeNames but we need a list of Types.
+ list := make([]Type, sig.RecvTypeParams().Len())
+ for i, t := range sig.RecvTypeParams().list() {
+ list[i] = t
+ check.mono.recordCanon(t, recvTParams[i])
+ }
+ smap := makeSubstMap(recvTParams, list)
+ for i, tpar := range sig.RecvTypeParams().list() {
+ bound := recvTParams[i].bound
+ // bound is (possibly) parameterized in the context of the
+ // receiver type declaration. Substitute parameters for the
+ // current context.
+ tpar.bound = check.subst(tpar.obj.pos, bound, smap, nil)
+ }
+ }
+ }
+ }
+
+ if ftyp.TypeParams != nil {
+ check.collectTypeParams(&sig.tparams, ftyp.TypeParams)
+ // Always type-check method type parameters but complain that they are not allowed.
+ // (A separate check is needed when type-checking interface method signatures because
+ // they don't have a receiver specification.)
+ if recvPar != nil {
+ check.errorf(ftyp.TypeParams, _InvalidMethodTypeParams, "methods cannot have type parameters")
+ }
+ }
+
+ // Value (non-type) parameters' scope starts in the function body. Use a temporary scope for their
+ // declarations and then squash that scope into the parent scope (and report any redeclarations at
+ // that time).
+ scope := NewScope(check.scope, token.NoPos, token.NoPos, "function body (temp. scope)")
+ recvList, _ := check.collectParams(scope, recvPar, false)
+ params, variadic := check.collectParams(scope, ftyp.Params, true)
+ results, _ := check.collectParams(scope, ftyp.Results, false)
+ scope.squash(func(obj, alt Object) {
+ check.errorf(obj, _DuplicateDecl, "%s redeclared in this block", obj.Name())
+ check.reportAltDecl(alt)
+ })
+
+ if recvPar != nil {
+ // recv parameter list present (may be empty)
+ // spec: "The receiver is specified via an extra parameter section preceding the
+ // method name. That parameter section must declare a single parameter, the receiver."
+ var recv *Var
+ switch len(recvList) {
+ case 0:
+ // error reported by resolver
+ recv = NewParam(0, nil, "", Typ[Invalid]) // ignore recv below
+ default:
+ // more than one receiver
+ check.error(recvList[len(recvList)-1], _BadRecv, "method must have exactly one receiver")
+ fallthrough // continue with first receiver
+ case 1:
+ recv = recvList[0]
+ }
+
+ // TODO(gri) We should delay rtyp expansion to when we actually need the
+ // receiver; thus all checks here should be delayed to later.
+ rtyp, _ := deref(recv.typ)
+
+ // spec: "The receiver type must be of the form T or *T where T is a type name."
+ // (ignore invalid types - error was reported before)
+ if rtyp != Typ[Invalid] {
+ var err string
+ switch T := rtyp.(type) {
+ case *Named:
+ T.resolve(check.bestContext(nil))
+ // The receiver type may be an instantiated type referred to
+ // by an alias (which cannot have receiver parameters for now).
+ if T.TypeArgs() != nil && sig.RecvTypeParams() == nil {
+ check.errorf(atPos(recv.pos), _InvalidRecv, "cannot define methods on instantiated type %s", recv.typ)
+ break
+ }
+ // spec: "The type denoted by T is called the receiver base type; it must not
+ // be a pointer or interface type and it must be declared in the same package
+ // as the method."
+ if T.obj.pkg != check.pkg {
+ err = "type not defined in this package"
+ } else {
+ // The underlying type of a receiver base type can be a type parameter;
+ // e.g. for methods with a generic receiver T[P] with type T[P any] P.
+ underIs(T, func(u Type) bool {
+ switch u := u.(type) {
+ case *Basic:
+ // unsafe.Pointer is treated like a regular pointer
+ if u.kind == UnsafePointer {
+ err = "unsafe.Pointer"
+ return false
+ }
+ case *Pointer, *Interface:
+ err = "pointer or interface type"
+ return false
+ }
+ return true
+ })
+ }
+ case *Basic:
+ err = "basic or unnamed type"
+ default:
+ check.errorf(recv, _InvalidRecv, "invalid receiver type %s", recv.typ)
+ }
+ if err != "" {
+ check.errorf(recv, _InvalidRecv, "invalid receiver type %s (%s)", recv.typ, err)
+ // ok to continue
+ }
+ }
+ sig.recv = recv
+ }
+
+ sig.params = NewTuple(params...)
+ sig.results = NewTuple(results...)
+ sig.variadic = variadic
+}
+
+// collectParams declares the parameters of list in scope and returns the corresponding
+// variable list.
+func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) {
+ if list == nil {
+ return
+ }
+
+ var named, anonymous bool
+ for i, field := range list.List {
+ ftype := field.Type
+ if t, _ := ftype.(*ast.Ellipsis); t != nil {
+ ftype = t.Elt
+ if variadicOk && i == len(list.List)-1 && len(field.Names) <= 1 {
+ variadic = true
+ } else {
+ check.softErrorf(t, _MisplacedDotDotDot, "can only use ... with final parameter in list")
+ // ignore ... and continue
+ }
+ }
+ typ := check.varType(ftype)
+ // The parser ensures that f.Tag is nil and we don't
+ // care if a constructed AST contains a non-nil tag.
+ if len(field.Names) > 0 {
+ // named parameter
+ for _, name := range field.Names {
+ if name.Name == "" {
+ check.invalidAST(name, "anonymous parameter")
+ // ok to continue
+ }
+ par := NewParam(name.Pos(), check.pkg, name.Name, typ)
+ check.declare(scope, name, par, scope.pos)
+ params = append(params, par)
+ }
+ named = true
+ } else {
+ // anonymous parameter
+ par := NewParam(ftype.Pos(), check.pkg, "", typ)
+ check.recordImplicit(field, par)
+ params = append(params, par)
+ anonymous = true
+ }
+ }
+
+ if named && anonymous {
+ check.invalidAST(list, "list contains both named and anonymous parameters")
+ // ok to continue
+ }
+
+ // For a variadic function, change the last parameter's type from T to []T.
+ // Since we type-checked T rather than ...T, we also need to retro-actively
+ // record the type for ...T.
+ if variadic {
+ last := params[len(params)-1]
+ last.typ = &Slice{elem: last.typ}
+ check.recordTypeAndValue(list.List[len(list.List)-1].Type, typexpr, last.typ, nil)
+ }
+
+ return
+}
diff --git a/libgo/go/go/types/sizeof_test.go b/libgo/go/go/types/sizeof_test.go
index 5a9d07c..bfd14a8 100644
--- a/libgo/go/go/types/sizeof_test.go
+++ b/libgo/go/go/types/sizeof_test.go
@@ -14,9 +14,9 @@ func TestSizeof(t *testing.T) {
const _64bit = ^uint(0)>>32 != 0
var tests = []struct {
- val interface{} // type as a value
- _32bit uintptr // size on 32bit platforms
- _64bit uintptr // size on 64bit platforms
+ val any // type as a value
+ _32bit uintptr // size on 32bit platforms
+ _64bit uintptr // size on 64bit platforms
}{
// Types
{Basic{}, 16, 32},
@@ -25,16 +25,14 @@ func TestSizeof(t *testing.T) {
{Struct{}, 24, 48},
{Pointer{}, 8, 16},
{Tuple{}, 12, 24},
- {Signature{}, 44, 88},
- {_Sum{}, 12, 24},
- {Interface{}, 60, 120},
+ {Signature{}, 28, 56},
+ {Union{}, 12, 24},
+ {Interface{}, 44, 88},
{Map{}, 16, 32},
{Chan{}, 12, 24},
- {Named{}, 64, 128},
- {_TypeParam{}, 28, 48},
- {instance{}, 44, 88},
- {bottom{}, 0, 0},
- {top{}, 0, 0},
+ {Named{}, 56, 104},
+ {TypeParam{}, 28, 48},
+ {term{}, 12, 24},
// Objects
{PkgName{}, 48, 88},
@@ -47,8 +45,9 @@ func TestSizeof(t *testing.T) {
{Nil{}, 40, 72},
// Misc
- {Scope{}, 40, 80},
+ {Scope{}, 44, 88},
{Package{}, 40, 80},
+ {_TypeSet{}, 28, 56},
}
for _, test := range tests {
got := reflect.TypeOf(test.val).Size()
diff --git a/libgo/go/go/types/sizes.go b/libgo/go/go/types/sizes.go
index 3b334ac..0dc0f4c 100644
--- a/libgo/go/go/types/sizes.go
+++ b/libgo/go/go/types/sizes.go
@@ -48,7 +48,7 @@ type StdSizes struct {
func (s *StdSizes) Alignof(T Type) int64 {
// For arrays and structs, alignment is defined in terms
// of alignment of the elements and fields, respectively.
- switch t := optype(T).(type) {
+ switch t := under(T).(type) {
case *Array:
// spec: "For a variable x of array type: unsafe.Alignof(x)
// is the same as unsafe.Alignof(x[0]), but at least 1."
@@ -67,12 +67,17 @@ func (s *StdSizes) Alignof(T Type) int64 {
case *Slice, *Interface:
// Multiword data structures are effectively structs
// in which each element has size WordSize.
+ // Type parameters lead to variable sizes/alignments;
+ // StdSizes.Alignof won't be called for them.
+ assert(!isTypeParam(T))
return s.WordSize
case *Basic:
// Strings are like slices and interfaces.
if t.Info()&IsString != 0 {
return s.WordSize
}
+ case *TypeParam, *Union:
+ unreachable()
}
a := s.Sizeof(T) // may be 0
// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
@@ -118,7 +123,7 @@ var basicSizes = [...]byte{
}
func (s *StdSizes) Sizeof(T Type) int64 {
- switch t := optype(T).(type) {
+ switch t := under(T).(type) {
case *Basic:
assert(isTyped(T))
k := t.kind
@@ -148,10 +153,13 @@ func (s *StdSizes) Sizeof(T Type) int64 {
}
offsets := s.Offsetsof(t.fields)
return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
- case *_Sum:
- panic("Sizeof unimplemented for type sum")
case *Interface:
+ // Type parameters lead to variable sizes/alignments;
+ // StdSizes.Sizeof won't be called for them.
+ assert(!isTypeParam(T))
return s.WordSize * 2
+ case *TypeParam, *Union:
+ unreachable()
}
return s.WordSize // catch-all
}
@@ -243,7 +251,7 @@ func (conf *Config) offsetsof(T *Struct) []int64 {
func (conf *Config) offsetof(typ Type, index []int) int64 {
var o int64
for _, i := range index {
- s := asStruct(typ)
+ s := under(typ).(*Struct)
o += conf.offsetsof(s)[i]
typ = s.fields[i].typ
}
diff --git a/libgo/go/go/types/slice.go b/libgo/go/go/types/slice.go
new file mode 100644
index 0000000..debdd81
--- /dev/null
+++ b/libgo/go/go/types/slice.go
@@ -0,0 +1,19 @@
+// Copyright 2011 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.
+
+package types
+
+// A Slice represents a slice type.
+type Slice struct {
+ elem Type
+}
+
+// NewSlice returns a new slice type for the given element type.
+func NewSlice(elem Type) *Slice { return &Slice{elem: elem} }
+
+// Elem returns the element type of slice s.
+func (s *Slice) Elem() Type { return s.elem }
+
+func (t *Slice) Underlying() Type { return t }
+func (t *Slice) String() string { return TypeString(t, nil) }
diff --git a/libgo/go/go/types/stdlib_test.go b/libgo/go/go/types/stdlib_test.go
index de30bdb..5c2bf55 100644
--- a/libgo/go/go/types/stdlib_test.go
+++ b/libgo/go/go/types/stdlib_test.go
@@ -142,8 +142,7 @@ func testTestDir(t *testing.T, path string, ignore ...string) {
// parse and type-check file
file, err := parser.ParseFile(fset, filename, nil, 0)
if err == nil {
- conf := Config{Importer: stdLibImporter}
- SetGoVersion(&conf, goVersion)
+ conf := Config{GoVersion: goVersion, Importer: stdLibImporter}
_, err = conf.Check(filename, fset, []*ast.File{file}, nil)
}
@@ -169,9 +168,11 @@ func TestStdTest(t *testing.T) {
testTestDir(t, filepath.Join(runtime.GOROOT(), "test"),
"cmplxdivide.go", // also needs file cmplxdivide1.go - ignore
"directive.go", // tests compiler rejection of bad directive placement - ignore
+ "directive2.go", // tests compiler rejection of bad directive placement - ignore
"embedfunc.go", // tests //go:embed
"embedvers.go", // tests //go:embed
"linkname2.go", // go/types doesn't check validity of //go:xxx directives
+ "linkname3.go", // go/types doesn't check validity of //go:xxx directives
)
}
@@ -197,6 +198,10 @@ func TestStdFixed(t *testing.T) {
"bug251.go", // issue #34333 which was exposed with fix for #34151
"issue42058a.go", // go/types does not have constraints on channel element size
"issue42058b.go", // go/types does not have constraints on channel element size
+ "issue48097.go", // go/types doesn't check validity of //go:xxx directives, and non-init bodyless function
+ "issue48230.go", // go/types doesn't check validity of //go:xxx directives
+ "issue49767.go", // go/types does not have constraints on channel element size
+ "issue49814.go", // go/types does not have constraints on array size
)
}
@@ -295,7 +300,7 @@ func pkgFilenames(dir string) ([]string, error) {
return filenames, nil
}
-func walkPkgDirs(dir string, pkgh func(dir string, filenames []string), errh func(args ...interface{})) time.Duration {
+func walkPkgDirs(dir string, pkgh func(dir string, filenames []string), errh func(args ...any)) time.Duration {
w := walker{time.Now(), 10 * time.Millisecond, pkgh, errh}
w.walk(dir)
return time.Since(w.start)
@@ -305,7 +310,7 @@ type walker struct {
start time.Time
dmax time.Duration
pkgh func(dir string, filenames []string)
- errh func(args ...interface{})
+ errh func(args ...any)
}
func (w *walker) walk(dir string) {
diff --git a/libgo/go/go/types/stmt.go b/libgo/go/go/types/stmt.go
index 47f6dcf..8026735 100644
--- a/libgo/go/go/types/stmt.go
+++ b/libgo/go/go/types/stmt.go
@@ -15,7 +15,7 @@ import (
func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt, iota constant.Value) {
if check.conf.IgnoreFuncBodies {
- panic("internal error: function body not ignored")
+ panic("function body not ignored")
}
if trace {
@@ -29,13 +29,13 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body
sig.scope.pos = body.Pos()
sig.scope.end = body.End()
- // save/restore current context and setup function context
+ // save/restore current environment and set up function environment
// (and use 0 indentation at function start)
- defer func(ctxt context, indent int) {
- check.context = ctxt
+ defer func(env environment, indent int) {
+ check.environment = env
check.indent = indent
- }(check.context, check.indent)
- check.context = context{
+ }(check.environment, check.indent)
+ check.environment = environment{
decl: decl,
scope: sig.scope,
iota: iota,
@@ -53,11 +53,6 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body
check.error(atPos(body.Rbrace), _MissingReturn, "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)
@@ -65,7 +60,8 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body
func (check *Checker) usage(scope *Scope) {
var unused []*Var
- for _, elem := range scope.elems {
+ for name, elem := range scope.elems {
+ elem = resolve(name, elem)
if v, _ := elem.(*Var); v != nil && !v.used {
unused = append(unused, v)
}
@@ -178,7 +174,7 @@ func (check *Checker) suspendedCall(keyword string, call *ast.CallExpr) {
var x operand
var msg string
var code errorCode
- switch check.rawExpr(&x, call, nil) {
+ switch check.rawExpr(&x, call, nil, false) {
case conversion:
msg = "requires function call, not conversion"
code = _InvalidDefer
@@ -197,7 +193,7 @@ func (check *Checker) suspendedCall(keyword string, call *ast.CallExpr) {
}
// goVal returns the Go value for val, or nil.
-func goVal(val constant.Value) interface{} {
+func goVal(val constant.Value) any {
// val should exist, but be conservative and check
if val == nil {
return nil
@@ -231,7 +227,7 @@ func goVal(val constant.Value) interface{} {
// types we need to also check the value's types (e.g., byte(1) vs myByte(1))
// when the switch expression is of interface type.
type (
- valueMap map[interface{}][]valueType // underlying Go value -> valueType
+ valueMap map[any][]valueType // underlying Go value -> valueType
valueType struct {
pos token.Pos
typ Type
@@ -264,7 +260,7 @@ L:
// look for duplicate types for a given value
// (quadratic algorithm, but these lists tend to be very short)
for _, vt := range seen[val] {
- if check.identical(v.typ, vt.typ) {
+ if Identical(v.typ, vt.typ) {
check.errorf(&v, _DuplicateCase, "duplicate case %s in expression switch", &v)
check.error(atPos(vt.pos), _DuplicateCase, "\tprevious case") // secondary error, \t indented
continue L
@@ -275,24 +271,38 @@ L:
}
}
+// isNil reports whether the expression e denotes the predeclared value nil.
+func (check *Checker) isNil(e ast.Expr) bool {
+ // The only way to express the nil value is by literally writing nil (possibly in parentheses).
+ if name, _ := unparen(e).(*ast.Ident); name != nil {
+ _, ok := check.lookup(name.Name).(*Nil)
+ return ok
+ }
+ return false
+}
+
func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]ast.Expr) (T Type) {
+ var dummy operand
L:
for _, e := range types {
- T = check.typeOrNil(e)
- if T == Typ[Invalid] {
- continue L
- }
- if T != nil {
- check.ordinaryType(e, T)
+ // The spec allows the value nil instead of a type.
+ if check.isNil(e) {
+ T = nil
+ check.expr(&dummy, e) // run e through expr so we get the usual Info recordings
+ } else {
+ T = check.varType(e)
+ if T == Typ[Invalid] {
+ continue L
+ }
}
// look for duplicate types
// (quadratic algorithm, but type switches tend to be reasonably small)
for t, other := range seen {
- if T == nil && t == nil || T != nil && t != nil && check.identical(T, t) {
+ if T == nil && t == nil || T != nil && t != nil && Identical(T, t) {
// talk about "case" rather than "type" because of nil case
Ts := "nil"
if T != nil {
- Ts = T.String()
+ Ts = TypeString(T, check.qualifier)
}
check.errorf(e, _DuplicateCase, "duplicate case %s in type switch", Ts)
check.error(other, _DuplicateCase, "\tprevious case") // secondary error, \t indented
@@ -300,13 +310,54 @@ L:
}
}
seen[T] = e
- if T != nil {
+ if T != nil && xtyp != nil {
check.typeAssertion(e, x, xtyp, T)
}
}
return
}
+// TODO(gri) Once we are certain that typeHash is correct in all situations, use this version of caseTypes instead.
+// (Currently it may be possible that different types have identical names and import paths due to ImporterFrom.)
+//
+// func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[string]ast.Expr) (T Type) {
+// var dummy operand
+// L:
+// for _, e := range types {
+// // The spec allows the value nil instead of a type.
+// var hash string
+// if check.isNil(e) {
+// check.expr(&dummy, e) // run e through expr so we get the usual Info recordings
+// T = nil
+// hash = "<nil>" // avoid collision with a type named nil
+// } else {
+// T = check.varType(e)
+// if T == Typ[Invalid] {
+// continue L
+// }
+// hash = typeHash(T, nil)
+// }
+// // look for duplicate types
+// if other := seen[hash]; other != nil {
+// // talk about "case" rather than "type" because of nil case
+// Ts := "nil"
+// if T != nil {
+// Ts = TypeString(T, check.qualifier)
+// }
+// var err error_
+// err.errorf(e, "duplicate case %s in type switch", Ts)
+// err.errorf(other, "previous case")
+// check.report(&err)
+// continue L
+// }
+// seen[hash] = e
+// if T != nil {
+// check.typeAssertion(e.Pos(), x, xtyp, T)
+// }
+// }
+// return
+// }
+
// stmt typechecks statement s.
func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
// statements must end with the same top scope as they started with
@@ -340,7 +391,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
// function and method calls and receive operations can appear
// in statement context. Such statements may be parenthesized."
var x operand
- kind := check.rawExpr(&x, s.X, nil)
+ kind := check.rawExpr(&x, s.X, nil, false)
var msg string
var code errorCode
switch x.mode {
@@ -360,25 +411,27 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
check.errorf(&x, code, "%s %s", &x, msg)
case *ast.SendStmt:
- var ch, x operand
+ var ch, val operand
check.expr(&ch, s.Chan)
- check.expr(&x, s.Value)
- if ch.mode == invalid || x.mode == invalid {
+ check.expr(&val, s.Value)
+ if ch.mode == invalid || val.mode == invalid {
return
}
-
- tch := asChan(ch.typ)
- if tch == nil {
- check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to non-chan type %s", ch.typ)
+ u := structuralType(ch.typ)
+ if u == nil {
+ check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to %s: no structural type", &ch)
return
}
-
- if tch.dir == RecvOnly {
- check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to receive-only type %s", tch)
+ uch, _ := u.(*Chan)
+ if uch == nil {
+ check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to non-channel %s", &ch)
return
}
-
- check.assignment(&x, tch.elem, "send")
+ if uch.dir == RecvOnly {
+ check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to receive-only channel %s", &ch)
+ return
+ }
+ check.assignment(&val, uch.elem, "send")
case *ast.IncDecStmt:
var op token.Token
@@ -397,7 +450,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
if x.mode == invalid {
return
}
- if !isNumeric(x.typ) {
+ if !allNumeric(x.typ) {
check.invalidOp(s.X, _NonNumericIncDec, "%s%s (non-numeric type %s)", s.X, s.Tok, x.typ)
return
}
@@ -450,27 +503,25 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
case *ast.ReturnStmt:
res := check.sig.results
- if res.Len() > 0 {
- // function returns results
- // (if one, say the first, result parameter is named, all of them are named)
- if len(s.Results) == 0 && res.vars[0].name != "" {
- // spec: "Implementation restriction: A compiler may disallow an empty expression
- // list in a "return" statement if a different entity (constant, type, or variable)
- // with the same name as a result parameter is in scope at the place of the return."
- for _, obj := range res.vars {
- if alt := check.lookup(obj.name); alt != nil && alt != obj {
- check.errorf(s, _OutOfScopeResult, "result parameter %s not in scope at return", obj.name)
- check.errorf(alt, _OutOfScopeResult, "\tinner declaration of %s", obj)
- // ok to continue
- }
+ // Return with implicit results allowed for function with named results.
+ // (If one is named, all are named.)
+ if len(s.Results) == 0 && res.Len() > 0 && res.vars[0].name != "" {
+ // spec: "Implementation restriction: A compiler may disallow an empty expression
+ // list in a "return" statement if a different entity (constant, type, or variable)
+ // with the same name as a result parameter is in scope at the place of the return."
+ for _, obj := range res.vars {
+ if alt := check.lookup(obj.name); alt != nil && alt != obj {
+ check.errorf(s, _OutOfScopeResult, "result parameter %s not in scope at return", obj.name)
+ check.errorf(alt, _OutOfScopeResult, "\tinner declaration of %s", obj)
+ // ok to continue
}
- } else {
- // return has results or result parameters are unnamed
- check.initVars(res.vars, s.Results, s.Return)
}
- } else if len(s.Results) > 0 {
- check.error(s.Results[0], _WrongResultCount, "no result values expected")
- check.use(s.Results...)
+ } else {
+ var lhs []*Var
+ if res.Len() > 0 {
+ lhs = res.vars
+ }
+ check.initVars(lhs, s.Results, s)
}
case *ast.BranchStmt:
@@ -513,7 +564,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
check.simpleStmt(s.Init)
var x operand
check.expr(&x, s.Cond)
- if x.mode != invalid && !isBoolean(x.typ) {
+ if x.mode != invalid && !allBoolean(x.typ) {
check.error(s.Cond, _InvalidCond, "non-boolean condition in if statement")
}
check.stmt(inner, s.Body)
@@ -632,12 +683,16 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
if x.mode == invalid {
return
}
- xtyp, _ := under(x.typ).(*Interface)
- if xtyp == nil {
- check.errorf(&x, _InvalidTypeSwitch, "%s is not an interface", &x)
- return
+ // TODO(gri) we may want to permit type switches on type parameter values at some point
+ var xtyp *Interface
+ if isTypeParam(x.typ) {
+ check.errorf(&x, _InvalidTypeSwitch, "cannot use type switch on type parameter value %s", &x)
+ } else {
+ xtyp, _ = under(x.typ).(*Interface)
+ if xtyp == nil {
+ check.errorf(&x, _InvalidTypeSwitch, "%s is not an interface", &x)
+ }
}
- check.ordinaryType(&x, xtyp)
check.multipleDefaults(s.Body.List)
@@ -746,7 +801,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
if s.Cond != nil {
var x operand
check.expr(&x, s.Cond)
- if x.mode != invalid && !isBoolean(x.typ) {
+ if x.mode != invalid && !allBoolean(x.typ) {
check.error(s.Cond, _InvalidCond, "non-boolean condition in for statement")
}
}
@@ -774,20 +829,28 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
// determine key/value types
var key, val Type
if x.mode != invalid {
- typ := optype(x.typ)
- if _, ok := typ.(*Chan); ok && s.Value != nil {
- // TODO(gri) this also needs to happen for channels in generic variables
- check.softErrorf(atPos(s.Value.Pos()), _InvalidIterVar, "range over %s permits only one iteration variable", &x)
- // ok to continue
+ // Ranging over a type parameter is permitted if it has a structural type.
+ var cause string
+ u := structuralType(x.typ)
+ switch t := u.(type) {
+ case nil:
+ cause = check.sprintf("%s has no structural type", x.typ)
+ case *Chan:
+ if s.Value != nil {
+ check.softErrorf(s.Value, _InvalidIterVar, "range over %s permits only one iteration variable", &x)
+ // ok to continue
+ }
+ if t.dir == SendOnly {
+ cause = "receive from send-only channel"
+ }
}
- var msg string
- key, val, msg = rangeKeyVal(typ, isVarName(s.Key), isVarName(s.Value))
- if key == nil || msg != "" {
- if msg != "" {
- // TODO(rFindley) should this be parenthesized, to be consistent with other qualifiers?
- msg = ": " + msg
+ key, val = rangeKeyVal(u)
+ if key == nil || cause != "" {
+ if cause == "" {
+ check.softErrorf(&x, _InvalidRangeExpr, "cannot range over %s", &x)
+ } else {
+ check.softErrorf(&x, _InvalidRangeExpr, "cannot range over %s (%s)", &x, cause)
}
- check.softErrorf(&x, _InvalidRangeExpr, "cannot range over %s%s", &x, msg)
// ok to continue
}
}
@@ -872,71 +935,23 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
}
}
-// isVarName reports whether x is a non-nil, non-blank (_) expression.
-func isVarName(x ast.Expr) bool {
- if x == nil {
- return false
- }
- ident, _ := unparen(x).(*ast.Ident)
- return ident == nil || ident.Name != "_"
-}
-
// rangeKeyVal returns the key and value type produced by a range clause
-// over an expression of type typ, and possibly an error message. If the
-// range clause is not permitted the returned key is nil or msg is not
-// empty (in that case we still may have a non-nil key type which can be
-// used to reduce the chance for follow-on errors).
-// The wantKey, wantVal, and hasVal flags indicate which of the iteration
-// variables are used or present; this matters if we range over a generic
-// type where not all keys or values are of the same type.
-func rangeKeyVal(typ Type, wantKey, wantVal bool) (Type, Type, string) {
- switch typ := typ.(type) {
+// over an expression of type typ. If the range clause is not permitted
+// the results are nil.
+func rangeKeyVal(typ Type) (key, val Type) {
+ switch typ := arrayPtrDeref(typ).(type) {
case *Basic:
if isString(typ) {
- return Typ[Int], universeRune, "" // use 'rune' name
+ return Typ[Int], universeRune // use 'rune' name
}
case *Array:
- return Typ[Int], typ.elem, ""
+ return Typ[Int], typ.elem
case *Slice:
- return Typ[Int], typ.elem, ""
- case *Pointer:
- if typ := asArray(typ.base); typ != nil {
- return Typ[Int], typ.elem, ""
- }
+ return Typ[Int], typ.elem
case *Map:
- return typ.key, typ.elem, ""
+ return typ.key, typ.elem
case *Chan:
- var msg string
- if typ.dir == SendOnly {
- msg = "send-only channel"
- }
- return typ.elem, Typ[Invalid], msg
- case *_Sum:
- first := true
- var key, val Type
- var msg string
- typ.is(func(t Type) bool {
- k, v, m := rangeKeyVal(under(t), wantKey, wantVal)
- if k == nil || m != "" {
- key, val, msg = k, v, m
- return false
- }
- if first {
- key, val, msg = k, v, m
- first = false
- return true
- }
- if wantKey && !Identical(key, k) {
- key, val, msg = nil, nil, "all possible values must have the same key type"
- return false
- }
- if wantVal && !Identical(val, v) {
- key, val, msg = nil, nil, "all possible values must have the same element type"
- return false
- }
- return true
- })
- return key, val, msg
+ return typ.elem, Typ[Invalid]
}
- return nil, nil, ""
+ return
}
diff --git a/libgo/go/go/types/struct.go b/libgo/go/go/types/struct.go
new file mode 100644
index 0000000..d6c5634
--- /dev/null
+++ b/libgo/go/go/types/struct.go
@@ -0,0 +1,217 @@
+// 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.
+
+package types
+
+import (
+ "go/ast"
+ "go/token"
+ "strconv"
+)
+
+// ----------------------------------------------------------------------------
+// API
+
+// A Struct represents a struct type.
+type Struct struct {
+ fields []*Var // fields != nil indicates the struct is set up (possibly with len(fields) == 0)
+ tags []string // field tags; nil if there are no tags
+}
+
+// NewStruct returns a new struct with the given fields and corresponding field tags.
+// If a field with index i has a tag, tags[i] must be that tag, but len(tags) may be
+// only as long as required to hold the tag with the largest index i. Consequently,
+// if no field has a tag, tags may be nil.
+func NewStruct(fields []*Var, tags []string) *Struct {
+ var fset objset
+ for _, f := range fields {
+ if f.name != "_" && fset.insert(f) != nil {
+ panic("multiple fields with the same name")
+ }
+ }
+ if len(tags) > len(fields) {
+ panic("more tags than fields")
+ }
+ s := &Struct{fields: fields, tags: tags}
+ s.markComplete()
+ return s
+}
+
+// NumFields returns the number of fields in the struct (including blank and embedded fields).
+func (s *Struct) NumFields() int { return len(s.fields) }
+
+// Field returns the i'th field for 0 <= i < NumFields().
+func (s *Struct) Field(i int) *Var { return s.fields[i] }
+
+// Tag returns the i'th field tag for 0 <= i < NumFields().
+func (s *Struct) Tag(i int) string {
+ if i < len(s.tags) {
+ return s.tags[i]
+ }
+ return ""
+}
+
+func (t *Struct) Underlying() Type { return t }
+func (t *Struct) String() string { return TypeString(t, nil) }
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+func (s *Struct) markComplete() {
+ if s.fields == nil {
+ s.fields = make([]*Var, 0)
+ }
+}
+
+func (check *Checker) structType(styp *Struct, e *ast.StructType) {
+ list := e.Fields
+ if list == nil {
+ styp.markComplete()
+ return
+ }
+
+ // struct fields and tags
+ var fields []*Var
+ var tags []string
+
+ // for double-declaration checks
+ var fset objset
+
+ // current field typ and tag
+ var typ Type
+ var tag string
+ add := func(ident *ast.Ident, embedded bool, pos token.Pos) {
+ if tag != "" && tags == nil {
+ tags = make([]string, len(fields))
+ }
+ if tags != nil {
+ tags = append(tags, tag)
+ }
+
+ name := ident.Name
+ fld := NewField(pos, check.pkg, name, typ, embedded)
+ // spec: "Within a struct, non-blank field names must be unique."
+ if name == "_" || check.declareInSet(&fset, pos, fld) {
+ fields = append(fields, fld)
+ check.recordDef(ident, fld)
+ }
+ }
+
+ // addInvalid adds an embedded field of invalid type to the struct for
+ // fields with errors; this keeps the number of struct fields in sync
+ // with the source as long as the fields are _ or have different names
+ // (issue #25627).
+ addInvalid := func(ident *ast.Ident, pos token.Pos) {
+ typ = Typ[Invalid]
+ tag = ""
+ add(ident, true, pos)
+ }
+
+ for _, f := range list.List {
+ typ = check.varType(f.Type)
+ tag = check.tag(f.Tag)
+ if len(f.Names) > 0 {
+ // named fields
+ for _, name := range f.Names {
+ add(name, false, name.Pos())
+ }
+ } else {
+ // embedded field
+ // spec: "An embedded type must be specified as a type name T or as a
+ // pointer to a non-interface type name *T, and T itself may not be a
+ // pointer type."
+ pos := f.Type.Pos()
+ name := embeddedFieldIdent(f.Type)
+ if name == nil {
+ check.invalidAST(f.Type, "embedded field type %s has no name", f.Type)
+ name = ast.NewIdent("_")
+ name.NamePos = pos
+ addInvalid(name, pos)
+ continue
+ }
+ add(name, true, pos)
+
+ // Because we have a name, typ must be of the form T or *T, where T is the name
+ // of a (named or alias) type, and t (= deref(typ)) must be the type of T.
+ // We must delay this check to the end because we don't want to instantiate
+ // (via under(t)) a possibly incomplete type.
+
+ // for use in the closure below
+ embeddedTyp := typ
+ embeddedPos := f.Type
+
+ check.later(func() {
+ t, isPtr := deref(embeddedTyp)
+ switch u := under(t).(type) {
+ case *Basic:
+ if t == Typ[Invalid] {
+ // error was reported before
+ return
+ }
+ // unsafe.Pointer is treated like a regular pointer
+ if u.kind == UnsafePointer {
+ check.error(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be unsafe.Pointer")
+ }
+ case *Pointer:
+ check.error(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be a pointer")
+ case *Interface:
+ if isTypeParam(t) {
+ // The error code here is inconsistent with other error codes for
+ // invalid embedding, because this restriction may be relaxed in the
+ // future, and so it did not warrant a new error code.
+ check.error(embeddedPos, _MisplacedTypeParam, "embedded field type cannot be a (pointer to a) type parameter")
+ break
+ }
+ if isPtr {
+ check.error(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be a pointer to an interface")
+ }
+ }
+ }).describef(embeddedPos, "check embedded type %s", embeddedTyp)
+ }
+ }
+
+ styp.fields = fields
+ styp.tags = tags
+ styp.markComplete()
+}
+
+func embeddedFieldIdent(e ast.Expr) *ast.Ident {
+ switch e := e.(type) {
+ case *ast.Ident:
+ return e
+ case *ast.StarExpr:
+ // *T is valid, but **T is not
+ if _, ok := e.X.(*ast.StarExpr); !ok {
+ return embeddedFieldIdent(e.X)
+ }
+ case *ast.SelectorExpr:
+ return e.Sel
+ case *ast.IndexExpr:
+ return embeddedFieldIdent(e.X)
+ case *ast.IndexListExpr:
+ return embeddedFieldIdent(e.X)
+ }
+ return nil // invalid embedded field
+}
+
+func (check *Checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool {
+ if alt := oset.insert(obj); alt != nil {
+ check.errorf(atPos(pos), _DuplicateDecl, "%s redeclared", obj.Name())
+ check.reportAltDecl(alt)
+ return false
+ }
+ return true
+}
+
+func (check *Checker) tag(t *ast.BasicLit) string {
+ if t != nil {
+ if t.Kind == token.STRING {
+ if val, err := strconv.Unquote(t.Value); err == nil {
+ return val
+ }
+ }
+ check.invalidAST(t, "incorrect tag syntax: %q", t.Value)
+ }
+ return ""
+}
diff --git a/libgo/go/go/types/subst.go b/libgo/go/go/types/subst.go
index 931375f..0cce46a 100644
--- a/libgo/go/go/types/subst.go
+++ b/libgo/go/go/types/subst.go
@@ -2,218 +2,43 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// This file implements instantiation of generic types
-// through substitution of type parameters by actual
-// types.
+// This file implements type parameter substitution.
package types
-import (
- "bytes"
- "fmt"
- "go/token"
-)
-
-// TODO(rFindley) decide error codes for the errors in this file, and check
-// if error spans can be improved
-
-type substMap struct {
- // The targs field is currently needed for *Named type substitution.
- // TODO(gri) rewrite that code, get rid of this field, and make this
- // struct just the map (proj)
- targs []Type
- proj map[*_TypeParam]Type
-}
+import "go/token"
+
+type substMap map[*TypeParam]Type
// makeSubstMap creates a new substitution map mapping tpars[i] to targs[i].
// If targs[i] is nil, tpars[i] is not substituted.
-func makeSubstMap(tpars []*TypeName, targs []Type) *substMap {
+func makeSubstMap(tpars []*TypeParam, targs []Type) substMap {
assert(len(tpars) == len(targs))
- proj := make(map[*_TypeParam]Type, len(tpars))
+ proj := make(substMap, len(tpars))
for i, tpar := range tpars {
- // We must expand type arguments otherwise *instance
- // types end up as components in composite types.
- // TODO(gri) explain why this causes problems, if it does
- targ := expand(targs[i]) // possibly nil
- targs[i] = targ
- proj[tpar.typ.(*_TypeParam)] = targ
+ proj[tpar] = targs[i]
}
- return &substMap{targs, proj}
-}
-
-func (m *substMap) String() string {
- return fmt.Sprintf("%s", m.proj)
+ return proj
}
-func (m *substMap) empty() bool {
- return len(m.proj) == 0
+func (m substMap) empty() bool {
+ return len(m) == 0
}
-func (m *substMap) lookup(tpar *_TypeParam) Type {
- if t := m.proj[tpar]; t != nil {
+func (m substMap) lookup(tpar *TypeParam) Type {
+ if t := m[tpar]; t != nil {
return t
}
return tpar
}
-func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist []token.Pos) (res Type) {
- if trace {
- check.trace(pos, "-- instantiating %s with %s", typ, typeListString(targs))
- check.indent++
- defer func() {
- check.indent--
- var under Type
- if res != nil {
- // Calling under() here may lead to endless instantiations.
- // Test case: type T[P any] T[P]
- // TODO(gri) investigate if that's a bug or to be expected.
- under = res.Underlying()
- }
- check.trace(pos, "=> %s (under = %s)", res, under)
- }()
- }
-
- assert(len(poslist) <= len(targs))
-
- // TODO(gri) What is better here: work with TypeParams, or work with TypeNames?
- var tparams []*TypeName
- switch t := typ.(type) {
- case *Named:
- tparams = t.tparams
- case *Signature:
- tparams = t.tparams
- 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
- }()
-
- default:
- check.dump("%v: cannot instantiate %v", pos, typ)
- unreachable() // only defined types and (defined) functions can be generic
- }
-
- // the number of supplied types must match the number of type parameters
- if len(targs) != len(tparams) {
- // TODO(gri) provide better error message
- check.errorf(atPos(pos), _Todo, "got %d arguments but %d type parameters", len(targs), len(tparams))
- return Typ[Invalid]
- }
-
- if len(tparams) == 0 {
- return typ // nothing to do (minor optimization)
- }
-
- smap := makeSubstMap(tparams, targs)
-
- // check bounds
- for i, tname := range tparams {
- tpar := tname.typ.(*_TypeParam)
- iface := tpar.Bound()
- if iface.Empty() {
- continue // no type bound
- }
-
- targ := targs[i]
-
- // best position for error reporting
- pos := pos
- if i < len(poslist) {
- pos = poslist[i]
- }
-
- // 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
- // the parameterized type.
- iface = check.subst(pos, iface, smap).(*Interface)
-
- // targ must implement iface (methods)
- // - check only if we have methods
- check.completeInterface(token.NoPos, iface)
- if len(iface.allMethods) > 0 {
- // If the type argument is a pointer to a type parameter, the type argument's
- // method set is empty.
- // TODO(gri) is this what we want? (spec question)
- if base, isPtr := deref(targ); isPtr && asTypeParam(base) != nil {
- check.errorf(atPos(pos), 0, "%s has no methods", targ)
- break
- }
- if m, wrong := check.missingMethod(targ, iface, true); m != nil {
- // TODO(gri) needs to print updated name to avoid major confusion in error message!
- // (print warning for now)
- // Old warning:
- // check.softErrorf(pos, "%s does not satisfy %s (warning: name not updated) = %s (missing method %s)", targ, tpar.bound, iface, m)
- if m.name == "==" {
- // We don't want to report "missing method ==".
- check.softErrorf(atPos(pos), 0, "%s does not satisfy comparable", targ)
- } else if wrong != nil {
- // TODO(gri) This can still report uninstantiated types which makes the error message
- // more difficult to read then necessary.
- // TODO(rFindley) should this use parentheses rather than ':' for qualification?
- check.softErrorf(atPos(pos), _Todo,
- "%s does not satisfy %s: wrong method signature\n\tgot %s\n\twant %s",
- targ, tpar.bound, wrong, m,
- )
- } else {
- check.softErrorf(atPos(pos), 0, "%s does not satisfy %s (missing method %s)", targ, tpar.bound, m.name)
- }
- break
- }
- }
-
- // targ's underlying type must also be one of the interface types listed, if any
- if iface.allTypes == nil {
- continue // 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 := asTypeParam(targ); targ != nil {
- targBound := targ.Bound()
- if targBound.allTypes == nil {
- check.softErrorf(atPos(pos), _Todo, "%s does not satisfy %s (%s has no type constraints)", targ, tpar.bound, targ)
- break
- }
- for _, t := range unpackType(targBound.allTypes) {
- if !iface.isSatisfiedBy(t) {
- // TODO(gri) match this error message with the one below (or vice versa)
- check.softErrorf(atPos(pos), 0, "%s does not satisfy %s (%s type constraint %s not found in %s)", targ, tpar.bound, targ, t, iface.allTypes)
- break
- }
- }
- break
- }
-
- // Otherwise, targ's type or underlying type must also be one of the interface types listed, if any.
- if !iface.isSatisfiedBy(targ) {
- check.softErrorf(atPos(pos), _Todo, "%s does not satisfy %s (%s or %s not found in %s)", targ, tpar.bound, targ, under(targ), iface.allTypes)
- break
- }
- }
-
- return check.subst(pos, typ, smap)
-}
-
-// subst returns the type typ with its type parameters tpars replaced by
-// the corresponding type arguments targs, recursively.
-// subst is functional in the sense that it doesn't modify the incoming
-// type. If a substitution took place, the result type is different from
-// from the incoming type.
-func (check *Checker) subst(pos token.Pos, typ Type, smap *substMap) Type {
+// subst returns the type typ with its type parameters tpars replaced by the
+// corresponding type arguments targs, recursively. subst is pure in the sense
+// that it doesn't modify the incoming type. If a substitution took place, the
+// result type is different from the incoming type.
+//
+// If the given context is non-nil, it is used in lieu of check.Config.Context
+func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, ctxt *Context) Type {
if smap.empty() {
return typ
}
@@ -222,20 +47,25 @@ func (check *Checker) subst(pos token.Pos, typ Type, smap *substMap) Type {
switch t := typ.(type) {
case *Basic:
return typ // nothing to do
- case *_TypeParam:
+ case *TypeParam:
return smap.lookup(t)
}
// general case
- subst := subster{check, pos, make(map[Type]Type), smap}
+ subst := subster{
+ pos: pos,
+ smap: smap,
+ check: check,
+ ctxt: check.bestContext(ctxt),
+ }
return subst.typ(typ)
}
type subster struct {
- check *Checker
pos token.Pos
- cache map[Type]Type
- smap *substMap
+ smap substMap
+ check *Checker // nil if called via Instantiate
+ ctxt *Context
}
func (subst *subster) typ(typ Type) Type {
@@ -244,7 +74,7 @@ func (subst *subster) typ(typ Type) Type {
// Call typOrNil if it's possible that typ is nil.
panic("nil typ")
- case *Basic, *bottom, *top:
+ case *Basic:
// nothing to do
case *Array:
@@ -261,7 +91,9 @@ func (subst *subster) typ(typ Type) Type {
case *Struct:
if fields, copied := subst.varList(t.fields); copied {
- return &Struct{fields: fields, tags: t.tags}
+ s := &Struct{fields: fields, tags: t.tags}
+ s.markComplete()
+ return s
}
case *Pointer:
@@ -274,18 +106,29 @@ func (subst *subster) typ(typ Type) Type {
return subst.tuple(t)
case *Signature:
- // TODO(gri) rethink the recv situation with respect to methods on parameterized types
- // recv := subst.var_(t.recv) // TODO(gri) this causes a stack overflow - explain
+ // Preserve the receiver: it is handled during *Interface and *Named type
+ // substitution.
+ //
+ // Naively doing the substitution here can lead to an infinite recursion in
+ // the case where the receiver is an interface. For example, consider the
+ // following declaration:
+ //
+ // type T[A any] struct { f interface{ m() } }
+ //
+ // In this case, the type of f is an interface that is itself the receiver
+ // type of all of its methods. Because we have no type name to break
+ // cycles, substituting in the recv results in an infinite loop of
+ // recv->interface->recv->interface->...
recv := t.recv
+
params := subst.tuple(t.params)
results := subst.tuple(t.results)
- if recv != t.recv || params != t.params || results != t.results {
+ if params != t.params || results != t.results {
return &Signature{
rparams: t.rparams,
- // TODO(rFindley) why can't we nil out tparams here, rather than in
- // instantiate above?
- tparams: t.tparams,
- scope: t.scope,
+ // TODO(rFindley) why can't we nil out tparams here, rather than in instantiate?
+ tparams: t.tparams,
+ // instantiated signatures have a nil scope
recv: recv,
params: params,
results: results,
@@ -293,26 +136,34 @@ func (subst *subster) typ(typ Type) Type {
}
}
- case *_Sum:
- types, copied := subst.typeList(t.types)
+ case *Union:
+ terms, copied := subst.termlist(t.terms)
if copied {
- // Don't do it manually, with a Sum literal: the new
- // types list may not be unique and NewSum may remove
- // duplicates.
- return _NewSum(types)
+ // term list substitution may introduce duplicate terms (unlikely but possible).
+ // This is ok; lazy type set computation will determine the actual type set
+ // in normal form.
+ return &Union{terms}
}
case *Interface:
methods, mcopied := subst.funcList(t.methods)
- types := t.types
- if t.types != nil {
- types = subst.typ(t.types)
- }
embeddeds, ecopied := subst.typeList(t.embeddeds)
- if mcopied || types != t.types || ecopied {
- iface := &Interface{methods: methods, types: types, embeddeds: embeddeds}
- subst.check.posMap[iface] = subst.check.posMap[t] // satisfy completeInterface requirement
- subst.check.completeInterface(token.NoPos, iface)
+ if mcopied || ecopied {
+ iface := &Interface{embeddeds: embeddeds, implicit: t.implicit, complete: t.complete}
+ // If we've changed the interface type, we may need to replace its
+ // receiver if the receiver type is the original interface. Receivers of
+ // *Named type are replaced during named type expansion.
+ //
+ // Notably, it's possible to reach here and not create a new *Interface,
+ // even though the receiver type may be parameterized. For example:
+ //
+ // type T[P any] interface{ m() }
+ //
+ // In this case the interface will not be substituted here, because its
+ // method signatures do not depend on the type parameter P, but we still
+ // need to create new interface methods to hold the instantiated
+ // receiver. This is handled by expandNamed.
+ iface.methods, _ = replaceRecvType(methods, t, iface)
return iface
}
@@ -330,85 +181,81 @@ func (subst *subster) typ(typ Type) Type {
}
case *Named:
- subst.check.indent++
- defer func() {
- subst.check.indent--
- }()
- dump := func(format string, args ...interface{}) {
- if trace {
+ // dump is for debugging
+ dump := func(string, ...any) {}
+ if subst.check != nil && trace {
+ subst.check.indent++
+ defer func() {
+ subst.check.indent--
+ }()
+ dump = func(format string, args ...any) {
subst.check.trace(subst.pos, format, args...)
}
}
- if t.tparams == nil {
+ // subst is called by expandNamed, so in this function we need to be
+ // careful not to call any methods that would cause t to be expanded: doing
+ // so would result in deadlock.
+ //
+ // So we call t.orig.TypeParams() rather than t.TypeParams() here and
+ // below.
+ if t.orig.TypeParams().Len() == 0 {
dump(">>> %s is not parameterized", t)
return t // type is not parameterized
}
- var newTargs []Type
-
- if len(t.targs) > 0 {
- // already instantiated
- dump(">>> %s already instantiated", t)
- assert(len(t.targs) == len(t.tparams))
- // For each (existing) type argument targ, determine if it needs
- // to be substituted; i.e., if it is or contains a type parameter
- // that has a type argument for it.
- for i, targ := range t.targs {
- dump(">>> %d targ = %s", i, targ)
- newTarg := subst.typ(targ)
- if newTarg != targ {
- dump(">>> substituted %d targ %s => %s", i, targ, newTarg)
- if newTargs == nil {
- newTargs = make([]Type, len(t.tparams))
- copy(newTargs, t.targs)
- }
- newTargs[i] = newTarg
+ var newTArgs []Type
+ if t.targs.Len() != t.orig.TypeParams().Len() {
+ return Typ[Invalid] // error reported elsewhere
+ }
+
+ // already instantiated
+ dump(">>> %s already instantiated", t)
+ // For each (existing) type argument targ, determine if it needs
+ // to be substituted; i.e., if it is or contains a type parameter
+ // that has a type argument for it.
+ for i, targ := range t.targs.list() {
+ dump(">>> %d targ = %s", i, targ)
+ new_targ := subst.typ(targ)
+ if new_targ != targ {
+ dump(">>> substituted %d targ %s => %s", i, targ, new_targ)
+ if newTArgs == nil {
+ newTArgs = make([]Type, t.orig.TypeParams().Len())
+ copy(newTArgs, t.targs.list())
}
+ newTArgs[i] = new_targ
}
+ }
- if newTargs == nil {
- dump(">>> nothing to substitute in %s", t)
- return t // nothing to substitute
- }
- } else {
- // not yet instantiated
- dump(">>> first instantiation of %s", t)
- // TODO(rFindley) can we instead subst the tparam types here?
- newTargs = subst.smap.targs
+ if newTArgs == nil {
+ dump(">>> nothing to substitute in %s", t)
+ return t // nothing to substitute
}
// before creating a new named type, check if we have this one already
- h := instantiatedHash(t, newTargs)
+ h := subst.ctxt.instanceHash(t.orig, newTArgs)
dump(">>> new type hash: %s", h)
- if named, found := subst.check.typMap[h]; found {
+ if named := subst.ctxt.lookup(h, t.orig, newTArgs); named != nil {
dump(">>> found %s", named)
- subst.cache[t] = named
return named
}
- // create a new named type and populate caches to avoid endless recursion
- tname := NewTypeName(subst.pos, t.obj.pkg, t.obj.name, nil)
- named := subst.check.newNamed(tname, t.underlying, t.methods) // method signatures are updated lazily
- named.tparams = t.tparams // new type is still parameterized
- named.targs = newTargs
- subst.check.typMap[h] = named
- subst.cache[t] = named
-
- // do the substitution
- dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, newTargs)
- named.underlying = subst.typOrNil(t.underlying)
- named.orig = named.underlying // for cycle detection (Checker.validType)
+ // Create a new instance and populate the context to avoid endless
+ // recursion. The position used here is irrelevant because validation only
+ // occurs on t (we don't call validType on named), but we use subst.pos to
+ // help with debugging.
+ t.orig.resolve(subst.ctxt)
+ return subst.check.instance(subst.pos, t.orig, newTArgs, subst.ctxt)
- return named
+ // Note that if we were to expose substitution more generally (not just in
+ // the context of a declaration), we'd have to substitute in
+ // named.underlying as well.
+ //
+ // But this is unnecessary for now.
- case *_TypeParam:
+ case *TypeParam:
return subst.smap.lookup(t)
- case *instance:
- // TODO(gri) can we avoid the expansion here and just substitute the type parameters?
- return subst.typ(t.expand())
-
default:
panic("unimplemented")
}
@@ -416,37 +263,6 @@ func (subst *subster) typ(typ Type) Type {
return typ
}
-// TODO(gri) Eventually, this should be more sophisticated.
-// It won't work correctly for locally declared types.
-func instantiatedHash(typ *Named, targs []Type) string {
- var buf bytes.Buffer
- writeTypeName(&buf, typ.obj, nil)
- buf.WriteByte('[')
- writeTypeList(&buf, targs, nil, nil)
- buf.WriteByte(']')
-
- // With respect to the represented type, whether a
- // type is fully expanded or stored as instance
- // does not matter - they are the same types.
- // Remove the instanceMarkers printed for instances.
- res := buf.Bytes()
- i := 0
- for _, b := range res {
- if b != instanceMarker {
- res[i] = b
- i++
- }
- }
-
- return string(res[:i])
-}
-
-func typeListString(list []Type) string {
- var buf bytes.Buffer
- writeTypeList(&buf, list, nil, nil)
- return buf.String()
-}
-
// typOrNil is like typ but if the argument is nil it is replaced with Typ[Invalid].
// A nil type may appear in pathological cases such as type T[P any] []func(_ T([]_))
// where an array/slice element is accessed before it is set up.
@@ -541,3 +357,49 @@ func (subst *subster) typeList(in []Type) (out []Type, copied bool) {
}
return
}
+
+func (subst *subster) termlist(in []*Term) (out []*Term, copied bool) {
+ out = in
+ for i, t := range in {
+ if u := subst.typ(t.typ); u != t.typ {
+ if !copied {
+ // first function that got substituted => allocate new out slice
+ // and copy all functions
+ new := make([]*Term, len(in))
+ copy(new, out)
+ out = new
+ copied = true
+ }
+ out[i] = NewTerm(t.tilde, u)
+ }
+ }
+ return
+}
+
+// replaceRecvType updates any function receivers that have type old to have
+// type new. It does not modify the input slice; if modifications are required,
+// the input slice and any affected signatures will be copied before mutating.
+//
+// The resulting out slice contains the updated functions, and copied reports
+// if anything was modified.
+func replaceRecvType(in []*Func, old, new Type) (out []*Func, copied bool) {
+ out = in
+ for i, method := range in {
+ sig := method.Type().(*Signature)
+ if sig.recv != nil && sig.recv.Type() == old {
+ if !copied {
+ // Allocate a new methods slice before mutating for the first time.
+ // This is defensive, as we may share methods across instantiations of
+ // a given interface type if they do not get substituted.
+ out = make([]*Func, len(in))
+ copy(out, in)
+ copied = true
+ }
+ newsig := *sig
+ sig = &newsig
+ sig.recv = NewVar(sig.recv.pos, sig.recv.pkg, "", new)
+ out[i] = NewFunc(method.pos, method.pkg, method.name, sig)
+ }
+ }
+ return
+}
diff --git a/libgo/go/go/types/termlist.go b/libgo/go/go/types/termlist.go
new file mode 100644
index 0000000..c4ab0e0
--- /dev/null
+++ b/libgo/go/go/types/termlist.go
@@ -0,0 +1,167 @@
+// 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.
+
+package types
+
+import "bytes"
+
+// A termlist represents the type set represented by the union
+// t1 ∪ y2 ∪ ... tn of the type sets of the terms t1 to tn.
+// A termlist is in normal form if all terms are disjoint.
+// termlist operations don't require the operands to be in
+// normal form.
+type termlist []*term
+
+// allTermlist represents the set of all types.
+// It is in normal form.
+var allTermlist = termlist{new(term)}
+
+// String prints the termlist exactly (without normalization).
+func (xl termlist) String() string {
+ if len(xl) == 0 {
+ return "∅"
+ }
+ var buf bytes.Buffer
+ for i, x := range xl {
+ if i > 0 {
+ buf.WriteString(" ∪ ")
+ }
+ buf.WriteString(x.String())
+ }
+ return buf.String()
+}
+
+// isEmpty reports whether the termlist xl represents the empty set of types.
+func (xl termlist) isEmpty() bool {
+ // If there's a non-nil term, the entire list is not empty.
+ // If the termlist is in normal form, this requires at most
+ // one iteration.
+ for _, x := range xl {
+ if x != nil {
+ return false
+ }
+ }
+ return true
+}
+
+// isAll reports whether the termlist xl represents the set of all types.
+func (xl termlist) isAll() bool {
+ // If there's a 𝓤 term, the entire list is 𝓤.
+ // If the termlist is in normal form, this requires at most
+ // one iteration.
+ for _, x := range xl {
+ if x != nil && x.typ == nil {
+ return true
+ }
+ }
+ return false
+}
+
+// norm returns the normal form of xl.
+func (xl termlist) norm() termlist {
+ // Quadratic algorithm, but good enough for now.
+ // TODO(gri) fix asymptotic performance
+ used := make([]bool, len(xl))
+ var rl termlist
+ for i, xi := range xl {
+ if xi == nil || used[i] {
+ continue
+ }
+ for j := i + 1; j < len(xl); j++ {
+ xj := xl[j]
+ if xj == nil || used[j] {
+ continue
+ }
+ if u1, u2 := xi.union(xj); u2 == nil {
+ // If we encounter a 𝓤 term, the entire list is 𝓤.
+ // Exit early.
+ // (Note that this is not just an optimization;
+ // if we continue, we may end up with a 𝓤 term
+ // and other terms and the result would not be
+ // in normal form.)
+ if u1.typ == nil {
+ return allTermlist
+ }
+ xi = u1
+ used[j] = true // xj is now unioned into xi - ignore it in future iterations
+ }
+ }
+ rl = append(rl, xi)
+ }
+ return rl
+}
+
+// If the type set represented by xl is specified by a single (non-𝓤) term,
+// singleType returns that type. Otherwise it returns nil.
+func (xl termlist) singleType() Type {
+ if nl := xl.norm(); len(nl) == 1 {
+ return nl[0].typ // if nl.isAll() then typ is nil, which is ok
+ }
+ return nil
+}
+
+// union returns the union xl ∪ yl.
+func (xl termlist) union(yl termlist) termlist {
+ return append(xl, yl...).norm()
+}
+
+// intersect returns the intersection xl ∩ yl.
+func (xl termlist) intersect(yl termlist) termlist {
+ if xl.isEmpty() || yl.isEmpty() {
+ return nil
+ }
+
+ // Quadratic algorithm, but good enough for now.
+ // TODO(gri) fix asymptotic performance
+ var rl termlist
+ for _, x := range xl {
+ for _, y := range yl {
+ if r := x.intersect(y); r != nil {
+ rl = append(rl, r)
+ }
+ }
+ }
+ return rl.norm()
+}
+
+// equal reports whether xl and yl represent the same type set.
+func (xl termlist) equal(yl termlist) bool {
+ // TODO(gri) this should be more efficient
+ return xl.subsetOf(yl) && yl.subsetOf(xl)
+}
+
+// includes reports whether t ∈ xl.
+func (xl termlist) includes(t Type) bool {
+ for _, x := range xl {
+ if x.includes(t) {
+ return true
+ }
+ }
+ return false
+}
+
+// supersetOf reports whether y ⊆ xl.
+func (xl termlist) supersetOf(y *term) bool {
+ for _, x := range xl {
+ if y.subsetOf(x) {
+ return true
+ }
+ }
+ return false
+}
+
+// subsetOf reports whether xl ⊆ yl.
+func (xl termlist) subsetOf(yl termlist) bool {
+ if yl.isEmpty() {
+ return xl.isEmpty()
+ }
+
+ // each term x of xl must be a subset of yl
+ for _, x := range xl {
+ if !yl.supersetOf(x) {
+ return false // x is not a subset yl
+ }
+ }
+ return true
+}
diff --git a/libgo/go/go/types/termlist_test.go b/libgo/go/go/types/termlist_test.go
new file mode 100644
index 0000000..dddca7a
--- /dev/null
+++ b/libgo/go/go/types/termlist_test.go
@@ -0,0 +1,313 @@
+// 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.
+
+package types
+
+import (
+ "strings"
+ "testing"
+)
+
+// maketl makes a term list from a string of the term list.
+func maketl(s string) termlist {
+ s = strings.ReplaceAll(s, " ", "")
+ names := strings.Split(s, "∪")
+ r := make(termlist, len(names))
+ for i, n := range names {
+ r[i] = testTerm(n)
+ }
+ return r
+}
+
+func TestTermlistAll(t *testing.T) {
+ if !allTermlist.isAll() {
+ t.Errorf("allTermlist is not the set of all types")
+ }
+}
+
+func TestTermlistString(t *testing.T) {
+ for _, want := range []string{
+ "∅",
+ "𝓤",
+ "int",
+ "~int",
+ "myInt",
+ "∅ ∪ ∅",
+ "𝓤 ∪ 𝓤",
+ "∅ ∪ 𝓤 ∪ int",
+ "∅ ∪ 𝓤 ∪ int ∪ myInt",
+ } {
+ if got := maketl(want).String(); got != want {
+ t.Errorf("(%v).String() == %v", want, got)
+ }
+ }
+}
+
+func TestTermlistIsEmpty(t *testing.T) {
+ for test, want := range map[string]bool{
+ "∅": true,
+ "∅ ∪ ∅": true,
+ "∅ ∪ ∅ ∪ 𝓤": false,
+ "∅ ∪ ∅ ∪ myInt": false,
+ "𝓤": false,
+ "𝓤 ∪ int": false,
+ "𝓤 ∪ myInt ∪ ∅": false,
+ } {
+ xl := maketl(test)
+ got := xl.isEmpty()
+ if got != want {
+ t.Errorf("(%v).isEmpty() == %v; want %v", test, got, want)
+ }
+ }
+}
+
+func TestTermlistIsAll(t *testing.T) {
+ for test, want := range map[string]bool{
+ "∅": false,
+ "∅ ∪ ∅": false,
+ "int ∪ ~string": false,
+ "~int ∪ myInt": false,
+ "∅ ∪ ∅ ∪ 𝓤": true,
+ "𝓤": true,
+ "𝓤 ∪ int": true,
+ "myInt ∪ 𝓤": true,
+ } {
+ xl := maketl(test)
+ got := xl.isAll()
+ if got != want {
+ t.Errorf("(%v).isAll() == %v; want %v", test, got, want)
+ }
+ }
+}
+
+func TestTermlistNorm(t *testing.T) {
+ for _, test := range []struct {
+ xl, want string
+ }{
+ {"∅", "∅"},
+ {"∅ ∪ ∅", "∅"},
+ {"∅ ∪ int", "int"},
+ {"∅ ∪ myInt", "myInt"},
+ {"𝓤 ∪ int", "𝓤"},
+ {"𝓤 ∪ myInt", "𝓤"},
+ {"int ∪ myInt", "int ∪ myInt"},
+ {"~int ∪ int", "~int"},
+ {"~int ∪ myInt", "~int"},
+ {"int ∪ ~string ∪ int", "int ∪ ~string"},
+ {"~int ∪ string ∪ 𝓤 ∪ ~string ∪ int", "𝓤"},
+ {"~int ∪ string ∪ myInt ∪ ~string ∪ int", "~int ∪ ~string"},
+ } {
+ xl := maketl(test.xl)
+ got := maketl(test.xl).norm()
+ if got.String() != test.want {
+ t.Errorf("(%v).norm() = %v; want %v", xl, got, test.want)
+ }
+ }
+}
+
+func TestTermlistSingleType(t *testing.T) {
+ // helper to deal with nil types
+ tstring := func(typ Type) string {
+ if typ == nil {
+ return "nil"
+ }
+ return typ.String()
+ }
+
+ for test, want := range map[string]string{
+ "∅": "nil",
+ "𝓤": "nil",
+ "int": "int",
+ "myInt": "myInt",
+ "~int": "int",
+ "~int ∪ string": "nil",
+ "~int ∪ myInt": "int",
+ "∅ ∪ int": "int",
+ "∅ ∪ ~int": "int",
+ "∅ ∪ ~int ∪ string": "nil",
+ } {
+ xl := maketl(test)
+ got := tstring(xl.singleType())
+ if got != want {
+ t.Errorf("(%v).singleType() == %v; want %v", test, got, want)
+ }
+ }
+}
+
+func TestTermlistUnion(t *testing.T) {
+ for _, test := range []struct {
+ xl, yl, want string
+ }{
+
+ {"∅", "∅", "∅"},
+ {"∅", "𝓤", "𝓤"},
+ {"∅", "int", "int"},
+ {"𝓤", "~int", "𝓤"},
+ {"int", "~int", "~int"},
+ {"int", "string", "int ∪ string"},
+ {"int", "myInt", "int ∪ myInt"},
+ {"~int", "myInt", "~int"},
+ {"int ∪ string", "~string", "int ∪ ~string"},
+ {"~int ∪ string", "~string ∪ int", "~int ∪ ~string"},
+ {"~int ∪ string ∪ ∅", "~string ∪ int", "~int ∪ ~string"},
+ {"~int ∪ myInt ∪ ∅", "~string ∪ int", "~int ∪ ~string"},
+ {"~int ∪ string ∪ 𝓤", "~string ∪ int", "𝓤"},
+ {"~int ∪ string ∪ myInt", "~string ∪ int", "~int ∪ ~string"},
+ } {
+ xl := maketl(test.xl)
+ yl := maketl(test.yl)
+ got := xl.union(yl).String()
+ if got != test.want {
+ t.Errorf("(%v).union(%v) = %v; want %v", test.xl, test.yl, got, test.want)
+ }
+ }
+}
+
+func TestTermlistIntersect(t *testing.T) {
+ for _, test := range []struct {
+ xl, yl, want string
+ }{
+
+ {"∅", "∅", "∅"},
+ {"∅", "𝓤", "∅"},
+ {"∅", "int", "∅"},
+ {"∅", "myInt", "∅"},
+ {"𝓤", "~int", "~int"},
+ {"𝓤", "myInt", "myInt"},
+ {"int", "~int", "int"},
+ {"int", "string", "∅"},
+ {"int", "myInt", "∅"},
+ {"~int", "myInt", "myInt"},
+ {"int ∪ string", "~string", "string"},
+ {"~int ∪ string", "~string ∪ int", "int ∪ string"},
+ {"~int ∪ string ∪ ∅", "~string ∪ int", "int ∪ string"},
+ {"~int ∪ myInt ∪ ∅", "~string ∪ int", "int"},
+ {"~int ∪ string ∪ 𝓤", "~string ∪ int", "int ∪ ~string"},
+ {"~int ∪ string ∪ myInt", "~string ∪ int", "int ∪ string"},
+ } {
+ xl := maketl(test.xl)
+ yl := maketl(test.yl)
+ got := xl.intersect(yl).String()
+ if got != test.want {
+ t.Errorf("(%v).intersect(%v) = %v; want %v", test.xl, test.yl, got, test.want)
+ }
+ }
+}
+
+func TestTermlistEqual(t *testing.T) {
+ for _, test := range []struct {
+ xl, yl string
+ want bool
+ }{
+ {"∅", "∅", true},
+ {"∅", "𝓤", false},
+ {"𝓤", "𝓤", true},
+ {"𝓤 ∪ int", "𝓤", true},
+ {"𝓤 ∪ int", "string ∪ 𝓤", true},
+ {"𝓤 ∪ myInt", "string ∪ 𝓤", true},
+ {"int ∪ ~string", "string ∪ int", false},
+ {"~int ∪ string", "string ∪ myInt", false},
+ {"int ∪ ~string ∪ ∅", "string ∪ int ∪ ~string", true},
+ } {
+ xl := maketl(test.xl)
+ yl := maketl(test.yl)
+ got := xl.equal(yl)
+ if got != test.want {
+ t.Errorf("(%v).equal(%v) = %v; want %v", test.xl, test.yl, got, test.want)
+ }
+ }
+}
+
+func TestTermlistIncludes(t *testing.T) {
+ for _, test := range []struct {
+ xl, typ string
+ want bool
+ }{
+ {"∅", "int", false},
+ {"𝓤", "int", true},
+ {"~int", "int", true},
+ {"int", "string", false},
+ {"~int", "string", false},
+ {"~int", "myInt", true},
+ {"int ∪ string", "string", true},
+ {"~int ∪ string", "int", true},
+ {"~int ∪ string", "myInt", true},
+ {"~int ∪ myInt ∪ ∅", "myInt", true},
+ {"myInt ∪ ∅ ∪ 𝓤", "int", true},
+ } {
+ xl := maketl(test.xl)
+ yl := testTerm(test.typ).typ
+ got := xl.includes(yl)
+ if got != test.want {
+ t.Errorf("(%v).includes(%v) = %v; want %v", test.xl, yl, got, test.want)
+ }
+ }
+}
+
+func TestTermlistSupersetOf(t *testing.T) {
+ for _, test := range []struct {
+ xl, typ string
+ want bool
+ }{
+ {"∅", "∅", true},
+ {"∅", "𝓤", false},
+ {"∅", "int", false},
+ {"𝓤", "∅", true},
+ {"𝓤", "𝓤", true},
+ {"𝓤", "int", true},
+ {"𝓤", "~int", true},
+ {"𝓤", "myInt", true},
+ {"~int", "int", true},
+ {"~int", "~int", true},
+ {"~int", "myInt", true},
+ {"int", "~int", false},
+ {"myInt", "~int", false},
+ {"int", "string", false},
+ {"~int", "string", false},
+ {"int ∪ string", "string", true},
+ {"int ∪ string", "~string", false},
+ {"~int ∪ string", "int", true},
+ {"~int ∪ string", "myInt", true},
+ {"~int ∪ string ∪ ∅", "string", true},
+ {"~string ∪ ∅ ∪ 𝓤", "myInt", true},
+ } {
+ xl := maketl(test.xl)
+ y := testTerm(test.typ)
+ got := xl.supersetOf(y)
+ if got != test.want {
+ t.Errorf("(%v).supersetOf(%v) = %v; want %v", test.xl, y, got, test.want)
+ }
+ }
+}
+
+func TestTermlistSubsetOf(t *testing.T) {
+ for _, test := range []struct {
+ xl, yl string
+ want bool
+ }{
+ {"∅", "∅", true},
+ {"∅", "𝓤", true},
+ {"𝓤", "∅", false},
+ {"𝓤", "𝓤", true},
+ {"int", "int ∪ string", true},
+ {"~int", "int ∪ string", false},
+ {"~int", "myInt ∪ string", false},
+ {"myInt", "~int ∪ string", true},
+ {"~int", "string ∪ string ∪ int ∪ ~int", true},
+ {"myInt", "string ∪ string ∪ ~int", true},
+ {"int ∪ string", "string", false},
+ {"int ∪ string", "string ∪ int", true},
+ {"int ∪ ~string", "string ∪ int", false},
+ {"myInt ∪ ~string", "string ∪ int ∪ 𝓤", true},
+ {"int ∪ ~string", "string ∪ int ∪ ∅ ∪ string", false},
+ {"int ∪ myInt", "string ∪ ~int ∪ ∅ ∪ string", true},
+ } {
+ xl := maketl(test.xl)
+ yl := maketl(test.yl)
+ got := xl.subsetOf(yl)
+ if got != test.want {
+ t.Errorf("(%v).subsetOf(%v) = %v; want %v", test.xl, test.yl, got, test.want)
+ }
+ }
+}
diff --git a/libgo/go/go/types/testdata/check/builtins.go2 b/libgo/go/go/types/testdata/check/builtins.go2
index 3918d83..c1accff 100644
--- a/libgo/go/go/types/testdata/check/builtins.go2
+++ b/libgo/go/go/types/testdata/check/builtins.go2
@@ -6,48 +6,269 @@
package builtins
-type Bmc interface {
- type map[rune]string, chan int
+import "unsafe"
+
+// close
+
+type C0 interface{ int }
+type C1 interface{ chan int }
+type C2 interface{ chan int | <-chan int }
+type C3 interface{ chan int | chan float32 }
+type C4 interface{ chan int | chan<- int }
+type C5[T any] interface{ ~chan T | chan<- T }
+
+func _[T any](ch T) {
+ close(ch /* ERROR cannot close non-channel */)
+}
+
+func _[T C0](ch T) {
+ close(ch /* ERROR cannot close non-channel */)
+}
+
+func _[T C1](ch T) {
+ close(ch)
+}
+
+func _[T C2](ch T) {
+ close(ch /* ERROR cannot close receive-only channel */)
+}
+
+func _[T C3](ch T) {
+ close(ch)
+}
+
+func _[T C4](ch T) {
+ close(ch)
}
-type Bms interface {
- type map[string]int, []int
+func _[T C5[X], X any](ch T) {
+ close(ch)
}
-type Bcs interface {
- type chan bool, []float64
+// copy
+
+func _[T any](x, y T) {
+ copy(x /* ERROR copy expects slice arguments */ , y)
}
-type Bss interface {
- type []int, []string
+func _[T ~[]byte](x, y T) {
+ copy(x, y)
+ copy(x, "foo")
+ copy("foo" /* ERROR expects slice arguments */ , y)
+
+ var x2 []byte
+ copy(x2, y) // element types are identical
+ copy(y, x2) // element types are identical
+
+ type myByte byte
+ var x3 []myByte
+ copy(x3 /* ERROR different element types */ , y)
+ copy(y /* ERROR different element types */ , x3)
}
-func _[T any] () {
- _ = make(T /* ERROR invalid argument */ )
- _ = make(T /* ERROR invalid argument */ , 10)
- _ = make(T /* ERROR invalid argument */ , 10, 20)
+func _[T ~[]E, E any](x T, y []E) {
+ copy(x, y)
+ copy(x /* ERROR different element types */ , "foo")
}
-func _[T Bmc] () {
- _ = make(T)
- _ = make(T, 10)
- _ = make /* ERROR expects 1 or 2 arguments */ (T, 10, 20)
+func _[T ~string](x []byte, y T) {
+ copy(x, y)
+ copy(y /* ERROR expects slice arguments */ , x)
}
-func _[T Bms] () {
- _ = make /* ERROR expects 2 arguments */ (T)
- _ = make(T, 10)
- _ = make /* ERROR expects 2 arguments */ (T, 10, 20)
+func _[T ~[]byte|~string](x T, y []byte) {
+ copy(x /* ERROR expects slice arguments */ , y)
+ copy(y, x)
}
-func _[T Bcs] () {
- _ = make /* ERROR expects 2 arguments */ (T)
- _ = make(T, 10)
- _ = make /* ERROR expects 2 arguments */ (T, 10, 20)
+type L0 []int
+type L1 []int
+
+func _[T L0 | L1](x, y T) {
+ copy(x, y)
}
-func _[T Bss] () {
- _ = make /* ERROR expects 2 or 3 arguments */ (T)
- _ = make(T, 10)
- _ = make(T, 10, 20)
+// delete
+
+type M0 interface{ int }
+type M1 interface{ map[string]int }
+type M2 interface { map[string]int | map[string]float64 }
+type M3 interface{ map[string]int | map[rune]int }
+type M4[K comparable, V any] interface{ map[K]V | map[rune]V }
+
+func _[T any](m T) {
+ delete(m /* ERROR not a map */, "foo")
+}
+
+func _[T M0](m T) {
+ delete(m /* ERROR not a map */, "foo")
+}
+
+func _[T M1](m T) {
+ delete(m, "foo")
+}
+
+func _[T M2](m T) {
+ delete(m, "foo")
+ delete(m, 0 /* ERROR cannot use .* as string */)
+}
+
+func _[T M3](m T) {
+ delete(m /* ERROR must have identical key types */, "foo")
+}
+
+func _[T M4[rune, V], V any](m T) {
+ delete(m, 'k')
+}
+
+func _[T M4[K, V], K comparable, V any](m T) {
+ delete(m /* ERROR must have identical key types */, "foo")
+}
+
+// make
+
+type myChan chan int
+
+func _[
+ S1 ~[]int,
+ S2 ~[]int | ~chan int,
+
+ M1 ~map[string]int,
+ M2 ~map[string]int | ~chan int,
+
+ C1 ~chan int,
+ C2 ~chan int | ~chan string,
+ C3 chan int | myChan, // single underlying type
+]() {
+ type S0 []int
+ _ = make([]int, 10)
+ _ = make(S0, 10)
+ _ = make(S1, 10)
+ _ = make() /* ERROR not enough arguments */
+ _ = make /* ERROR expects 2 or 3 arguments */ (S1)
+ _ = make(S1, 10, 20)
+ _ = make /* ERROR expects 2 or 3 arguments */ (S1, 10, 20, 30)
+ _ = make(S2 /* ERROR cannot make S2: no structural type */ , 10)
+
+ type M0 map[string]int
+ _ = make(map[string]int)
+ _ = make(M0)
+ _ = make(M1)
+ _ = make(M1, 10)
+ _ = make/* ERROR expects 1 or 2 arguments */(M1, 10, 20)
+ _ = make(M2 /* ERROR cannot make M2: no structural type */ )
+
+ type C0 chan int
+ _ = make(chan int)
+ _ = make(C0)
+ _ = make(C1)
+ _ = make(C1, 10)
+ _ = make/* ERROR expects 1 or 2 arguments */(C1, 10, 20)
+ _ = make(C2 /* ERROR cannot make C2: no structural type */ )
+ _ = make(C3)
+}
+
+// unsafe.Alignof
+
+func _[T comparable]() {
+ var (
+ b int64
+ a [10]T
+ s struct{ f T }
+ p *T
+ l []T
+ f func(T)
+ i interface{ m() T }
+ c chan T
+ m map[T]T
+ t T
+ )
+
+ const bb = unsafe.Alignof(b)
+ assert(bb == 8)
+ const _ = unsafe /* ERROR not constant */ .Alignof(a)
+ const _ = unsafe /* ERROR not constant */ .Alignof(s)
+ const pp = unsafe.Alignof(p)
+ assert(pp == 8)
+ const ll = unsafe.Alignof(l)
+ assert(ll == 8)
+ const ff = unsafe.Alignof(f)
+ assert(ff == 8)
+ const ii = unsafe.Alignof(i)
+ assert(ii == 8)
+ const cc = unsafe.Alignof(c)
+ assert(cc == 8)
+ const mm = unsafe.Alignof(m)
+ assert(mm == 8)
+ const _ = unsafe /* ERROR not constant */ .Alignof(t)
+}
+
+// unsafe.Offsetof
+
+func _[T comparable]() {
+ var (
+ b struct{ _, f int64 }
+ a struct{ _, f [10]T }
+ s struct{ _, f struct{ f T } }
+ p struct{ _, f *T }
+ l struct{ _, f []T }
+ f struct{ _, f func(T) }
+ i struct{ _, f interface{ m() T } }
+ c struct{ _, f chan T }
+ m struct{ _, f map[T]T }
+ t struct{ _, f T }
+ )
+
+ const bb = unsafe.Offsetof(b.f)
+ assert(bb == 8)
+ const _ = unsafe /* ERROR not constant */ .Alignof(a)
+ const _ = unsafe /* ERROR not constant */ .Alignof(s)
+ const pp = unsafe.Offsetof(p.f)
+ assert(pp == 8)
+ const ll = unsafe.Offsetof(l.f)
+ assert(ll == 24)
+ const ff = unsafe.Offsetof(f.f)
+ assert(ff == 8)
+ const ii = unsafe.Offsetof(i.f)
+ assert(ii == 16)
+ const cc = unsafe.Offsetof(c.f)
+ assert(cc == 8)
+ const mm = unsafe.Offsetof(m.f)
+ assert(mm == 8)
+ const _ = unsafe /* ERROR not constant */ .Alignof(t)
+}
+
+// unsafe.Sizeof
+
+func _[T comparable]() {
+ var (
+ b int64
+ a [10]T
+ s struct{ f T }
+ p *T
+ l []T
+ f func(T)
+ i interface{ m() T }
+ c chan T
+ m map[T]T
+ t T
+ )
+
+ const bb = unsafe.Sizeof(b)
+ assert(bb == 8)
+ const _ = unsafe /* ERROR not constant */ .Alignof(a)
+ const _ = unsafe /* ERROR not constant */ .Alignof(s)
+ const pp = unsafe.Sizeof(p)
+ assert(pp == 8)
+ const ll = unsafe.Sizeof(l)
+ assert(ll == 24)
+ const ff = unsafe.Sizeof(f)
+ assert(ff == 8)
+ const ii = unsafe.Sizeof(i)
+ assert(ii == 16)
+ const cc = unsafe.Sizeof(c)
+ assert(cc == 8)
+ const mm = unsafe.Sizeof(m)
+ assert(mm == 8)
+ const _ = unsafe /* ERROR not constant */ .Alignof(t)
}
diff --git a/libgo/go/go/types/testdata/check/builtins.src b/libgo/go/go/types/testdata/check/builtins.src
index 3707528..7fd6a4b 100644
--- a/libgo/go/go/types/testdata/check/builtins.src
+++ b/libgo/go/go/types/testdata/check/builtins.src
@@ -144,7 +144,7 @@ func close1() {
var r <-chan int
close() // ERROR not enough arguments
close(1, 2) // ERROR too many arguments
- close(42 /* ERROR not a channel */)
+ close(42 /* ERROR cannot close non-channel */)
close(r /* ERROR receive-only channel */)
close(c)
_ = close /* ERROR used as value */ (c)
diff --git a/libgo/go/go/types/testdata/check/const0.src b/libgo/go/go/types/testdata/check/const0.src
index 5608b15..3cffdf9 100644
--- a/libgo/go/go/types/testdata/check/const0.src
+++ b/libgo/go/go/types/testdata/check/const0.src
@@ -27,7 +27,7 @@ const (
ub1 = true
ub2 = 2 < 1
ub3 = ui1 == uf1
- ub4 = true /* ERROR "cannot convert" */ == 0
+ ub4 = true /* ERROR "mismatched types untyped bool and untyped int" */ == 0
// integer values
ui0 = 0
diff --git a/libgo/go/go/types/testdata/check/cycles.src b/libgo/go/go/types/testdata/check/cycles.src
index 218b4ca..27b6111 100644
--- a/libgo/go/go/types/testdata/check/cycles.src
+++ b/libgo/go/go/types/testdata/check/cycles.src
@@ -45,6 +45,7 @@ type (
// pointers
P0 *P0
+ PP *struct{ PP.f /* ERROR no field or method f */ }
// functions
F0 func(F0)
diff --git a/libgo/go/go/types/testdata/check/cycles4.src b/libgo/go/go/types/testdata/check/cycles4.src
index 445babc..924aabf 100644
--- a/libgo/go/go/types/testdata/check/cycles4.src
+++ b/libgo/go/go/types/testdata/check/cycles4.src
@@ -4,6 +4,8 @@
package p
+import "unsafe"
+
// Check that all methods of T are collected before
// determining the result type of m (which embeds
// all methods of T).
@@ -13,7 +15,7 @@ type T interface {
E
}
-var _ = T.m(nil).m().e()
+var _ int = T.m(nil).m().e()
type E interface {
e() int
@@ -22,7 +24,7 @@ type E interface {
// Check that unresolved forward chains are followed
// (see also comment in resolver.go, checker.typeDecl).
-var _ = C.m(nil).m().e()
+var _ int = C.m(nil).m().e()
type A B
@@ -108,3 +110,12 @@ type Element interface {
type Event interface {
Target() Element
}
+
+// Check that accessing an interface method too early doesn't lead
+// to follow-on errors due to an incorrectly computed type set.
+
+type T8 interface {
+ m() [unsafe.Sizeof(T8.m /* ERROR undefined */ )]int
+}
+
+var _ = T8.m // no error expected here
diff --git a/libgo/go/go/types/testdata/check/cycles5.src b/libgo/go/go/types/testdata/check/cycles5.src
index 397adcc..c932ef92 100644
--- a/libgo/go/go/types/testdata/check/cycles5.src
+++ b/libgo/go/go/types/testdata/check/cycles5.src
@@ -135,7 +135,7 @@ type (
type (
a struct{ *b }
b = c
- c struct{ *b }
+ c struct{ *b /* ERROR invalid use of type alias */ }
)
// issue #24939
@@ -145,7 +145,7 @@ type (
}
M interface {
- F() P
+ F() P // ERROR invalid use of type alias
}
P = interface {
diff --git a/libgo/go/go/types/testdata/check/decls0.src b/libgo/go/go/types/testdata/check/decls0.src
index 5ad8f53..18f0d32 100644
--- a/libgo/go/go/types/testdata/check/decls0.src
+++ b/libgo/go/go/types/testdata/check/decls0.src
@@ -4,7 +4,7 @@
// type declarations
-package decls0
+package go1_17 // don't permit non-interface elements in interfaces
import "unsafe"
@@ -146,7 +146,7 @@ type (
m1(I5)
}
I6 interface {
- S0 /* ERROR "not an interface" */
+ S0 /* ERROR "non-interface type S0" */
}
I7 interface {
I1
@@ -187,10 +187,10 @@ func f4() (x *f4 /* ERROR "not a type" */ ) { return }
// TODO(#43215) this should be detected as a cycle error
func f5([unsafe.Sizeof(f5)]int) {}
-func (S0) m1 (x S0 /* ERROR value .* is not a type */ .m1) {}
-func (S0) m2 (x *S0 /* ERROR value .* is not a type */ .m2) {}
-func (S0) m3 () (x S0 /* ERROR value .* is not a type */ .m3) { return }
-func (S0) m4 () (x *S0 /* ERROR value .* is not a type */ .m4) { return }
+func (S0) m1 (x S0 /* ERROR illegal cycle in method declaration */ .m1) {}
+func (S0) m2 (x *S0 /* ERROR illegal cycle in method declaration */ .m2) {}
+func (S0) m3 () (x S0 /* ERROR illegal cycle in method declaration */ .m3) { return }
+func (S0) m4 () (x *S0 /* ERROR illegal cycle in method declaration */ .m4) { return }
// interfaces may not have any blank methods
type BlankI interface {
diff --git a/libgo/go/go/types/testdata/check/decls1.src b/libgo/go/go/types/testdata/check/decls1.src
index f4d2eab..6fe349b 100644
--- a/libgo/go/go/types/testdata/check/decls1.src
+++ b/libgo/go/go/types/testdata/check/decls1.src
@@ -83,7 +83,7 @@ var (
// Constant expression initializations
var (
- v1 = 1 /* ERROR "cannot convert" */ + "foo"
+ v1 = 1 /* ERROR "mismatched types untyped int and untyped string" */ + "foo"
v2 = c + 255
v3 = c + 256 /* ERROR "overflows" */
v4 = r + 2147483647
diff --git a/libgo/go/go/types/testdata/check/errors.src b/libgo/go/go/types/testdata/check/errors.src
index ff92921..7cdc5fb 100644
--- a/libgo/go/go/types/testdata/check/errors.src
+++ b/libgo/go/go/types/testdata/check/errors.src
@@ -8,32 +8,38 @@ package errors
// (matching messages are regular expressions, hence the \'s).
func f(x int, m map[string]int) {
// no values
- _ = f /* ERROR "f\(0, m\) \(no value\) used as value" */ (0, m)
+ _ = f /* ERROR f\(0, m\) \(no value\) used as value */ (0, m)
// built-ins
- _ = println /* ERROR "println \(built-in\) must be called" */
+ _ = println // ERROR println \(built-in\) must be called
// types
- _ = complex128 /* ERROR "complex128 \(type\) is not an expression" */
+ _ = complex128 // ERROR complex128 \(type\) is not an expression
// constants
const c1 = 991
const c2 float32 = 0.5
- 0 /* ERROR "0 \(untyped int constant\) is not used" */
- c1 /* ERROR "c1 \(untyped int constant 991\) is not used" */
- c2 /* ERROR "c2 \(constant 0.5 of type float32\) is not used" */
- c1 /* ERROR "c1 \+ c2 \(constant 991.5 of type float32\) is not used" */ + c2
+ const c3 = "foo"
+ 0 // ERROR 0 \(untyped int constant\) is not used
+ 0.5 // ERROR 0.5 \(untyped float constant\) is not used
+ "foo" // ERROR "foo" \(untyped string constant\) is not used
+ c1 // ERROR c1 \(untyped int constant 991\) is not used
+ c2 // ERROR c2 \(constant 0.5 of type float32\) is not used
+ c1 /* ERROR c1 \+ c2 \(constant 991.5 of type float32\) is not used */ + c2
+ c3 // ERROR c3 \(untyped string constant "foo"\) is not used
// variables
- x /* ERROR "x \(variable of type int\) is not used" */
+ x // ERROR x \(variable of type int\) is not used
// values
- x /* ERROR "x != x \(untyped bool value\) is not used" */ != x
- x /* ERROR "x \+ x \(value of type int\) is not used" */ + x
+ nil // ERROR nil is not used
+ ( /* ERROR \(\*int\)\(nil\) \(value of type \*int\) is not used */ *int)(nil)
+ x /* ERROR x != x \(untyped bool value\) is not used */ != x
+ x /* ERROR x \+ x \(value of type int\) is not used */ + x
// value, ok's
const s = "foo"
- m /* ERROR "m\[s\] \(map index expression of type int\) is not used" */ [s]
+ m /* ERROR m\[s\] \(map index expression of type int\) is not used */ [s]
}
// Valid ERROR comments can have a variety of forms.
diff --git a/libgo/go/go/types/testdata/check/expr1.src b/libgo/go/go/types/testdata/check/expr1.src
index 4ead815..42b95fb 100644
--- a/libgo/go/go/types/testdata/check/expr1.src
+++ b/libgo/go/go/types/testdata/check/expr1.src
@@ -111,10 +111,10 @@ type mystring string
func _(x, y string, z mystring) {
x = x + "foo"
x = x /* ERROR not defined */ - "foo"
- x = x + 1 // ERROR cannot convert
+ x = x /* ERROR mismatched types string and untyped int */ + 1
x = x + y
x = x /* ERROR not defined */ - y
- x = x * 10 // ERROR cannot convert
+ x = x /* ERROR mismatched types string and untyped int */* 10
}
func f() (a, b int) { return }
diff --git a/libgo/go/go/types/testdata/check/expr2.src b/libgo/go/go/types/testdata/check/expr2.src
index 0c959e8..8757fd9 100644
--- a/libgo/go/go/types/testdata/check/expr2.src
+++ b/libgo/go/go/types/testdata/check/expr2.src
@@ -10,7 +10,7 @@ func _bool() {
const t = true == true
const f = true == false
_ = t /* ERROR "cannot compare" */ < f
- _ = 0 /* ERROR "cannot convert" */ == t
+ _ = 0 /* ERROR "mismatched types untyped int and untyped bool" */ == t
var b bool
var x, y float32
b = x < y
@@ -29,7 +29,7 @@ func arrays() {
_ = a == b
_ = a != b
_ = a /* ERROR < not defined */ < b
- _ = a == nil /* ERROR cannot convert */
+ _ = a /* ERROR cannot compare.*mismatched types */ == nil
type C [10]int
var c C
@@ -53,7 +53,7 @@ func structs() {
_ = s == t
_ = s != t
_ = s /* ERROR < not defined */ < t
- _ = s == nil /* ERROR cannot convert */
+ _ = s /* ERROR cannot compare.*mismatched types */ == nil
type S struct {
x int
diff --git a/libgo/go/go/types/testdata/check/expr3.src b/libgo/go/go/types/testdata/check/expr3.src
index 0525a5a..b8f96dc 100644
--- a/libgo/go/go/types/testdata/check/expr3.src
+++ b/libgo/go/go/types/testdata/check/expr3.src
@@ -44,9 +44,9 @@ func indexes() {
_ = a[:10:10]
_ = a[:11 /* ERROR "index .* out of bounds" */ :10]
_ = a[:10:11 /* ERROR "index .* out of bounds" */ ]
- _ = a[10:0:10] /* ERROR swapped slice indices" */
- _ = a[0:10:0] /* ERROR "swapped slice indices" */
- _ = a[10:0:0] /* ERROR "swapped slice indices" */
+ _ = a[10:0 /* ERROR "invalid slice indices" */ :10]
+ _ = a[0:10:0 /* ERROR "invalid slice indices" */ ]
+ _ = a[10:0 /* ERROR "invalid slice indices" */:0]
_ = &a /* ERROR "cannot take address" */ [:10]
pa := &a
@@ -62,9 +62,9 @@ func indexes() {
_ = pa[:10:10]
_ = pa[:11 /* ERROR "index .* out of bounds" */ :10]
_ = pa[:10:11 /* ERROR "index .* out of bounds" */ ]
- _ = pa[10:0:10] /* ERROR "swapped slice indices" */
- _ = pa[0:10:0] /* ERROR "swapped slice indices" */
- _ = pa[10:0:0] /* ERROR "swapped slice indices" */
+ _ = pa[10:0 /* ERROR "invalid slice indices" */ :10]
+ _ = pa[0:10:0 /* ERROR "invalid slice indices" */ ]
+ _ = pa[10:0 /* ERROR "invalid slice indices" */ :0]
_ = &pa /* ERROR "cannot take address" */ [:10]
var b [0]int
@@ -82,16 +82,16 @@ func indexes() {
_ = s[: - /* ERROR "negative" */ 1]
_ = s[0]
_ = s[1:2]
- _ = s[2:1] /* ERROR "swapped slice indices" */
+ _ = s[2:1 /* ERROR "invalid slice indices" */ ]
_ = s[2:]
_ = s[: 1 /* ERROR "overflows" */ <<100]
_ = s[1 /* ERROR "overflows" */ <<100 :]
_ = s[1 /* ERROR "overflows" */ <<100 : 1 /* ERROR "overflows" */ <<100]
_ = s[: /* ERROR "2nd index required" */ : /* ERROR "3rd index required" */ ]
_ = s[:10:10]
- _ = s[10:0:10] /* ERROR "swapped slice indices" */
- _ = s[0:10:0] /* ERROR "swapped slice indices" */
- _ = s[10:0:0] /* ERROR "swapped slice indices" */
+ _ = s[10:0 /* ERROR "invalid slice indices" */ :10]
+ _ = s[0:10:0 /* ERROR "invalid slice indices" */ ]
+ _ = s[10:0 /* ERROR "invalid slice indices" */ :0]
_ = &s /* ERROR "cannot take address" */ [:10]
var m map[string]int
@@ -103,14 +103,14 @@ func indexes() {
var ok mybool
_, ok = m["bar"]
_ = ok
- _ = m[0 /* ERROR "cannot use 0" */ ] + "foo" // ERROR "cannot convert"
+ _ = m/* ERROR "mismatched types int and untyped string" */[0 /* ERROR "cannot use 0" */ ] + "foo"
var t string
_ = t[- /* ERROR "negative" */ 1]
_ = t[- /* ERROR "negative" */ 1 :]
_ = t[: - /* ERROR "negative" */ 1]
- _ = t /* ERROR "3-index slice of string" */ [1:2:3]
- _ = "foo" /* ERROR "3-index slice of string" */ [1:2:3]
+ _ = t[1:2:3 /* ERROR "3-index slice of string" */ ]
+ _ = "foo"[1:2:3 /* ERROR "3-index slice of string" */ ]
var t0 byte
t0 = t[0]
_ = t0
@@ -458,7 +458,7 @@ func type_asserts() {
var t I
_ = t /* ERROR "use of .* outside type switch" */ .(type)
- _ = t /* ERROR "missing method m" */ .(T)
+ _ = t /* ERROR "m has pointer receiver" */ .(T)
_ = t.(*T)
_ = t /* ERROR "missing method m" */ .(T1)
_ = t /* ERROR "wrong type for method m" */ .(T2)
@@ -493,20 +493,20 @@ func _calls() {
f1(0)
f1(x)
f1(10.0)
- f1() /* ERROR "not enough arguments" */
- f1(x, y /* ERROR "too many arguments" */ )
+ f1() /* ERROR "not enough arguments in call to f1\n\thave \(\)\n\twant \(int\)" */
+ f1(x, y /* ERROR "too many arguments in call to f1\n\thave \(int, float32\)\n\twant \(int\)" */ )
f1(s /* ERROR "cannot use .* in argument" */ )
f1(x ... /* ERROR "cannot use ..." */ )
f1(g0 /* ERROR "used as value" */ ())
f1(g1())
- f1(g2 /* ERROR "too many arguments" */ ())
+ f1(g2 /* ERROR "too many arguments in call to f1\n\thave \(float32, string\)\n\twant \(int\)" */ ())
- f2() /* ERROR "not enough arguments" */
- f2(3.14) /* ERROR "not enough arguments" */
+ f2() /* ERROR "not enough arguments in call to f2\n\thave \(\)\n\twant \(float32, string\)" */
+ f2(3.14) /* ERROR "not enough arguments in call to f2\n\thave \(number\)\n\twant \(float32, string\)" */
f2(3.14, "foo")
f2(x /* ERROR "cannot use .* in argument" */ , "foo")
f2(g0 /* ERROR "used as value" */ ())
- f2(g1()) /* ERROR "not enough arguments" */
+ f2(g1()) /* ERROR "not enough arguments in call to f2\n\thave \(int\)\n\twant \(float32, string\)" */
f2(g2())
fs() /* ERROR "not enough arguments" */
diff --git a/libgo/go/go/types/testdata/check/issues.go2 b/libgo/go/go/types/testdata/check/issues.go2
index 8994164..c164825 100644
--- a/libgo/go/go/types/testdata/check/issues.go2
+++ b/libgo/go/go/types/testdata/check/issues.go2
@@ -24,55 +24,46 @@ func _() {
eql[io.Reader](nil, nil)
}
-// If we have a receiver of pointer type (below: *T) we must ignore
-// the pointer in the implementation of the method lookup because
-// the type bound of T is an interface and pointer to interface types
-// have no methods and then the lookup would fail.
+// If we have a receiver of pointer to type parameter type (below: *T)
+// we don't have any methods, like for interfaces.
type C[T any] interface {
m()
}
// using type bound C
func _[T C[T]](x *T) {
- x.m()
+ x.m /* ERROR x\.m undefined */ ()
}
// using an interface literal as bound
func _[T interface{ m() }](x *T) {
- x.m()
+ x.m /* ERROR x\.m undefined */ ()
}
-// In a generic function body all method calls will be pointer method calls.
-// If necessary, the function body will insert temporary variables, not seen
-// by the user, in order to get an addressable variable to use to call the method.
-// Thus, assume an argument type for a generic function to be the type of addressable
-// values in the generic function when checking if the argument type satisfies the
-// generic function's type bound.
-func f2[_ interface{ m1(); m2() }]()
+func f2[_ interface{ m1(); m2() }]() {}
type T struct{}
func (T) m1()
func (*T) m2()
func _() {
- // TODO(rFindley) this error should be positioned on the 'T'.
- f2 /* ERROR wrong method signature */ [T]()
+ f2[T /* ERROR m2 has pointer receiver */ ]()
f2[*T]()
}
// When a type parameter is used as an argument to instantiate a parameterized
-// type with a type list constraint, all of the type argument's types in its
-// bound, but at least one (!), must be in the type list of the bound of the
+// type with a type set constraint, all of the type argument's types in its
+// bound, but at least one (!), must be in the type set of the bound of the
// corresponding parameterized type's type parameter.
-type T1[P interface{type uint}] struct{}
+type T1[P interface{~uint}] struct{}
func _[P any]() {
- _ = T1[P /* ERROR P has no type constraints */ ]{}
+ _ = T1[P /* ERROR P does not implement interface{~uint} */ ]{}
}
// This is the original (simplified) program causing the same issue.
type Unsigned interface {
- type uint
+ ~uint
}
type T2[U Unsigned] struct {
@@ -83,8 +74,8 @@ func (u T2[U]) Add1() U {
return u.s + 1
}
-func NewT2[U any]() T2[U /* ERROR U has no type constraints */ ] {
- return T2[U /* ERROR U has no type constraints */ ]{}
+func NewT2[U any]() T2[U /* ERROR U does not implement Unsigned */ ] {
+ return T2[U /* ERROR U does not implement Unsigned */ ]{}
}
func _() {
@@ -160,10 +151,10 @@ type inf2[T any] struct{ inf2 /* ERROR illegal cycle */ [T] }
// The implementation of conversions T(x) between integers and floating-point
// numbers checks that both T and x have either integer or floating-point
// type. When the type of T or x is a type parameter, the respective simple
-// predicate disjunction in the implementation was wrong because if a type list
+// predicate disjunction in the implementation was wrong because if a term list
// contains both an integer and a floating-point type, the type parameter is
// neither an integer or a floating-point number.
-func convert[T1, T2 interface{type int, uint, float32}](v T1) T2 {
+func convert[T1, T2 interface{~int | ~uint | ~float32}](v T1) T2 {
return T2(v)
}
@@ -175,12 +166,12 @@ func _() {
// both numeric, or both strings. The implementation had the same problem
// with this check as the conversion issue above (issue #39623).
-func issue39623[T interface{type int, string}](x, y T) T {
+func issue39623[T interface{~int | ~string}](x, y T) T {
return x + y
}
// Simplified, from https://go2goplay.golang.org/p/efS6x6s-9NI:
-func Sum[T interface{type int, string}](s []T) (sum T) {
+func Sum[T interface{~int | ~string}](s []T) (sum T) {
for _, v := range s {
sum += v
}
@@ -189,19 +180,19 @@ func Sum[T interface{type int, string}](s []T) (sum T) {
// Assignability of an unnamed pointer type to a type parameter that
// has a matching underlying type.
-func _[T interface{}, PT interface{type *T}] (x T) PT {
+func _[T interface{}, PT interface{~*T}] (x T) PT {
return &x
}
-// Indexing of generic types containing type parameters in their type list:
-func at[T interface{ type []E }, E interface{}](x T, i int) E {
+// Indexing of generic types containing type parameters in their term list:
+func at[T interface{ ~[]E }, E interface{}](x T, i int) E {
return x[i]
}
// A generic type inside a function acts like a named type. Its underlying
-// type is itself, its "operational type" is defined by the type list in
+// type is itself, its "operational type" is defined by the term list in
// the tybe bound, if any.
-func _[T interface{type int}](x T) {
+func _[T interface{~int}](x T) {
type myint int
var _ int = int(x)
var _ T = 42
@@ -210,38 +201,45 @@ func _[T interface{type int}](x T) {
// Indexing a generic type with an array type bound checks length.
// (Example by mdempsky@.)
-func _[T interface { type [10]int }](x T) {
+func _[T interface { ~[10]int }](x T) {
_ = x[9] // ok
_ = x[20 /* ERROR out of bounds */ ]
}
// Pointer indirection of a generic type.
-func _[T interface{ type *int }](p T) int {
+func _[T interface{ ~*int }](p T) int {
return *p
}
// Channel sends and receives on generic types.
-func _[T interface{ type chan int }](ch T) int {
+func _[T interface{ ~chan int }](ch T) int {
ch <- 0
return <- ch
}
// Calling of a generic variable.
-func _[T interface{ type func() }](f T) {
+func _[T interface{ ~func() }](f T) {
f()
go f()
}
-// We must compare against the underlying type of type list entries
+type F1 func()
+type F2 func()
+func _[T interface{ func()|F1|F2 }](f T) {
+ f()
+ go f()
+}
+
+// We must compare against the underlying type of term list entries
// when checking if a constraint is satisfied by a type. The under-
-// lying type of each type list entry must be computed after the
+// lying type of each term list entry must be computed after the
// interface has been instantiated as its typelist may contain a
// type parameter that was substituted with a defined type.
// Test case from an (originally) failing example.
-type sliceOf[E any] interface{ type []E }
+type sliceOf[E any] interface{ ~[]E }
-func append[T interface{}, S sliceOf[T], T2 interface{ type T }](s S, t ...T2) S
+func append[T interface{}, S sliceOf[T], T2 interface{}](s S, t ...T2) S { panic(0) }
var f func()
var cancelSlice []context.CancelFunc
@@ -249,7 +247,7 @@ var _ = append[context.CancelFunc, []context.CancelFunc, context.CancelFunc](can
// A generic function must be instantiated with a type, not a value.
-func g[T any](T) T
+func g[T any](T) T { panic(0) }
var _ = g[int]
var _ = g[nil /* ERROR is not a type */ ]
diff --git a/libgo/go/go/types/testdata/check/issues.src b/libgo/go/go/types/testdata/check/issues.src
index 55fe220..0b77b0e 100644
--- a/libgo/go/go/types/testdata/check/issues.src
+++ b/libgo/go/go/types/testdata/check/issues.src
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package issues
+package go1_17 // don't permit non-interface elements in interfaces
import (
"fmt"
@@ -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 */
@@ -165,8 +165,8 @@ func issue10260() {
_ = map[int]I1{0: i0 /* ERROR cannot use .* missing method foo */ }
_ = map[int]I1{0: i2 /* ERROR cannot use .* wrong type for method foo */ }
- make(chan I1) <- i0 /* ERROR cannot use .* in send: missing method foo */
- make(chan I1) <- i2 /* ERROR cannot use .* in send: wrong type for method foo */
+ make(chan I1) <- i0 /* ERROR I0 does not implement I1: missing method foo */
+ make(chan I1) <- i2 /* ERROR wrong type for method foo \(have func\(x int\), want func\(\)\) */
}
// Check that constants representable as integers are in integer form
@@ -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/libgo/go/go/types/testdata/check/linalg.go2 b/libgo/go/go/types/testdata/check/linalg.go2
index 0d27603..f02e773 100644
--- a/libgo/go/go/types/testdata/check/linalg.go2
+++ b/libgo/go/go/types/testdata/check/linalg.go2
@@ -4,15 +4,13 @@
package linalg
-import "math"
-
// Numeric is type bound that matches any numeric type.
// It would likely be in a constraints package in the standard library.
type Numeric interface {
- type int, int8, int16, int32, int64,
- uint, uint8, uint16, uint32, uint64, uintptr,
- float32, float64,
- complex64, complex128
+ ~int | ~int8 | ~int16 | ~int32 | ~int64 |
+ ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
+ ~float32 | ~float64 |
+ ~complex64 | ~complex128
}
func DotProduct[T Numeric](s1, s2 []T) T {
@@ -42,42 +40,43 @@ func AbsDifference[T NumericAbs[T]](a, b T) T {
// OrderedNumeric is a type bound that matches numeric types that support the < operator.
type OrderedNumeric interface {
- type int, int8, int16, int32, int64,
- uint, uint8, uint16, uint32, uint64, uintptr,
- float32, float64
+ ~int | ~int8 | ~int16 | ~int32 | ~int64 |
+ ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
+ ~float32 | ~float64
}
// Complex is a type bound that matches the two complex types, which do not have a < operator.
type Complex interface {
- type complex64, complex128
-}
-
-// OrderedAbs is a helper type that defines an Abs method for
-// ordered numeric types.
-type OrderedAbs[T OrderedNumeric] T
-
-func (a OrderedAbs[T]) Abs() OrderedAbs[T] {
- if a < 0 {
- return -a
- }
- return a
+ ~complex64 | ~complex128
}
-// ComplexAbs is a helper type that defines an Abs method for
-// complex types.
-type ComplexAbs[T Complex] T
-
-func (a ComplexAbs[T]) Abs() ComplexAbs[T] {
- r := float64(real(a))
- i := float64(imag(a))
- d := math.Sqrt(r * r + i * i)
- return ComplexAbs[T](complex(d, 0))
-}
-
-func OrderedAbsDifference[T OrderedNumeric](a, b T) T {
- return T(AbsDifference(OrderedAbs[T](a), OrderedAbs[T](b)))
-}
-
-func ComplexAbsDifference[T Complex](a, b T) T {
- return T(AbsDifference(ComplexAbs[T](a), ComplexAbs[T](b)))
-}
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// // OrderedAbs is a helper type that defines an Abs method for
+// // ordered numeric types.
+// type OrderedAbs[T OrderedNumeric] T
+//
+// func (a OrderedAbs[T]) Abs() OrderedAbs[T] {
+// if a < 0 {
+// return -a
+// }
+// return a
+// }
+//
+// // ComplexAbs is a helper type that defines an Abs method for
+// // complex types.
+// type ComplexAbs[T Complex] T
+//
+// func (a ComplexAbs[T]) Abs() ComplexAbs[T] {
+// r := float64(real(a))
+// i := float64(imag(a))
+// d := math.Sqrt(r * r + i * i)
+// return ComplexAbs[T](complex(d, 0))
+// }
+//
+// func OrderedAbsDifference[T OrderedNumeric](a, b T) T {
+// return T(AbsDifference(OrderedAbs[T](a), OrderedAbs[T](b)))
+// }
+//
+// func ComplexAbsDifference[T Complex](a, b T) T {
+// return T(AbsDifference(ComplexAbs[T](a), ComplexAbs[T](b)))
+// }
diff --git a/libgo/go/go/types/testdata/check/main.go2 b/libgo/go/go/types/testdata/check/main.go2
index 65e9aa2..fb567a0 100644
--- a/libgo/go/go/types/testdata/check/main.go2
+++ b/libgo/go/go/types/testdata/check/main.go2
@@ -4,4 +4,4 @@
package main
-func main[ /* ERROR "func main must have no type parameters" */ T any]() {}
+func main[T /* ERROR "func main must have no type parameters" */ any]() {}
diff --git a/libgo/go/go/types/testdata/check/map2.go2 b/libgo/go/go/types/testdata/check/map2.go2
index 2833445..e13bf33 100644
--- a/libgo/go/go/types/testdata/check/map2.go2
+++ b/libgo/go/go/types/testdata/check/map2.go2
@@ -114,7 +114,7 @@ func (it *Iterator[K, V]) Next() (K, V, bool) {
// chans
-func chans_Ranger[T any]() (*chans_Sender[T], *chans_Receiver[T])
+func chans_Ranger[T any]() (*chans_Sender[T], *chans_Receiver[T]) { panic(0) }
// A sender is used to send values to a Receiver.
type chans_Sender[T any] struct {
@@ -143,4 +143,4 @@ type chans_Receiver[T any] struct {
func (r *chans_Receiver[T]) Next() (T, bool) {
v, ok := <-r.values
return v, ok
-} \ No newline at end of file
+}
diff --git a/libgo/go/go/types/testdata/check/methodsets.src b/libgo/go/go/types/testdata/check/methodsets.src
index 9fb10de..b0eb14cf 100644
--- a/libgo/go/go/types/testdata/check/methodsets.src
+++ b/libgo/go/go/types/testdata/check/methodsets.src
@@ -196,9 +196,9 @@ func issue5918() {
_ func(error) string = error.Error
perr = &err
- _ = perr.Error /* ERROR "no field or method" */ ()
- _ func() string = perr.Error /* ERROR "no field or method" */
- _ func(*error) string = (*error).Error /* ERROR "no field or method" */
+ _ = perr.Error /* ERROR "type \*error is pointer to interface, not interface" */ ()
+ _ func() string = perr.Error /* ERROR "type \*error is pointer to interface, not interface" */
+ _ func(*error) string = (*error).Error /* ERROR "type \*error is pointer to interface, not interface" */
)
type T *interface{ m() int }
@@ -207,8 +207,8 @@ func issue5918() {
_ = (*x).m()
_ = (*x).m
- _ = x.m /* ERROR "no field or method" */ ()
- _ = x.m /* ERROR "no field or method" */
- _ = T.m /* ERROR "no field or method" */
+ _ = x.m /* ERROR "type T is pointer to interface, not interface" */ ()
+ _ = x.m /* ERROR "type T is pointer to interface, not interface" */
+ _ = T.m /* ERROR "type T is pointer to interface, not interface" */
)
}
diff --git a/libgo/go/go/types/testdata/check/shifts.src b/libgo/go/go/types/testdata/check/shifts.src
index 4d3c59a..16a67ae 100644
--- a/libgo/go/go/types/testdata/check/shifts.src
+++ b/libgo/go/go/types/testdata/check/shifts.src
@@ -380,7 +380,7 @@ func issue21727() {
var a = make([]int, 1<<s + 1.2 /* ERROR "truncated to int" */ )
var _ = a[1<<s - 2.3 /* ERROR "truncated to int" */ ]
var _ int = 1<<s + 3.4 /* ERROR "truncated to int" */
- var _ = string(1 << s)
+ var _ = string(1 /* ERROR shifted operand 1 .* must be integer */ << s)
var _ = string(1.0 /* ERROR "cannot convert" */ << s)
}
diff --git a/libgo/go/go/types/testdata/check/stmt0.src b/libgo/go/go/types/testdata/check/stmt0.src
index 76b6e70..ec8bf71 100644
--- a/libgo/go/go/types/testdata/check/stmt0.src
+++ b/libgo/go/go/types/testdata/check/stmt0.src
@@ -29,10 +29,10 @@ func assignments0() (int, int) {
a, b, c = <- /* ERROR "cannot assign [1-9]+ values to [1-9]+ variables" */ ch
- return /* ERROR "wrong number of return values" */
- return /* ERROR "wrong number of return values" */ 1
+ return /* ERROR "not enough return values\n\thave \(\)\n\twant \(int, int\)" */
+ return 1 /* ERROR "not enough return values\n\thave \(number\)\n\twant \(int, int\)" */
return 1, 2
- return /* ERROR "wrong number of return values" */ 1, 2, 3
+ return 1, 2, 3 /* ERROR "too many return values\n\thave \(number, number, number\)\n\twant \(int, int\)" */
}
func assignments1() {
@@ -49,18 +49,18 @@ func assignments1() {
b = true
i += 1
- i += "foo" /* ERROR "cannot convert.*int" */
+ i /* ERROR "mismatched types int and untyped string" */+= "foo"
f -= 1
f /= 0
f = float32(0)/0 /* ERROR "division by zero" */
- f -= "foo" /* ERROR "cannot convert.*float64" */
+ f /* ERROR "mismatched types float64 and untyped string" */-= "foo"
c *= 1
c /= 0
s += "bar"
- s += 1 /* ERROR "cannot convert.*string" */
+ s /* ERROR "mismatched types string and untyped int" */+= 1
var u64 uint64
u64 += 1<<u64
@@ -69,10 +69,10 @@ func assignments1() {
// test cases for issue 5800
var (
- _ int = nil /* ERROR "untyped nil value" */
- _ [10]int = nil /* ERROR "untyped nil value" */
+ _ int = nil /* ERROR "cannot use nil as int value in variable declaration" */
+ _ [10]int = nil /* ERROR "cannot use nil as \[10\]int value in variable declaration" */
_ []byte = nil
- _ struct{} = nil /* ERROR "untyped nil value" */
+ _ struct{} = nil /* ERROR "cannot use nil as struct{} value in variable declaration" */
_ func() = nil
_ map[int]string = nil
_ chan int = nil
@@ -81,7 +81,7 @@ func assignments1() {
// test cases for issue 5500
_ = func() (int, bool) {
var m map[int]int
- return /* ERROR "wrong number of return values" */ m[0]
+ return m /* ERROR "not enough return values" */ [0]
}
g := func(int, bool){}
@@ -375,20 +375,20 @@ func continues() {
func returns0() {
return
- return 0 /* ERROR no result values expected */
+ return 0 /* ERROR too many return values */
}
func returns1(x float64) (int, *float64) {
return 0, &x
- return /* ERROR wrong number of return values */
+ return /* ERROR not enough return values */
return "foo" /* ERROR "cannot .* in return statement" */, x /* ERROR "cannot use .* in return statement" */
- return /* ERROR wrong number of return values */ 0, &x, 1
+ return 0, &x, 1 /* ERROR too many return values */
}
func returns2() (a, b int) {
return
return 1, "foo" /* ERROR cannot use .* in return statement */
- return /* ERROR wrong number of return values */ 1, 2, 3
+ return 1, 2, 3 /* ERROR too many return values */
{
type a int
return 1, 2
@@ -695,7 +695,7 @@ func typeswitches() {
_ = y
}
- switch x := i /* ERROR "not an interface" */ .(type) {}
+ switch x /* ERROR "x declared but not used" */ := i /* ERROR "not an interface" */ .(type) {}
switch t := x.(type) {
case nil:
@@ -719,6 +719,18 @@ func typeswitches() {
case T2 /* ERROR "wrong type for method m" */ :
case I2 /* STRICT "wrong type for method m" */ : // only an error in strict mode (issue 8561)
}
+
+
+ {
+ x := 1
+ v := 2
+ switch v /* ERROR "v [(]variable of type int[)] is not an interface" */ .(type) {
+ case int:
+ println(x)
+ println(x / 0 /* ERROR "invalid operation: division by zero" */)
+ case 1 /* ERROR "expected type, found 1" */:
+ }
+ }
}
// Test that each case clause uses the correct type of the variable
@@ -937,13 +949,13 @@ func issue6766b() {
// errors reported).
func issue10148() {
for y /* ERROR declared but not used */ := range "" {
- _ = "" /* ERROR cannot convert */ + 1
+ _ = "" /* ERROR mismatched types untyped string and untyped int */ + 1
}
for range 1 /* ERROR cannot range over 1 */ {
- _ = "" /* ERROR cannot convert */ + 1
+ _ = "" /* ERROR mismatched types untyped string and untyped int */ + 1
}
for y := range 1 /* ERROR cannot range over 1 */ {
- _ = "" /* ERROR cannot convert */ + 1
+ _ = "" /* ERROR mismatched types untyped string and untyped int */ + 1
}
}
diff --git a/libgo/go/go/types/testdata/check/tinference.go2 b/libgo/go/go/types/testdata/check/tinference.go2
deleted file mode 100644
index 31338b33..0000000
--- a/libgo/go/go/types/testdata/check/tinference.go2
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2020 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.
-
-package tinferenceB
-
-import "strconv"
-
-type any interface{}
-
-// TODO(rFindley) the below partially applied function types should probably
-// not be permitted (spec question).
-
-func f0[A any, B interface{type C}, C interface{type D}, D interface{type A}](A, B, C, D)
-func _() {
- f := f0[string]
- f("a", "b", "c", "d")
- f0("a", "b", "c", "d")
-}
-
-func f1[A any, B interface{type A}](A, B)
-func _() {
- f := f1[int]
- f(int(0), int(0))
- f1(int(0), int(0))
-}
-
-func f2[A any, B interface{type []A}](A, B)
-func _() {
- f := f2[byte]
- f(byte(0), []byte{})
- f2(byte(0), []byte{})
-}
-
-func f3[A any, B interface{type C}, C interface{type *A}](A, B, C)
-func _() {
- f := f3[int]
- var x int
- f(x, &x, &x)
- f3(x, &x, &x)
-}
-
-func f4[A any, B interface{type []C}, C interface{type *A}](A, B, C)
-func _() {
- f := f4[int]
- var x int
- f(x, []*int{}, &x)
- f4(x, []*int{}, &x)
-}
-
-func f5[A interface{type struct{b B; c C}}, B any, C interface{type *B}](x B) A
-func _() {
- x := f5(1.2)
- var _ float64 = x.b
- var _ float64 = *x.c
-}
-
-func f6[A any, B interface{type struct{f []A}}](B) A
-func _() {
- x := f6(struct{f []string}{})
- 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{type A}]()
-
-// More realistic examples
-
-func Double[S interface{ type []E }, E interface{ type int, int8, int16, int32, int64 }](s S) S {
- r := make(S, len(s))
- for i, v := range s {
- r[i] = v + v
- }
- return r
-}
-
-type MySlice []int
-
-var _ = Double(MySlice{1})
-
-// From the draft design.
-
-type Setter[B any] interface {
- Set(string)
- type *B
-}
-
-func FromStrings[T interface{}, PT Setter[T]](s []string) []T {
- result := make([]T, len(s))
- for i, v := range s {
- // The type of &result[i] is *T which is in the type list
- // of Setter2, so we can convert it to PT.
- p := PT(&result[i])
- // PT has a Set method.
- p.Set(v)
- }
- return result
-}
-
-type Settable int
-
-func (p *Settable) Set(s string) {
- i, _ := strconv.Atoi(s) // real code should not ignore the error
- *p = Settable(i)
-}
-
-var _ = FromStrings[Settable]([]string{"1", "2"})
diff --git a/libgo/go/go/types/testdata/check/tmp.go2 b/libgo/go/go/types/testdata/check/tmp.go2
deleted file mode 100644
index dae78ca..0000000
--- a/libgo/go/go/types/testdata/check/tmp.go2
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2020 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.
-
-// This file is meant as "dumping ground" for debugging code.
-
-package p
-
-// fun test case
-type C[P interface{m()}] P
-
-func (r C[P]) m() { r.m() }
-
-func f[T interface{m(); n()}](x T) {
- y := C[T](x)
- y.m()
-}
diff --git a/libgo/go/go/types/testdata/check/typeinst.go2 b/libgo/go/go/types/testdata/check/typeinst.go2
index 3184a4b..6423cb8 100644
--- a/libgo/go/go/types/testdata/check/typeinst.go2
+++ b/libgo/go/go/types/testdata/check/typeinst.go2
@@ -8,7 +8,8 @@ type myInt int
// Parameterized type declarations
-type T1[P any] P
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+type T1[P any] P // ERROR cannot use a type parameter as RHS in type declaration
type T2[P any] struct {
f P
@@ -17,13 +18,15 @@ type T2[P any] struct {
type List[P any] []P
-// Alias type declarations cannot have type parameters. Syntax error.
-type A1[P any] = /* ERROR cannot be alias */ P
+// Alias type declarations cannot have type parameters.
+// Issue #46477 proposses to change that.
+type A1[P any] = /* ERROR cannot be alias */ struct{}
-// But an alias may refer to a generic, uninstantiated type.
-type A2 = List
+// Pending clarification of #46477 we disallow aliases
+// of generic types.
+type A2 = List // ERROR cannot use generic type
var _ A2[int]
-var _ A2 /* ERROR without instantiation */
+var _ A2
type A3 = List[int]
var _ A3
@@ -33,11 +36,11 @@ var _ A3
var x int
type _ x /* ERROR not a type */ [int]
-type _ int /* ERROR not a generic type */ []
-type _ myInt /* ERROR not a generic type */ []
+type _ int /* ERROR not a generic type */ [] // ERROR expected type argument list
+type _ myInt /* ERROR not a generic type */ [] // ERROR expected type argument list
// TODO(gri) better error messages
-type _ T1 /* ERROR got 0 arguments but 1 type parameters */ []
+type _ T1[] // ERROR expected type argument list
type _ T1[x /* ERROR not a type */ ]
type _ T1 /* ERROR got 2 arguments but 1 type parameters */ [int, float32]
diff --git a/libgo/go/go/types/testdata/check/typeinst2.go2 b/libgo/go/go/types/testdata/check/typeinst2.go2
index 6e2104a..1c3eb21 100644
--- a/libgo/go/go/types/testdata/check/typeinst2.go2
+++ b/libgo/go/go/types/testdata/check/typeinst2.go2
@@ -85,27 +85,29 @@ type NumericAbs[T any] interface {
Abs() T
}
-func AbsDifference[T NumericAbs[T]](x T)
+func AbsDifference[T NumericAbs[T]](x T) { panic(0) }
-type OrderedAbs[T any] T
-
-func (a OrderedAbs[T]) Abs() OrderedAbs[T]
-
-func OrderedAbsDifference[T any](x T) {
- AbsDifference(OrderedAbs[T](x))
-}
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// type OrderedAbs[T any] T
+//
+// func (a OrderedAbs[T]) Abs() OrderedAbs[T]
+//
+// func OrderedAbsDifference[T any](x T) {
+// AbsDifference(OrderedAbs[T](x))
+// }
// same code, reduced to essence
-func g[P interface{ m() P }](x P)
+func g[P interface{ m() P }](x P) { panic(0) }
-type T4[P any] P
-
-func (_ T4[P]) m() T4[P]
-
-func _[Q any](x Q) {
- g(T4[Q](x))
-}
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// type T4[P any] P
+//
+// func (_ T4[P]) m() T4[P]
+//
+// func _[Q any](x Q) {
+// g(T4[Q](x))
+// }
// Another test case that caused problems in the past
@@ -148,78 +150,76 @@ func _[T any](r R2[T, int], p *R2[string, T]) {
p.pm()
}
-// An interface can (explicitly) declare at most one type list.
+// It is ok to have multiple embedded unions.
type _ interface {
m0()
- type int, string, bool
- type /* ERROR multiple type lists */ float32, float64
+ ~int | ~string | ~bool
+ ~float32 | ~float64
m1()
m2()
- type /* ERROR multiple type lists */ complex64, complex128
- type /* ERROR multiple type lists */ rune
+ ~complex64 | ~complex128
+ ~rune
}
-// Interface type lists may contain each type at most once.
-// (If there are multiple lists, we assume the author intended
-// for them to be all in a single list, and we report the error
-// as well.)
+// Type sets may contain each type at most once.
type _ interface {
- type int, int /* ERROR duplicate type int */
- type /* ERROR multiple type lists */ int /* ERROR duplicate type int */
+ ~int|~ /* ERROR overlapping terms ~int */ int
+ ~int|int /* ERROR overlapping terms int */
+ int|int /* ERROR overlapping terms int */
}
type _ interface {
- type struct{f int}, struct{g int}, struct /* ERROR duplicate type */ {f int}
+ ~struct{f int} | ~struct{g int} | ~ /* ERROR overlapping terms */ struct {f int}
}
-// Interface type lists can contain any type, incl. *Named types.
+// Interface term lists can contain any type, incl. *Named types.
// Verify that we use the underlying type to compute the operational type.
type MyInt int
-func add1[T interface{type MyInt}](x T) T {
+func add1[T interface{MyInt}](x T) T {
return x + 1
}
type MyString string
-func double[T interface{type MyInt, MyString}](x T) T {
+func double[T interface{MyInt|MyString}](x T) T {
return x + x
}
-// Embedding of interfaces with type lists leads to interfaces
-// with type lists that are the intersection of the embedded
-// type lists.
+// Embedding of interfaces with term lists leads to interfaces
+// with term lists that are the intersection of the embedded
+// term lists.
type E0 interface {
- type int, bool, string
+ ~int | ~bool | ~string
}
type E1 interface {
- type int, float64, string
+ ~int | ~float64 | ~string
}
type E2 interface {
- type float64
+ ~float64
}
type I0 interface {
E0
}
-func f0[T I0]()
+func f0[T I0]() {}
var _ = f0[int]
var _ = f0[bool]
var _ = f0[string]
-var _ = f0[float64 /* ERROR does not satisfy I0 */ ]
+var _ = f0[float64 /* ERROR does not implement I0 */ ]
type I01 interface {
E0
E1
}
-func f01[T I01]()
+func f01[T I01]() {}
var _ = f01[int]
-var _ = f01[bool /* ERROR does not satisfy I0 */ ]
+var _ = f01[bool /* ERROR does not implement I0 */ ]
var _ = f01[string]
-var _ = f01[float64 /* ERROR does not satisfy I0 */ ]
+var _ = f01[float64 /* ERROR does not implement I0 */ ]
type I012 interface {
E0
@@ -227,30 +227,54 @@ type I012 interface {
E2
}
-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 */ ]
+func f012[T I012]() {}
+var _ = f012[int /* ERROR cannot implement I012.*empty type set */ ]
+var _ = f012[bool /* ERROR cannot implement I012.*empty type set */ ]
+var _ = f012[string /* ERROR cannot implement I012.*empty type set */ ]
+var _ = f012[float64 /* ERROR cannot implement I012.*empty type set */ ]
type I12 interface {
E1
E2
}
-func f12[T I12]()
-var _ = f12[int /* ERROR does not satisfy I12 */ ]
-var _ = f12[bool /* ERROR does not satisfy I12 */ ]
-var _ = f12[string /* ERROR does not satisfy I12 */ ]
+func f12[T I12]() {}
+var _ = f12[int /* ERROR does not implement I12 */ ]
+var _ = f12[bool /* ERROR does not implement I12 */ ]
+var _ = f12[string /* ERROR does not implement I12 */ ]
var _ = f12[float64]
type I0_ interface {
E0
- type int
+ ~int
}
-func f0_[T I0_]()
+func f0_[T I0_]() {}
var _ = f0_[int]
-var _ = f0_[bool /* ERROR does not satisfy I0_ */ ]
-var _ = f0_[string /* ERROR does not satisfy I0_ */ ]
-var _ = f0_[float64 /* ERROR does not satisfy I0_ */ ]
+var _ = f0_[bool /* ERROR does not implement I0_ */ ]
+var _ = f0_[string /* ERROR does not implement I0_ */ ]
+var _ = f0_[float64 /* ERROR does not implement 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 cannot implement none \(empty type set\) */ ]
+ _ = ff[T] // pathological but ok because T's type set is empty, too
+ _ = gg[int]
+ _ = gg[T]
+ _ = hh[int]
+ _ = hh[T]
+}
diff --git a/libgo/go/go/types/testdata/check/typeparams.go2 b/libgo/go/go/types/testdata/check/typeparams.go2
index d95e02e..6d63d59 100644
--- a/libgo/go/go/types/testdata/check/typeparams.go2
+++ b/libgo/go/go/types/testdata/check/typeparams.go2
@@ -6,18 +6,16 @@ package p
// import "io" // for type assertion tests
-// The predeclared identifier "any" is only visible as a constraint
-// in a type parameter list.
-var _ any // ERROR undeclared
-func _[_ any /* ok here */ , _ interface{any /* ERROR undeclared */ }](any /* ERROR undeclared */ ) {
- var _ any /* ERROR undeclared */
+var _ any // ok to use any anywhere
+func _[_ any, _ interface{any}](any) {
+ var _ any
}
func identity[T any](x T) T { return x }
-func _[_ any](x int) int
-func _[T any](T /* ERROR redeclared */ T)()
-func _[T, T /* ERROR redeclared */ any]()
+func _[_ any](x int) int { panic(0) }
+func _[T any](T /* ERROR redeclared */ T)() {}
+func _[T, T /* ERROR redeclared */ any]() {}
// Constraints (incl. any) may be parenthesized.
func _[_ (any)]() {}
@@ -52,22 +50,22 @@ func swapswap[A, B any](a A, b B) (A, B) {
type F[A, B any] func(A, B) (B, A)
-func min[T interface{ type int }](x, y T) T {
+func min[T interface{ ~int }](x, y T) T {
if x < y {
return x
}
return y
}
-func _[T interface{type int, float32}](x, y T) bool { return x < y }
+func _[T interface{~int | ~float32}](x, y T) bool { return x < y }
func _[T any](x, y T) bool { return x /* ERROR cannot compare */ < y }
-func _[T interface{type int, float32, bool}](x, y T) bool { return x /* ERROR cannot compare */ < y }
+func _[T interface{~int | ~float32 | ~bool}](x, y T) bool { return x /* ERROR cannot compare */ < y }
func _[T C1[T]](x, y T) bool { return x /* ERROR cannot compare */ < y }
func _[T C2[T]](x, y T) bool { return x < y }
type C1[T any] interface{}
-type C2[T any] interface{ type int, float32 }
+type C2[T any] interface{ ~int | ~float32 }
func new[T any]() *T {
var x T
@@ -77,66 +75,92 @@ func new[T any]() *T {
var _ = new /* ERROR cannot use generic function new */
var _ *int = new[int]()
-func _[T any](map[T /* ERROR incomparable map key type T \(missing comparable constraint\) */]int) // w/o constraint we don't know if T is comparable
+func _[T any](map[T /* ERROR incomparable map key type T \(missing comparable constraint\) */]int) {} // w/o constraint we don't know if T is comparable
-func f1[T1 any](struct{T1}) int
+func f1[T1 any](struct{T1 /* ERROR cannot be a .* type parameter */ }) int { panic(0) }
var _ = f1[int](struct{T1}{})
type T1 = int
-func f2[t1 any](struct{t1; x float32}) int
+func f2[t1 any](struct{t1 /* ERROR cannot be a .* type parameter */ ; x float32}) int { panic(0) }
var _ = f2[t1](struct{t1; x float32}{})
type t1 = int
-func f3[A, B, C any](A, struct{x B}, func(A, struct{x B}, *C)) int
+func f3[A, B, C any](A, struct{x B}, func(A, struct{x B}, *C)) int { panic(0) }
var _ = f3[int, rune, bool](1, struct{x rune}{}, nil)
// indexing
func _[T any] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
-func _[T interface{ type int }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
-func _[T interface{ type string }] (x T, i int) { _ = x[i] }
-func _[T interface{ type []int }] (x T, i int) { _ = x[i] }
-func _[T interface{ type [10]int, *[20]int, map[int]int }] (x T, i int) { _ = x[i] }
-func _[T interface{ type string, []byte }] (x T, i int) { _ = x[i] }
-func _[T interface{ type []int, [1]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
-func _[T interface{ type string, []rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
-
-// indexing with various combinations of map types in type lists (see issue #42616)
-func _[T interface{ type []E, map[int]E }, E any](x T, i int) { _ = x[i] }
-func _[T interface{ type []E }, E any](x T, i int) { _ = &x[i] }
-func _[T interface{ type map[int]E }, E any](x T, i int) { _, _ = x[i] } // comma-ok permitted
-func _[T interface{ type []E, map[int]E }, E any](x T, i int) { _ = &x /* ERROR cannot take address */ [i] }
-func _[T interface{ type []E, map[int]E, map[uint]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // different map element types
-func _[T interface{ type []E, map[string]E }, E any](x T, i int) { _ = x[i /* ERROR cannot use i */ ] }
+func _[T interface{ ~int }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
+func _[T interface{ ~string }] (x T, i int) { _ = x[i] }
+func _[T interface{ ~[]int }] (x T, i int) { _ = x[i] }
+func _[T interface{ ~[10]int | ~*[20]int | ~map[int]int }] (x T, i int) { _ = x /* ERROR cannot index */ [i] } // map and non-map types
+func _[T interface{ ~string | ~[]byte }] (x T, i int) { _ = x[i] }
+func _[T interface{ ~[]int | ~[1]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
+func _[T interface{ ~string | ~[]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
+
+// indexing with various combinations of map types in type sets (see issue #42616)
+func _[T interface{ ~[]E | ~map[int]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // map and non-map types
+func _[T interface{ ~[]E }, E any](x T, i int) { _ = &x[i] }
+func _[T interface{ ~map[int]E }, E any](x T, i int) { _, _ = x[i] } // comma-ok permitted
+func _[T interface{ ~map[int]E }, E any](x T, i int) { _ = &x /* ERROR cannot take address */ [i] }
+func _[T interface{ ~map[int]E | ~map[uint]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // different map element types
+func _[T interface{ ~[]E | ~map[string]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // map and non-map types
+
+// indexing with various combinations of array and other types in type sets
+func _[T interface{ [10]int }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERROR out of bounds */ ] }
+func _[T interface{ [10]byte | string }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERROR out of bounds */ ] }
+func _[T interface{ [10]int | *[20]int | []int }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERROR out of bounds */ ] }
+
+// indexing with strings and non-variable arrays (assignment not permitted)
+func _[T string](x T) { _ = x[0]; x /* ERROR cannot assign */ [0] = 0 }
+func _[T []byte | string](x T) { x /* ERROR cannot assign */ [0] = 0 }
+func _[T [10]byte]() { f := func() (x T) { return }; f /* ERROR cannot assign */ ()[0] = 0 }
+func _[T [10]byte]() { f := func() (x *T) { return }; f /* ERROR cannot index */ ()[0] = 0 }
+func _[T [10]byte]() { f := func() (x *T) { return }; (*f())[0] = 0 }
+func _[T *[10]byte]() { f := func() (x T) { return }; f()[0] = 0 }
// slicing
-// TODO(gri) implement this
-func _[T interface{ type string }] (x T, i, j, k int) { _ = x /* ERROR invalid operation */ [i:j:k] }
+func _[T interface{ ~[10]E }, E any] (x T, i, j, k int) { var _ []E = x[i:j] }
+func _[T interface{ ~[10]E }, E any] (x T, i, j, k int) { var _ []E = x[i:j:k] }
+func _[T interface{ ~[]byte }] (x T, i, j, k int) { var _ T = x[i:j] }
+func _[T interface{ ~[]byte }] (x T, i, j, k int) { var _ T = x[i:j:k] }
+func _[T interface{ ~string }] (x T, i, j, k int) { var _ T = x[i:j] }
+func _[T interface{ ~string }] (x T, i, j, k int) { var _ T = x[i:j:k /* ERROR 3-index slice of string */ ] }
+
+type myByte1 []byte
+type myByte2 []byte
+func _[T interface{ []byte | myByte1 | myByte2 }] (x T, i, j, k int) { var _ T = x[i:j:k] }
+func _[T interface{ []byte | myByte1 | []int }] (x T, i, j, k int) { var _ T = x /* ERROR no structural type */ [i:j:k] }
+
+func _[T interface{ []byte | myByte1 | myByte2 | string }] (x T, i, j, k int) { var _ T = x[i:j] }
+func _[T interface{ []byte | myByte1 | myByte2 | string }] (x T, i, j, k int) { var _ T = x[i:j:k /* ERROR 3-index slice of string */ ] }
+func _[T interface{ []byte | myByte1 | []int | string }] (x T, i, j, k int) { var _ T = x /* ERROR no structural type */ [i:j] }
// len/cap built-ins
func _[T any](x T) { _ = len(x /* ERROR invalid argument */ ) }
-func _[T interface{ type int }](x T) { _ = len(x /* ERROR invalid argument */ ) }
-func _[T interface{ type string, []byte, int }](x T) { _ = len(x /* ERROR invalid argument */ ) }
-func _[T interface{ type string }](x T) { _ = len(x) }
-func _[T interface{ type [10]int }](x T) { _ = len(x) }
-func _[T interface{ type []byte }](x T) { _ = len(x) }
-func _[T interface{ type map[int]int }](x T) { _ = len(x) }
-func _[T interface{ type chan int }](x T) { _ = len(x) }
-func _[T interface{ type string, []byte, chan int }](x T) { _ = len(x) }
+func _[T interface{ ~int }](x T) { _ = len(x /* ERROR invalid argument */ ) }
+func _[T interface{ ~string | ~[]byte | ~int }](x T) { _ = len(x /* ERROR invalid argument */ ) }
+func _[T interface{ ~string }](x T) { _ = len(x) }
+func _[T interface{ ~[10]int }](x T) { _ = len(x) }
+func _[T interface{ ~[]byte }](x T) { _ = len(x) }
+func _[T interface{ ~map[int]int }](x T) { _ = len(x) }
+func _[T interface{ ~chan int }](x T) { _ = len(x) }
+func _[T interface{ ~string | ~[]byte | ~chan int }](x T) { _ = len(x) }
func _[T any](x T) { _ = cap(x /* ERROR invalid argument */ ) }
-func _[T interface{ type int }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
-func _[T interface{ type string, []byte, int }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
-func _[T interface{ type string }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
-func _[T interface{ type [10]int }](x T) { _ = cap(x) }
-func _[T interface{ type []byte }](x T) { _ = cap(x) }
-func _[T interface{ type map[int]int }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
-func _[T interface{ type chan int }](x T) { _ = cap(x) }
-func _[T interface{ type []byte, chan int }](x T) { _ = cap(x) }
+func _[T interface{ ~int }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
+func _[T interface{ ~string | ~[]byte | ~int }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
+func _[T interface{ ~string }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
+func _[T interface{ ~[10]int }](x T) { _ = cap(x) }
+func _[T interface{ ~[]byte }](x T) { _ = cap(x) }
+func _[T interface{ ~map[int]int }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
+func _[T interface{ ~chan int }](x T) { _ = cap(x) }
+func _[T interface{ ~[]byte | ~chan int }](x T) { _ = cap(x) }
// range iteration
@@ -144,64 +168,136 @@ func _[T interface{}](x T) {
for range x /* ERROR cannot range */ {}
}
-func _[T interface{ type string, []string }](x T) {
- for range x {}
- for i := range x { _ = i }
- for i, _ := range x { _ = i }
- for i, e := range x /* ERROR must have the same element type */ { _ = i }
- for _, e := range x /* ERROR must have the same element type */ {}
- var e rune
- _ = e
- for _, (e) = range x /* ERROR must have the same element type */ {}
-}
+type myString string
+func _[
+ B1 interface{ string },
+ B2 interface{ string | myString },
-func _[T interface{ type string, []rune, map[int]rune }](x T) {
- for _, e := range x { _ = e }
- for i, e := range x { _ = i; _ = e }
-}
+ C1 interface{ chan int },
+ C2 interface{ chan int | <-chan int },
+ C3 interface{ chan<- int },
-func _[T interface{ type string, []rune, map[string]rune }](x T) {
- for _, e := range x { _ = e }
- for i, e := range x /* ERROR must have the same key type */ { _ = e }
-}
+ S1 interface{ []int },
+ S2 interface{ []int | [10]int },
-func _[T interface{ type string, chan int }](x T) {
- for range x {}
- for i := range x { _ = i }
- for i, _ := range x { _ = i } // TODO(gri) should get an error here: channels only return one value
-}
+ A1 interface{ [10]int },
+ A2 interface{ [10]int | []int },
+
+ P1 interface{ *[10]int },
+ P2 interface{ *[10]int | *[]int },
+
+ M1 interface{ map[string]int },
+ M2 interface{ map[string]int | map[string]string },
+]() {
+ var b0 string
+ for range b0 {}
+ for _ = range b0 {}
+ for _, _ = range b0 {}
+
+ var b1 B1
+ for range b1 {}
+ for _ = range b1 {}
+ for _, _ = range b1 {}
+
+ var b2 B2
+ for range b2 {}
+
+ var c0 chan int
+ for range c0 {}
+ for _ = range c0 {}
+ for _, _ /* ERROR permits only one iteration variable */ = range c0 {}
+
+ var c1 C1
+ for range c1 {}
+ for _ = range c1 {}
+ for _, _ /* ERROR permits only one iteration variable */ = range c1 {}
+
+ var c2 C2
+ for range c2 {}
+
+ var c3 C3
+ for range c3 /* ERROR receive from send-only channel */ {}
+
+ var s0 []int
+ for range s0 {}
+ for _ = range s0 {}
+ for _, _ = range s0 {}
+
+ var s1 S1
+ for range s1 {}
+ for _ = range s1 {}
+ for _, _ = range s1 {}
+
+ var s2 S2
+ for range s2 /* ERROR cannot range over s2.*no structural type */ {}
-func _[T interface{ type string, chan<-int }](x T) {
- for i := range x /* ERROR send-only channel */ { _ = i }
+ var a0 []int
+ for range a0 {}
+ for _ = range a0 {}
+ for _, _ = range a0 {}
+
+ var a1 A1
+ for range a1 {}
+ for _ = range a1 {}
+ for _, _ = range a1 {}
+
+ var a2 A2
+ for range a2 /* ERROR cannot range over a2.*no structural type */ {}
+
+ var p0 *[10]int
+ for range p0 {}
+ for _ = range p0 {}
+ for _, _ = range p0 {}
+
+ var p1 P1
+ for range p1 {}
+ for _ = range p1 {}
+ for _, _ = range p1 {}
+
+ var p2 P2
+ for range p2 /* ERROR cannot range over p2.*no structural type */ {}
+
+ var m0 map[string]int
+ for range m0 {}
+ for _ = range m0 {}
+ for _, _ = range m0 {}
+
+ var m1 M1
+ for range m1 {}
+ for _ = range m1 {}
+ for _, _ = range m1 {}
+
+ var m2 M2
+ for range m2 /* ERROR cannot range over m2.*no structural type */ {}
}
// type inference checks
var _ = new /* ERROR cannot infer T */ ()
-func f4[A, B, C any](A, B) C
+func f4[A, B, C any](A, B) C { panic(0) }
var _ = f4 /* ERROR cannot infer C */ (1, 2)
var _ = f4[int, float32, complex128](1, 2)
-func f5[A, B, C any](A, []*B, struct{f []C}) int
+func f5[A, B, C any](A, []*B, struct{f []C}) int { panic(0) }
var _ = f5[int, float32, complex128](0, nil, struct{f []complex128}{})
var _ = f5 /* ERROR cannot infer */ (0, nil, struct{f []complex128}{})
var _ = f5(0, []*float32{new[float32]()}, struct{f []complex128}{})
-func f6[A any](A, []A) int
+func f6[A any](A, []A) int { panic(0) }
var _ = f6(0, nil)
-func f6nil[A any](A) int
+func f6nil[A any](A) int { panic(0) }
var _ = f6nil /* ERROR cannot infer */ (nil)
// type inference with variadic functions
-func f7[T any](...T) T
+func f7[T any](...T) T { panic(0) }
var _ int = f7 /* ERROR cannot infer T */ ()
var _ int = f7(1)
@@ -214,7 +310,7 @@ var _ = f7(float64(1), 2.3)
var _ = f7(1, 2.3 /* ERROR does not match */ )
var _ = f7(1.2, 3 /* ERROR does not match */ )
-func f8[A, B any](A, B, ...B) int
+func f8[A, B any](A, B, ...B) int { panic(0) }
var _ = f8(1) /* ERROR not enough arguments */
var _ = f8(1, 2.3)
@@ -227,8 +323,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 {}
@@ -240,7 +336,7 @@ func (T) m3[ /* ERROR methods cannot have type parameters */ P any]() {}
type S1[P any] struct { f P }
-func f9[P any](x S1[P])
+func f9[P any](x S1[P]) {}
func _() {
f9[int](S1[int]{42})
@@ -249,7 +345,7 @@ func _() {
type S2[A, B, C any] struct{}
-func f10[X, Y, Z any](a S2[X, int, Z], b S2[X, Y, bool])
+func f10[X, Y, Z any](a S2[X, int, Z], b S2[X, Y, bool]) {}
func _[P any]() {
f10[int, float32, string](S2[int, int, string]{}, S2[int, float32, bool]{})
@@ -260,7 +356,7 @@ func _[P any]() {
// corner case for type inference
// (was bug: after instanting f11, the type-checker didn't mark f11 as non-generic)
-func f11[T any]()
+func f11[T any]() {}
func _() {
f11[int]()
@@ -268,15 +364,16 @@ func _() {
// the previous example was extracted from
-func f12[T interface{m() T}]()
-
-type A[T any] T
-
-func (a A[T]) m() A[T]
-
-func _[T any]() {
- f12[A[T]]()
-}
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// func f12[T interface{m() T}]() {}
+//
+// type A[T any] T
+//
+// func (a A[T]) m() A[T]
+//
+// func _[T any]() {
+// f12[A[T]]()
+// }
// method expressions
@@ -296,15 +393,15 @@ func _[T any] (x T) {
type R0 struct{}
-func (R0) _[ /* ERROR methods cannot have type parameters */ T any](x T)
-func (R0 /* ERROR invalid receiver */ ) _[ /* ERROR methods cannot have type parameters */ R0 any]() // scope of type parameters starts at "func"
+func (R0) _[ /* ERROR methods cannot have type parameters */ T any](x T) {}
+func (R0 /* ERROR invalid receiver */ ) _[ /* ERROR methods cannot have type parameters */ R0 any]() {} // scope of type parameters starts at "func"
type R1[A, B any] struct{}
func (_ R1[A, B]) m0(A, B)
-func (_ R1[A, B]) m1[ /* ERROR methods cannot have type parameters */ T any](A, B, T) T
+func (_ R1[A, B]) m1[ /* ERROR methods cannot have type parameters */ T any](A, B, T) T { panic(0) }
func (_ R1 /* ERROR not a generic type */ [R1, _]) _()
-func (_ R1[A, B]) _[ /* ERROR methods cannot have type parameters */ A /* ERROR redeclared */ any](B)
+func (_ R1[A, B]) _[ /* ERROR methods cannot have type parameters */ A /* ERROR redeclared */ any](B) {}
func _() {
var r R1[int, string]
@@ -388,8 +485,8 @@ func (_ R2[X, Y]) m2(X) Y
// type assertions and type switches over generic types lead to errors for now
func _[T any](x T) {
- _ = x /* ERROR not an interface */ .(int)
- switch x /* ERROR not an interface */ .(type) {
+ _ = x /* ERROR cannot use type assertion */ .(int)
+ switch x /* ERROR cannot use type switch */ .(type) {
}
// work-around
@@ -399,9 +496,9 @@ func _[T any](x T) {
}
}
-func _[T interface{type int}](x T) {
- _ = x /* ERROR not an interface */ .(int)
- switch x /* ERROR not an interface */ .(type) {
+func _[T interface{~int}](x T) {
+ _ = x /* ERROR cannot use type assertion */ .(int)
+ switch x /* ERROR cannot use type switch */ .(type) {
}
// work-around
@@ -421,13 +518,13 @@ func _[P C[P]] (x P) {
type I interface {}
func _[P I] (x P) {
- x.m /* ERROR interface I has no method m */ ()
+ x.m /* ERROR type P has no field or method m */ ()
}
func _[P interface{}] (x P) {
- x.m /* ERROR type bound for P has no method m */ ()
+ x.m /* ERROR type P has no field or method m */ ()
}
func _[P any] (x P) {
- x.m /* ERROR type bound for P has no method m */ ()
+ x.m /* ERROR type P has no field or method m */ ()
}
diff --git a/libgo/go/go/types/testdata/check/vardecl.src b/libgo/go/go/types/testdata/check/vardecl.src
index 54f5ef1..56abf97 100644
--- a/libgo/go/go/types/testdata/check/vardecl.src
+++ b/libgo/go/go/types/testdata/check/vardecl.src
@@ -169,13 +169,13 @@ func _() {
func _() {
var x int
- return x /* ERROR no result values expected */
- return math /* ERROR no result values expected */ .Sin(0)
+ return x /* ERROR too many return values */
+ return math /* ERROR too many return values */ .Sin(0)
}
func _() int {
var x, y int
- return /* ERROR wrong number of return values */ x, y
+ return x, y /* ERROR too many return values */
}
// Short variable declarations must declare at least one new non-blank variable.
diff --git a/libgo/go/go/types/testdata/examples/functions.go2 b/libgo/go/go/types/testdata/examples/functions.go2
index a053471..0af7726 100644
--- a/libgo/go/go/types/testdata/examples/functions.go2
+++ b/libgo/go/go/types/testdata/examples/functions.go2
@@ -66,7 +66,7 @@ var _ float64 = foo(42, []float64{1.0}, &s)
// Type inference works in a straight-forward manner even
// for variadic functions.
-func variadic[A, B any](A, B, ...B) int
+func variadic[A, B any](A, B, ...B) int { panic(0) }
// var _ = variadic(1) // ERROR not enough arguments
var _ = variadic(1, 2.3)
@@ -98,7 +98,7 @@ func g2b[P, Q any](x P, y Q) {
// Here's an example of a recursive function call with variadic
// arguments and type inference inferring the type parameter of
// the caller (i.e., itself).
-func max[T interface{ type int }](x ...T) T {
+func max[T interface{ ~int }](x ...T) T {
var x0 T
if len(x) > 0 {
x0 = x[0]
@@ -118,9 +118,9 @@ func max[T interface{ type int }](x ...T) T {
// Thus even if a type can be inferred successfully, the function
// call may not be valid.
-func fboth[T any](chan T)
-func frecv[T any](<-chan T)
-func fsend[T any](chan<- T)
+func fboth[T any](chan T) {}
+func frecv[T any](<-chan T) {}
+func fsend[T any](chan<- T) {}
func _() {
var both chan int
@@ -140,9 +140,9 @@ func _() {
fsend(send)
}
-func ffboth[T any](func(chan T))
-func ffrecv[T any](func(<-chan T))
-func ffsend[T any](func(chan<- T))
+func ffboth[T any](func(chan T)) {}
+func ffrecv[T any](func(<-chan T)) {}
+func ffsend[T any](func(chan<- T)) {}
func _() {
var both func(chan int)
@@ -169,9 +169,9 @@ func _() {
// assignment is permitted, parameter passing is permitted as well,
// so type inference should be able to handle these cases well.
-func g1[T any]([]T)
-func g2[T any]([]T, T)
-func g3[T any](*T, ...T)
+func g1[T any]([]T) {}
+func g2[T any]([]T, T) {}
+func g3[T any](*T, ...T) {}
func _() {
type intSlize []int
@@ -194,7 +194,7 @@ func _() {
// Here's a realistic example.
-func append[T any](s []T, t ...T) []T
+func append[T any](s []T, t ...T) []T { panic(0) }
func _() {
var f func()
@@ -207,8 +207,12 @@ func _() {
// (that would indicate a slice type). Thus, generic functions cannot
// have empty type parameter lists, either. This is a syntax error.
-func h[] /* ERROR empty type parameter list */ ()
+func h[] /* ERROR empty type parameter list */ () {}
func _() {
h /* ERROR cannot index */ [] /* ERROR operand */ ()
}
+
+// Parameterized functions must have a function body.
+
+func _ /* ERROR missing function body */ [P any]()
diff --git a/libgo/go/go/types/testdata/examples/inference.go2 b/libgo/go/go/types/testdata/examples/inference.go2
index b4f3369..ffa30ee 100644
--- a/libgo/go/go/types/testdata/examples/inference.go2
+++ b/libgo/go/go/types/testdata/examples/inference.go2
@@ -7,10 +7,10 @@
package p
type Ordered interface {
- type int, float64, string
+ ~int|~float64|~string
}
-func min[T Ordered](x, y T) T
+func min[T Ordered](x, y T) T { panic(0) }
func _() {
// min can be called with explicit instantiation.
@@ -37,7 +37,7 @@ func _() {
_ = min("foo", "bar")
}
-func mixed[T1, T2, T3 any](T1, T2, T3)
+func mixed[T1, T2, T3 any](T1, T2, T3) {}
func _() {
// mixed can be called with explicit instantiation.
@@ -54,7 +54,7 @@ func _() {
mixed[int, string](1.1 /* ERROR cannot use 1.1 */ , "", false)
}
-func related1[Slice interface{type []Elem}, Elem any](s Slice, e Elem)
+func related1[Slice interface{~[]Elem}, Elem any](s Slice, e Elem) {}
func _() {
// related1 can be called with explicit instantiation.
@@ -78,7 +78,7 @@ func _() {
related1(si, "foo" /* ERROR cannot use "foo" */ )
}
-func related2[Elem any, Slice interface{type []Elem}](e Elem, s Slice)
+func related2[Elem any, Slice interface{~[]Elem}](e Elem, s Slice) {}
func _() {
// related2 can be called with explicit instantiation.
@@ -97,5 +97,28 @@ func _() {
// last.
related2(1.2, []float64{})
related2(1.0, []int{})
- related2 /* ERROR does not satisfy */ (float64(1.0), []int{})
+ related2 /* ERROR does not implement */ (float64(1.0), []int{}) // TODO(gri) fix error position
+}
+
+type List[P any] []P
+
+func related3[Elem any, Slice []Elem | List[Elem]]() Slice { return nil }
+
+func _() {
+ // related3 can be instantiated explicitly
+ related3[int, []int]()
+ related3[byte, List[byte]]()
+
+ // Alternatively, the 2nd type argument can be inferred
+ // from the first one through constraint type inference.
+ related3[int]()
+
+ // The inferred type is the structural type of the Slice
+ // type parameter.
+ var _ []int = related3[int]()
+
+ // It is not the defined parameterized type List.
+ type anotherList []float32
+ var _ anotherList = related3[float32]() // valid
+ var _ anotherList = related3 /* ERROR cannot use .* \(value of type List\[float32\]\) as anotherList */ [float32, List[float32]]()
}
diff --git a/libgo/go/go/types/testdata/examples/methods.go2 b/libgo/go/go/types/testdata/examples/methods.go2
index 76c6539..1d76d55 100644
--- a/libgo/go/go/types/testdata/examples/methods.go2
+++ b/libgo/go/go/types/testdata/examples/methods.go2
@@ -94,3 +94,19 @@ func (_ T2[_, _, _]) _() int { return 42 }
type T0 struct{}
func (T0) _() {}
func (T1[A]) _() {}
+
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// // A generic receiver type may constrain its type parameter such
+// // that it must be a pointer type. Such receiver types are not
+// // permitted.
+// type T3a[P interface{ ~int | ~string | ~float64 }] P
+//
+// func (T3a[_]) m() {} // this is ok
+//
+// type T3b[P interface{ ~unsafe.Pointer }] P
+//
+// func (T3b /* ERROR invalid receiver */ [_]) m() {}
+//
+// type T3c[P interface{ *int | *string }] P
+//
+// func (T3c /* ERROR invalid receiver */ [_]) m() {}
diff --git a/libgo/go/go/types/testdata/examples/types.go2 b/libgo/go/go/types/testdata/examples/types.go2
index 59c8804..33642fa 100644
--- a/libgo/go/go/types/testdata/examples/types.go2
+++ b/libgo/go/go/types/testdata/examples/types.go2
@@ -102,6 +102,7 @@ func _() {
// Generic types cannot be used without instantiation.
var _ T // ERROR cannot use generic type T
+var _ = T /* ERROR cannot use generic type T */ (0)
// In type context, generic (parameterized) types cannot be parenthesized before
// being instantiated. See also NOTES entry from 12/4/2019.
@@ -113,7 +114,7 @@ type I1[T any] interface{
}
// There is no such thing as a variadic generic type.
-type _[T ... /* ERROR invalid use of ... */ interface{}] struct{}
+type _[T ... /* ERROR invalid use of ... */ any] struct{}
// Generic interfaces may be embedded as one would expect.
type I2 interface {
@@ -161,31 +162,42 @@ type _ struct {
* /* ERROR List redeclared */ List[int]
}
+// Issue #45639: We don't allow this anymore. Keep this code
+// in case we decide to revisit this decision.
+//
// It's possible to declare local types whose underlying types
// are type parameters. As with ordinary type definitions, the
// types underlying properties are "inherited" but the methods
// are not.
-func _[T interface{ m(); type int }]() {
- type L T
- var x L
-
- // m is not defined on L (it is not "inherited" from
- // its underlying type).
- x.m /* ERROR x.m undefined */ ()
-
- // But the properties of T, such that as that it supports
- // the operations of the types given by its type bound,
- // are also the properties of L.
- x++
- _ = x - x
-
- // On the other hand, if we define a local alias for T,
- // that alias stands for T as expected.
- type A = T
- var y A
- y.m()
- _ = y < 0
-}
+//func _[T interface{ m(); ~int }]() {
+// type L T
+// var x L
+//
+// // m is not defined on L (it is not "inherited" from
+// // its underlying type).
+// x.m /* ERROR x.m undefined */ ()
+//
+// // But the properties of T, such that as that it supports
+// // the operations of the types given by its type bound,
+// // are also the properties of L.
+// x++
+// _ = x - x
+//
+// // On the other hand, if we define a local alias for T,
+// // that alias stands for T as expected.
+// type A = T
+// var y A
+// y.m()
+// _ = y < 0
+//}
+
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// // It is not permitted to declare a local type whose underlying
+// // type is a type parameter not declared by that type declaration.
+// func _[T any]() {
+// type _ T // ERROR cannot use function type parameter T as RHS in type declaration
+// type _ [_ any] T // ERROR cannot use function type parameter T as RHS in type declaration
+// }
// As a special case, an explicit type argument may be omitted
// from a type parameter bound if the type bound expects exactly
@@ -208,19 +220,19 @@ func Sum[T Adder[T]](list []T) T {
}
// Valid and invalid variations.
-type B0 interface {}
-type B1[_ any] interface{}
-type B2[_, _ any] interface{}
+type B0 any
+type B1[_ any] any
+type B2[_, _ any] any
-func _[T1 B0]()
-func _[T1 B1[T1]]()
-func _[T1 B2 /* ERROR cannot use generic type .* without instantiation */ ]()
+func _[T1 B0]() {}
+func _[T1 B1[T1]]() {}
+func _[T1 B2 /* ERROR cannot use generic type .* without instantiation */ ]() {}
-func _[T1, T2 B0]()
-func _[T1 B1[T1], T2 B1[T2]]()
-func _[T1, T2 B2 /* ERROR cannot use generic type .* without instantiation */ ]()
+func _[T1, T2 B0]() {}
+func _[T1 B1[T1], T2 B1[T2]]() {}
+func _[T1, T2 B2 /* ERROR cannot use generic type .* without instantiation */ ]() {}
-func _[T1 B0, T2 B1[T2]]() // here B1 applies to T2
+func _[T1 B0, T2 B1[T2]]() {} // here B1 applies to T2
// When the type argument is left away, the type bound is
// instantiated for each type parameter with that type
@@ -238,11 +250,11 @@ func _[A Adder[A], B Adder[B], C Adder[A]]() {
// The type of variables (incl. parameters and return values) cannot
// be an interface with type constraints or be/embed comparable.
type I interface {
- type int
+ ~int
}
var (
- _ interface /* ERROR contains type constraints */ {type int}
+ _ interface /* ERROR contains type constraints */ {~int}
_ I /* ERROR contains type constraints */
)
@@ -270,10 +282,10 @@ func _() {
// Type parameters are never const types, i.e., it's
// not possible to declare a constant of type parameter type.
-// (If a type list contains just a single const type, we could
-// allow it, but such type lists don't make much sense in the
+// (If a type set contains just a single const type, we could
+// allow it, but such type sets don't make much sense in the
// first place.)
-func _[T interface { type int, float64 }]() {
+func _[T interface {~int|~float64}]() {
// not valid
const _ = T /* ERROR not constant */ (0)
const _ T /* ERROR invalid constant type T */ = 1
@@ -284,3 +296,26 @@ func _[T interface { type int, float64 }]() {
_ = T(0)
}
+// It is possible to create composite literals of type parameter
+// type as long as it's possible to create a composite literal
+// of the structural type of the type parameter's constraint.
+func _[P interface{ ~[]int }]() P {
+ return P{}
+ return P{1, 2, 3}
+}
+
+func _[P interface{ ~[]E }, E interface{ map[string]P } ]() P {
+ x := P{}
+ return P{{}}
+ return P{E{}}
+ return P{E{"foo": x}}
+ return P{{"foo": x}, {}}
+}
+
+// This is a degenerate case with a singleton type set, but we can create
+// composite literals even if the structural type is a defined type.
+type MyInts []int
+
+func _[P MyInts]() P {
+ return P{}
+}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue25838.go b/libgo/go/go/types/testdata/fixedbugs/issue25838.go
new file mode 100644
index 0000000..adbd138
--- /dev/null
+++ b/libgo/go/go/types/testdata/fixedbugs/issue25838.go
@@ -0,0 +1,26 @@
+// Copyright 2022 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.
+
+package p
+
+// examples from the issue
+
+type (
+ e = f
+ f = g
+ g = []h
+ h i
+ i = j
+ j = e
+)
+
+type (
+ e1 = []h1
+ h1 e1
+)
+
+type (
+ P = *T
+ T P
+)
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue28251.src b/libgo/go/go/types/testdata/fixedbugs/issue28251.src
index cd79e0e..ef5e61d 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue28251.src
+++ b/libgo/go/go/types/testdata/fixedbugs/issue28251.src
@@ -60,6 +60,6 @@ type (
T11 = T
)
-func (T9 /* ERROR invalid receiver \*\*T */ ) m9() {}
+func (T9 /* ERROR invalid receiver type \*\*T */ ) m9() {}
func _() { (T{}).m9 /* ERROR has no field or method m9 */ () }
func _() { (&T{}).m9 /* ERROR has no field or method m9 */ () }
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39634.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39634.go2
index a13ed13..34ab654 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue39634.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue39634.go2
@@ -31,13 +31,14 @@ type x7[A any] struct{ foo7 }
func main7() { var _ foo7 = x7[int]{} }
// crash 8
-type foo8[A any] interface { type A }
-func bar8[A foo8[A]](a A) {}
-func main8() {}
+// Embedding stand-alone type parameters is not permitted for now. Disabled.
+// type foo8[A any] interface { ~A }
+// func bar8[A foo8[A]](a A) {}
+// func main8() {}
// crash 9
-type foo9[A any] interface { type foo9 /* ERROR interface contains type constraints */ [A] }
-func _() { var _ = new(foo9 /* ERROR interface contains type constraints */ [int]) }
+type foo9[A any] interface { foo9 /* ERROR illegal cycle */ [A] }
+func _() { var _ = new(foo9[int]) }
// crash 12
var u /* ERROR cycle */ , i [func /* ERROR used as value */ /* ERROR used as value */ (u, c /* ERROR undeclared */ /* ERROR undeclared */ ) {}(0, len /* ERROR must be called */ /* ERROR must be called */ )]c /* ERROR undeclared */ /* ERROR undeclared */
@@ -49,7 +50,7 @@ func (G15 /* ERROR generic type .* without instantiation */ ) p()
// crash 16
type Foo16[T any] r16 /* ERROR not a type */
-func r16[T any]() Foo16[Foo16[T]]
+func r16[T any]() Foo16[Foo16[T]] { panic(0) }
// crash 17
type Y17 interface{ c() }
@@ -57,7 +58,7 @@ type Z17 interface {
c() Y17
Y17 /* ERROR duplicate method */
}
-func F17[T Z17](T)
+func F17[T Z17](T) {}
// crash 18
type o18[T any] []func(_ o18[[]_ /* ERROR cannot use _ */ ])
@@ -73,9 +74,10 @@ func F20[t Z20]() { F20(t /* ERROR invalid composite literal type */ {}) }
type Z21 /* ERROR illegal cycle */ interface{ Z21 }
func F21[T Z21]() { ( /* ERROR not used */ F21[Z21]) }
-// crash 24
-type T24[P any] P
-func (r T24[P]) m() { T24 /* ERROR without instantiation */ .m() }
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// // crash 24
+// type T24[P any] P
+// func (r T24[P]) m() { T24 /* ERROR without instantiation */ .m() }
// crash 25
type T25[A any] int
@@ -84,8 +86,11 @@ var x T25 /* ERROR without instantiation */ .m1
// crash 26
type T26 = interface{ F26[ /* ERROR methods cannot have type parameters */ Z any]() }
+// The error messages on the line below differ from types2 because for backward
+// compatibility go/parser must produce an IndexExpr with BadExpr index for the
+// expression F26[].
func F26[Z any]() T26 { return F26[] /* ERROR operand */ }
// crash 27
-func e27[T any]() interface{ x27 /* ERROR not a type */ }
+func e27[T any]() interface{ x27 /* ERROR not a type */ } { panic(0) }
func x27() { e27 /* ERROR cannot infer T */ () }
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39680.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39680.go2
index 9bc26f3..e56bc35 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue39680.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue39680.go2
@@ -4,16 +4,19 @@
package p
+// Embedding stand-alone type parameters is not permitted for now. Disabled.
+
+/*
import "fmt"
// Minimal test case.
-func _[T interface{type T}](x T) T{
+func _[T interface{~T}](x T) T{
return x
}
// Test case from issue.
type constr[T any] interface {
- type T
+ ~T
}
func Print[T constr[T]](s []T) {
@@ -25,3 +28,4 @@ func Print[T constr[T]](s []T) {
func f() {
Print([]string{"Hello, ", "playground\n"})
}
+*/
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39693.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39693.go2
index 316ab19..ec76419 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue39693.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue39693.go2
@@ -4,11 +4,20 @@
package p
-type Number interface {
- int /* ERROR int is not an interface */
- float64 /* ERROR float64 is not an interface */
+type Number1 interface {
+ // embedding non-interface types is permitted
+ int
+ float64
}
-func Add[T Number](a, b T) T {
+func Add[T Number1](a, b T) T {
return a /* ERROR not defined */ + b
}
+
+type Number2 interface {
+ int|float64
+}
+
+func Add2[T Number2](a, b T) T {
+ return a + b
+}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39699.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39699.go2
index 75491e7..72f8399 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue39699.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue39699.go2
@@ -8,7 +8,7 @@ type T0 interface{
}
type T1 interface{
- type int
+ ~int
}
type T2 interface{
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39711.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39711.go2
index df621a4..d85fa03 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue39711.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue39711.go2
@@ -4,8 +4,10 @@
package p
-// Do not report a duplicate type error for this type list.
+// Do not report a duplicate type error for this term list.
// (Check types after interfaces have been completed.)
type _ interface {
- type interface{ Error() string }, interface{ String() string }
+ // TODO(rfindley) Once we have full type sets we can enable this again.
+ // Fow now we don't permit interfaces in term lists.
+ // type interface{ Error() string }, interface{ String() string }
}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39723.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39723.go2
index 55464e6..0088523 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue39723.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue39723.go2
@@ -6,4 +6,4 @@ package p
// A constraint must be an interface; it cannot
// be a type parameter, for instance.
-func _[A interface{ type interface{} }, B A /* ERROR not an interface */ ]()
+func _[A interface{ ~int }, B A /* ERROR cannot use a type parameter as constraint */ ]() {}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39725.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39725.go2
index e19b677..62dc45a 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue39725.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue39725.go2
@@ -4,13 +4,13 @@
package p
-func f1[T1, T2 any](T1, T2, struct{a T1; b T2})
+func f1[T1, T2 any](T1, T2, struct{a T1; b T2}) {}
func _() {
f1(42, string("foo"), struct /* ERROR does not match inferred type struct\{a int; b string\} */ {a, b int}{})
}
// simplified test case from issue
-func f2[T any](_ []T, _ func(T))
+func f2[T any](_ []T, _ func(T)) {}
func _() {
f2([]string{}, func /* ERROR does not match inferred type func\(string\) */ (f []byte) {})
}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39754.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39754.go2
index b2ba9de..5c9c4e6 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue39754.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue39754.go2
@@ -16,9 +16,6 @@ func f[V interface{}, A, B Box[V]]() {}
func _() {
f[int, Optional[int], Optional[int]]()
- _ = f[int, Optional[int], Optional /* ERROR does not satisfy Box */ [string]]
- // TODO(gri) Provide better position information here.
- // See TODO in call.go, Checker.arguments.
- // TODO(rFindley) Reconcile this error position with types2.
- f /* ERROR does not satisfy Box */ [int, Optional[int], Optional[string]]()
+ _ = f[int, Optional[int], Optional /* ERROR does not implement Box */ [string]]
+ _ = f[int, Optional[int], Optional /* ERROR Optional.* does not implement Box.* */ [string]]
}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39755.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39755.go2
index b7ab688..257b73a 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue39755.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue39755.go2
@@ -4,14 +4,14 @@
package p
-func _[T interface{type map[string]int}](x T) {
+func _[T interface{~map[string]int}](x T) {
_ = x == nil
}
// simplified test case from issue
type PathParamsConstraint interface {
- type map[string]string, []struct{key, value string}
+ ~map[string]string | ~[]struct{key, value string}
}
type PathParams[T PathParamsConstraint] struct {
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39768.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39768.go2
index abac141..696d9d9 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue39768.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue39768.go2
@@ -4,17 +4,18 @@
package p
-type T[P any] P
-type A = T
-var x A[int]
-var _ A /* ERROR cannot use generic type */
-
-type B = T[int]
-var y B = x
-var _ B /* ERROR not a generic type */ [int]
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// type T[P any] P
+// type A = T // ERROR cannot use generic type
+// var x A[int]
+// var _ A
+//
+// type B = T[int]
+// var y B = x
+// var _ B /* ERROR not a generic type */ [int]
// test case from issue
type Vector[T any] []T
-type VectorAlias = Vector
+type VectorAlias = Vector // ERROR cannot use generic type
var v Vector[int]
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39938.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39938.go2
index 76e7e36..6bc9284 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue39938.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue39938.go2
@@ -2,20 +2,20 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Check "infinite expansion" cycle errors across instantiated types.
-
package p
-type E0[P any] P
+// All but E2 and E5 provide an "indirection" and break infinite expansion of a type.
+type E0[P any] []P
type E1[P any] *P
-type E2[P any] struct{ P }
-type E3[P any] struct{ *P }
+type E2[P any] struct{ _ P }
+type E3[P any] struct{ _ *P }
+type E5[P any] struct{ _ [10]P }
-type T0 /* ERROR illegal cycle */ struct {
+type T0 struct {
_ E0[T0]
}
-type T0_ /* ERROR illegal cycle */ struct {
+type T0_ struct {
E0[T0_]
}
@@ -31,20 +31,24 @@ type T3 struct {
_ E3[T3]
}
-// some more complex cases
-
-type T4 /* ERROR illegal cycle */ struct {
- _ E0[E2[T4]]
-}
+type T4 /* ERROR illegal cycle */ [10]E5[T4]
type T5 struct {
- _ E0[E2[E0[E1[E2[[10]T5]]]]]
+ _ E0[E2[T5]]
}
-type T6 /* ERROR illegal cycle */ struct {
- _ E0[[10]E2[E0[E2[E2[T6]]]]]
+type T6 struct {
+ _ E0[E2[E0[E1[E2[[10]T6]]]]]
}
type T7 struct {
- _ E0[[]E2[E0[E2[E2[T6]]]]]
+ _ E0[[10]E2[E0[E2[E2[T7]]]]]
}
+
+type T8 struct {
+ _ E0[[]E2[E0[E2[E2[T8]]]]]
+}
+
+type T9 /* ERROR illegal cycle */ [10]E2[E5[E2[T9]]]
+
+type T10 [10]E2[E5[E2[func(T10)]]]
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39948.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39948.go2
index c2b4609..e38e572 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue39948.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue39948.go2
@@ -5,5 +5,5 @@
package p
type T[P any] interface{
- P // ERROR P is a type parameter, not an interface
+ P // ERROR cannot embed a type parameter
}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39976.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39976.go2
index 3db4eae..d703da9 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue39976.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue39976.go2
@@ -7,7 +7,7 @@ package p
type policy[K, V any] interface{}
type LRU[K, V any] struct{}
-func NewCache[K, V any](p policy[K, V])
+func NewCache[K, V any](p policy[K, V]) {}
func _() {
var lru LRU[int, string]
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue40038.go2 b/libgo/go/go/types/testdata/fixedbugs/issue40038.go2
index 8948d61..5f81fcb 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue40038.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue40038.go2
@@ -8,8 +8,8 @@ type A[T any] int
func (A[T]) m(A[T])
-func f[P interface{m(P)}]()
+func f[P interface{m(P)}]() {}
func _() {
_ = f[A[int]]
-} \ No newline at end of file
+}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue40056.go2 b/libgo/go/go/types/testdata/fixedbugs/issue40056.go2
index f587691..66130c0 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue40056.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue40056.go2
@@ -10,6 +10,6 @@ func _() {
type S struct {}
-func NewS[T any]() *S
+func NewS[T any]() *S { panic(0) }
func (_ *S /* ERROR S is not a generic type */ [T]) M()
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue40301.go2 b/libgo/go/go/types/testdata/fixedbugs/issue40301.go2
index 5d97855..c78f9a1 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue40301.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue40301.go2
@@ -7,6 +7,6 @@ package p
import "unsafe"
func _[T any](x T) {
- _ = unsafe /* ERROR undefined */ .Alignof(x)
- _ = unsafe /* ERROR undefined */ .Sizeof(x)
+ _ = unsafe.Alignof(x)
+ _ = unsafe.Sizeof(x)
}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue40684.go2 b/libgo/go/go/types/testdata/fixedbugs/issue40684.go2
index 0269c3a..63a058d 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue40684.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue40684.go2
@@ -6,10 +6,10 @@ package p
type T[_ any] int
-func f[_ any]()
-func g[_, _ any]()
+func f[_ any]() {}
+func g[_, _ any]() {}
func _() {
_ = f[T /* ERROR without instantiation */ ]
_ = g[T /* ERROR without instantiation */ , T /* ERROR without instantiation */ ]
-} \ No newline at end of file
+}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue41124.go2 b/libgo/go/go/types/testdata/fixedbugs/issue41124.go2
index 61f766b..7f55ba8 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue41124.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue41124.go2
@@ -6,13 +6,13 @@ package p
// Test case from issue.
-type Nat interface {
- type Zero, Succ
+type Nat /* ERROR cycle */ interface {
+ Zero|Succ
}
type Zero struct{}
type Succ struct{
- Nat // ERROR interface contains type constraints
+ Nat // Nat contains type constraints but is invalid, so no error
}
// Struct tests.
@@ -22,7 +22,7 @@ type I1 interface {
}
type I2 interface {
- type int
+ ~int
}
type I3 interface {
@@ -47,7 +47,7 @@ type _ struct{
}
type _ struct{
- I3 // ERROR interface contains type constraints
+ I3 // ERROR interface is .* comparable
}
// General composite types.
@@ -59,19 +59,19 @@ type (
_ []I1 // ERROR interface is .* comparable
_ []I2 // ERROR interface contains type constraints
- _ *I3 // ERROR interface contains type constraints
+ _ *I3 // ERROR interface is .* comparable
_ map[I1 /* ERROR interface is .* comparable */ ]I2 // ERROR interface contains type constraints
- _ chan I3 // ERROR interface contains type constraints
+ _ chan I3 // ERROR interface is .* comparable
_ func(I1 /* ERROR interface is .* comparable */ )
_ func() I2 // ERROR interface contains type constraints
)
// Other cases.
-var _ = [...]I3 /* ERROR interface contains type constraints */ {}
+var _ = [...]I3 /* ERROR interface is .* comparable */ {}
func _(x interface{}) {
- _ = x.(I3 /* ERROR interface contains type constraints */ )
+ _ = x.(I3 /* ERROR interface is .* comparable */ )
}
type T1[_ any] struct{}
@@ -79,9 +79,9 @@ type T3[_, _, _ any] struct{}
var _ T1[I2 /* ERROR interface contains type constraints */ ]
var _ T3[int, I2 /* ERROR interface contains type constraints */ , float32]
-func f1[_ any]() int
+func f1[_ any]() int { panic(0) }
var _ = f1[I2 /* ERROR interface contains type constraints */ ]()
-func f3[_, _, _ any]() int
+func f3[_, _, _ any]() int { panic(0) }
var _ = f3[int, I2 /* ERROR interface contains type constraints */ , float32]()
func _(x interface{}) {
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue42758.go2 b/libgo/go/go/types/testdata/fixedbugs/issue42758.go2
index 698cb8a..dd66e96 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue42758.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue42758.go2
@@ -17,7 +17,7 @@ func _[T any](x interface{}){
}
type constraint interface {
- type int
+ ~int
}
func _[T constraint](x interface{}){
@@ -28,6 +28,6 @@ func _[T constraint](x interface{}){
}
func _(x constraint /* ERROR contains type constraints */ ) {
- switch x /* ERROR contains type constraints */ .(type) {
+ switch x.(type) { // no need to report another error
}
}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue45114.go b/libgo/go/go/types/testdata/fixedbugs/issue45114.go
new file mode 100644
index 0000000..0093660
--- /dev/null
+++ b/libgo/go/go/types/testdata/fixedbugs/issue45114.go
@@ -0,0 +1,8 @@
+// Copyright 2022 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.
+
+package p
+
+var s uint
+var _ = string(1 /* ERROR shifted operand 1 .* must be integer */ << s)
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue45548.go2 b/libgo/go/go/types/testdata/fixedbugs/issue45548.go2
index b1e4249..b8ba0ad 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue45548.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue45548.go2
@@ -4,7 +4,7 @@
package p
-func f[F interface{type *Q}, G interface{type *R}, Q, R any](q Q, r R) {}
+func f[F interface{~*Q}, G interface{~*R}, Q, R any](q Q, r R) {}
func _() {
f[*float64, *int](1, 2)
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue45635.go2 b/libgo/go/go/types/testdata/fixedbugs/issue45635.go2
index 3e2ccec..fc50797 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue45635.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue45635.go2
@@ -10,10 +10,10 @@ func main() {
type N[T any] struct{}
-var _ N /* ERROR "0 arguments but 1 type parameters" */ []
+var _ N [] // ERROR expected type argument list
type I interface {
- type map[int]int, []int
+ ~[]int
}
func _[T I](i, j int) {
@@ -27,6 +27,5 @@ func _[T I](i, j int) {
_ = s[i, j /* ERROR "more than one index" */ ]
var t T
- // TODO(rFindley) Fix the duplicate error below.
- _ = t[i, j /* ERROR "more than one index" */ /* ERROR "more than one index" */ ]
+ _ = t[i, j /* ERROR "more than one index" */ ]
}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue45985.go2 b/libgo/go/go/types/testdata/fixedbugs/issue45985.go2
index 550b9c6..9a0f5e3 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue45985.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue45985.go2
@@ -4,11 +4,10 @@
package issue45985
-// TODO(rFindley): this error should be on app[int] below.
-func app[S /* ERROR "type S = S does not match" */ interface{ type []T }, T any](s S, e T) S {
+func app[S interface{ ~[]T }, T any](s S, e T) S {
return append(s, e)
}
func _() {
- _ = app[int]
+ _ = app/* ERROR "S does not match" */[int]
}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue49003.go b/libgo/go/go/types/testdata/fixedbugs/issue49003.go
new file mode 100644
index 0000000..ece1a27
--- /dev/null
+++ b/libgo/go/go/types/testdata/fixedbugs/issue49003.go
@@ -0,0 +1,10 @@
+// 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.
+
+package p
+
+func f(s string) int {
+ for range s {
+ }
+} // ERROR missing return
diff --git a/libgo/go/go/types/tuple.go b/libgo/go/go/types/tuple.go
new file mode 100644
index 0000000..e85c5aa
--- /dev/null
+++ b/libgo/go/go/types/tuple.go
@@ -0,0 +1,34 @@
+// Copyright 2011 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.
+
+package types
+
+// A Tuple represents an ordered list of variables; a nil *Tuple is a valid (empty) tuple.
+// Tuples are used as components of signatures and to represent the type of multiple
+// assignments; they are not first class types of Go.
+type Tuple struct {
+ vars []*Var
+}
+
+// NewTuple returns a new tuple for the given variables.
+func NewTuple(x ...*Var) *Tuple {
+ if len(x) > 0 {
+ return &Tuple{vars: x}
+ }
+ return nil
+}
+
+// Len returns the number variables of tuple t.
+func (t *Tuple) Len() int {
+ if t != nil {
+ return len(t.vars)
+ }
+ return 0
+}
+
+// At returns the i'th variable of tuple t.
+func (t *Tuple) At(i int) *Var { return t.vars[i] }
+
+func (t *Tuple) Underlying() Type { return t }
+func (t *Tuple) String() string { return TypeString(t, nil) }
diff --git a/libgo/go/go/types/type.go b/libgo/go/go/types/type.go
index 2660ce4..3acb19c 100644
--- a/libgo/go/go/types/type.go
+++ b/libgo/go/go/types/type.go
@@ -4,983 +4,123 @@
package types
-import (
- "fmt"
- "go/token"
- "sync/atomic"
-)
-
// A Type represents a type of Go.
// All types implement the Type interface.
type Type interface {
// Underlying returns the underlying type of a type
// w/o following forwarding chains. Only used by
- // client packages (here for backward-compatibility).
+ // client packages.
Underlying() Type
// String returns a string representation of a type.
String() string
}
-// BasicKind describes the kind of basic type.
-type BasicKind int
-
-const (
- Invalid BasicKind = iota // type is invalid
-
- // predeclared types
- Bool
- Int
- Int8
- Int16
- Int32
- Int64
- Uint
- Uint8
- Uint16
- Uint32
- Uint64
- Uintptr
- Float32
- Float64
- Complex64
- Complex128
- String
- UnsafePointer
-
- // types for untyped values
- UntypedBool
- UntypedInt
- UntypedRune
- UntypedFloat
- UntypedComplex
- UntypedString
- UntypedNil
-
- // aliases
- Byte = Uint8
- Rune = Int32
-)
-
-// BasicInfo is a set of flags describing properties of a basic type.
-type BasicInfo int
-
-// Properties of basic types.
-const (
- IsBoolean BasicInfo = 1 << iota
- IsInteger
- IsUnsigned
- IsFloat
- IsComplex
- IsString
- IsUntyped
-
- IsOrdered = IsInteger | IsFloat | IsString
- IsNumeric = IsInteger | IsFloat | IsComplex
- IsConstType = IsBoolean | IsNumeric | IsString
-)
-
-// A Basic represents a basic type.
-type Basic struct {
- kind BasicKind
- info BasicInfo
- name string
-}
-
-// Kind returns the kind of basic type b.
-func (b *Basic) Kind() BasicKind { return b.kind }
-
-// Info returns information about properties of basic type b.
-func (b *Basic) Info() BasicInfo { return b.info }
-
-// Name returns the name of basic type b.
-func (b *Basic) Name() string { return b.name }
-
-// An Array represents an array type.
-type Array struct {
- len int64
- elem Type
-}
-
-// NewArray returns a new array type for the given element type and length.
-// A negative length indicates an unknown length.
-func NewArray(elem Type, len int64) *Array { return &Array{len: len, elem: elem} }
-
-// Len returns the length of array a.
-// A negative result indicates an unknown length.
-func (a *Array) Len() int64 { return a.len }
-
-// Elem returns element type of array a.
-func (a *Array) Elem() Type { return a.elem }
-
-// A Slice represents a slice type.
-type Slice struct {
- elem Type
-}
-
-// NewSlice returns a new slice type for the given element type.
-func NewSlice(elem Type) *Slice { return &Slice{elem: elem} }
-
-// Elem returns the element type of slice s.
-func (s *Slice) Elem() Type { return s.elem }
-
-// A Struct represents a struct type.
-type Struct struct {
- fields []*Var
- tags []string // field tags; nil if there are no tags
-}
-
-// NewStruct returns a new struct with the given fields and corresponding field tags.
-// If a field with index i has a tag, tags[i] must be that tag, but len(tags) may be
-// only as long as required to hold the tag with the largest index i. Consequently,
-// if no field has a tag, tags may be nil.
-func NewStruct(fields []*Var, tags []string) *Struct {
- var fset objset
- for _, f := range fields {
- if f.name != "_" && fset.insert(f) != nil {
- panic("multiple fields with the same name")
- }
- }
- if len(tags) > len(fields) {
- panic("more tags than fields")
- }
- return &Struct{fields: fields, tags: tags}
-}
-
-// NumFields returns the number of fields in the struct (including blank and embedded fields).
-func (s *Struct) NumFields() int { return len(s.fields) }
-
-// Field returns the i'th field for 0 <= i < NumFields().
-func (s *Struct) Field(i int) *Var { return s.fields[i] }
-
-// Tag returns the i'th field tag for 0 <= i < NumFields().
-func (s *Struct) Tag(i int) string {
- if i < len(s.tags) {
- return s.tags[i]
- }
- return ""
-}
-
-// A Pointer represents a pointer type.
-type Pointer struct {
- base Type // element type
-}
-
-// NewPointer returns a new pointer type for the given element (base) type.
-func NewPointer(elem Type) *Pointer { return &Pointer{base: elem} }
-
-// Elem returns the element type for the given pointer p.
-func (p *Pointer) Elem() Type { return p.base }
-
-// A Tuple represents an ordered list of variables; a nil *Tuple is a valid (empty) tuple.
-// Tuples are used as components of signatures and to represent the type of multiple
-// assignments; they are not first class types of Go.
-type Tuple struct {
- vars []*Var
-}
-
-// NewTuple returns a new tuple for the given variables.
-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
-}
-
-// Len returns the number variables of tuple t.
-func (t *Tuple) Len() int {
- if t != nil {
- return len(t.vars)
- }
- return 0
-}
-
-// At returns the i'th variable of tuple t.
-func (t *Tuple) At(i int) *Var { return t.vars[i] }
-
-// A Signature represents a (non-builtin) function or method type.
-// The receiver is ignored when comparing signatures for identity.
-type Signature struct {
- // We need to keep the scope in Signature (rather than passing it around
- // and store it in the Func Object) because when type-checking a function
- // literal we call the general type checker which returns a general Type.
- // We then unpack the *Signature and use the scope for the literal body.
- rparams []*TypeName // receiver type parameters from left to right, or nil
- tparams []*TypeName // type parameters from left to right, or nil
- scope *Scope // function scope, present for package-local signatures
- recv *Var // nil if not a method
- params *Tuple // (incoming) parameters from left to right; or nil
- results *Tuple // (outgoing) results from left to right; or nil
- variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only)
-}
-
-// NewSignature returns a new function type for the given receiver, parameters,
-// and results, either of which may be nil. If variadic is set, the function
-// is variadic, it must have at least one parameter, and the last parameter
-// must be of unnamed slice type.
-func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature {
- if variadic {
- n := params.Len()
- if n == 0 {
- panic("types.NewSignature: variadic function must have at least one parameter")
- }
- if _, ok := params.At(n - 1).typ.(*Slice); !ok {
- panic("types.NewSignature: variadic parameter must be of unnamed slice type")
- }
- }
- return &Signature{recv: recv, params: params, results: results, variadic: variadic}
-}
-
-// Recv returns the receiver of signature s (if a method), or nil if a
-// function. It is ignored when comparing signatures for identity.
-//
-// For an abstract method, Recv returns the enclosing interface either
-// as a *Named or an *Interface. Due to embedding, an interface may
-// contain methods whose receiver type is a different interface.
-func (s *Signature) Recv() *Var { return s.recv }
-
-// _TParams returns the type parameters of signature s, or nil.
-func (s *Signature) _TParams() []*TypeName { return s.tparams }
-
-// _SetTParams sets the type parameters of signature s.
-func (s *Signature) _SetTParams(tparams []*TypeName) { s.tparams = tparams }
-
-// Params returns the parameters of signature s, or nil.
-func (s *Signature) Params() *Tuple { return s.params }
-
-// Results returns the results of signature s, or nil.
-func (s *Signature) Results() *Tuple { return s.results }
-
-// Variadic reports whether the signature s is variadic.
-func (s *Signature) Variadic() bool { return s.variadic }
-
-// A _Sum represents a set of possible types.
-// Sums are currently used to represent type lists of interfaces
-// and thus the underlying types of type parameters; they are not
-// first class types of Go.
-type _Sum struct {
- types []Type // types are unique
-}
-
-// _NewSum returns a new Sum type consisting of the provided
-// types if there are more than one. If there is exactly one
-// type, it returns that type. If the list of types is empty
-// the result is nil.
-func _NewSum(types []Type) Type {
- if len(types) == 0 {
- return nil
- }
-
- // What should happen if types contains a sum type?
- // Do we flatten the types list? For now we check
- // and panic. This should not be possible for the
- // current use case of type lists.
- // TODO(gri) Come up with the rules for sum types.
- for _, t := range types {
- if _, ok := t.(*_Sum); ok {
- panic("sum type contains sum type - unimplemented")
- }
- }
-
- if len(types) == 1 {
- return types[0]
- }
- return &_Sum{types: types}
-}
-
-// is reports whether all types in t satisfy pred.
-func (s *_Sum) is(pred func(Type) bool) bool {
- if s == nil {
- return false
- }
- for _, t := range s.types {
- if !pred(t) {
- return false
- }
+// under returns the true expanded underlying type.
+// If it doesn't exist, the result is Typ[Invalid].
+// under must only be called when a type is known
+// to be fully set up.
+func under(t Type) Type {
+ if t, _ := t.(*Named); t != nil {
+ return t.under()
}
- return true
-}
-
-// An Interface represents an interface type.
-type Interface struct {
- methods []*Func // ordered list of explicitly declared methods
- types Type // (possibly a Sum) type declared with a type list (TODO(gri) need better field name)
- embeddeds []Type // ordered list of explicitly embedded types
-
- allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset)
- allTypes Type // intersection of all embedded and locally declared types (TODO(gri) need better field name)
-
- obj Object // type declaration defining this interface; or nil (for better error messages)
+ return t.Underlying()
}
-// unpack unpacks a type into a list of types.
-// TODO(gri) Try to eliminate the need for this function.
-func unpackType(typ Type) []Type {
- if typ == nil {
- return nil
- }
- if sum := asSum(typ); sum != nil {
- return sum.types
+// If t is not a type parameter, structuralType returns the underlying type.
+// If t is a type parameter, structuralType returns the single underlying
+// type of all types in its type set if it exists, or nil otherwise. If the
+// type set contains only unrestricted and restricted channel types (with
+// identical element types), the single underlying type is the restricted
+// channel type if the restrictions are always the same, or nil otherwise.
+func structuralType(t Type) Type {
+ tpar, _ := t.(*TypeParam)
+ if tpar == nil {
+ return under(t)
}
- return []Type{typ}
-}
-// is reports whether interface t represents types that all satisfy pred.
-func (t *Interface) is(pred func(Type) bool) bool {
- if t.allTypes == nil {
- return false // we must have at least one type! (was bug)
- }
- for _, t := range unpackType(t.allTypes) {
- if !pred(t) {
+ var su Type
+ if tpar.underIs(func(u Type) bool {
+ if u == nil {
return false
}
- }
- return true
-}
-
-// emptyInterface represents the empty (completed) interface
-var emptyInterface = Interface{allMethods: markComplete}
-
-// markComplete is used to mark an empty interface as completely
-// set up by setting the allMethods field to a non-nil empty slice.
-var markComplete = make([]*Func, 0)
-
-// NewInterface returns a new (incomplete) interface for the given methods and embedded types.
-// Each embedded type must have an underlying type of interface type.
-// NewInterface takes ownership of the provided methods and may modify their types by setting
-// missing receivers. To compute the method set of the interface, Complete must be called.
-//
-// Deprecated: Use NewInterfaceType instead which allows any (even non-defined) interface types
-// to be embedded. This is necessary for interfaces that embed alias type names referring to
-// non-defined (literal) interface types.
-func NewInterface(methods []*Func, embeddeds []*Named) *Interface {
- tnames := make([]Type, len(embeddeds))
- for i, t := range embeddeds {
- tnames[i] = t
- }
- return NewInterfaceType(methods, tnames)
-}
-
-// NewInterfaceType returns a new (incomplete) interface for the given methods and embedded types.
-// Each embedded type must have an underlying type of interface type (this property is not
-// verified for defined types, which may be in the process of being set up and which don't
-// have a valid underlying type yet).
-// NewInterfaceType takes ownership of the provided methods and may modify their types by setting
-// missing receivers. To compute the method set of the interface, Complete must be called.
-func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
- if len(methods) == 0 && len(embeddeds) == 0 {
- return &emptyInterface
- }
-
- // set method receivers if necessary
- typ := new(Interface)
- for _, m := range methods {
- if sig := m.typ.(*Signature); sig.recv == nil {
- sig.recv = NewVar(m.pos, m.pkg, "", typ)
- }
- }
-
- // All embedded types should be interfaces; however, defined types
- // may not yet be fully resolved. Only verify that non-defined types
- // are interfaces. This matches the behavior of the code before the
- // fix for #25301 (issue #25596).
- for _, t := range embeddeds {
- if _, ok := t.(*Named); !ok && !IsInterface(t) {
- panic("embedded type is not an interface")
- }
- }
-
- // sort for API stability
- sortMethods(methods)
- sortTypes(embeddeds)
-
- typ.methods = methods
- typ.embeddeds = embeddeds
- return typ
-}
-
-// NumExplicitMethods returns the number of explicitly declared methods of interface t.
-func (t *Interface) NumExplicitMethods() int { return len(t.methods) }
-
-// ExplicitMethod returns the i'th explicitly declared method of interface t for 0 <= i < t.NumExplicitMethods().
-// The methods are ordered by their unique Id.
-func (t *Interface) ExplicitMethod(i int) *Func { return t.methods[i] }
-
-// NumEmbeddeds returns the number of embedded types in interface t.
-func (t *Interface) NumEmbeddeds() int { return len(t.embeddeds) }
-
-// Embedded returns the i'th embedded defined (*Named) type of interface t for 0 <= i < t.NumEmbeddeds().
-// The result is nil if the i'th embedded type is not a defined type.
-//
-// Deprecated: Use EmbeddedType which is not restricted to defined (*Named) types.
-func (t *Interface) Embedded(i int) *Named { tname, _ := t.embeddeds[i].(*Named); return tname }
-
-// EmbeddedType returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds().
-func (t *Interface) EmbeddedType(i int) Type { return t.embeddeds[i] }
-
-// NumMethods returns the total number of methods of interface t.
-// The interface must have been completed.
-func (t *Interface) NumMethods() int { t.assertCompleteness(); return len(t.allMethods) }
-
-func (t *Interface) assertCompleteness() {
- if t.allMethods == nil {
- panic("interface is incomplete")
- }
-}
-
-// Method returns the i'th method of interface t for 0 <= i < t.NumMethods().
-// The methods are ordered by their unique Id.
-// The interface must have been completed.
-func (t *Interface) Method(i int) *Func { t.assertCompleteness(); return t.allMethods[i] }
-
-// Empty reports whether t is the empty interface.
-func (t *Interface) Empty() bool {
- if t.allMethods != nil {
- // interface is complete - quick test
- // A non-nil allTypes may still be empty and represents the bottom type.
- return len(t.allMethods) == 0 && t.allTypes == nil
- }
- return !t.iterate(func(t *Interface) bool {
- return len(t.methods) > 0 || t.types != nil
- }, nil)
-}
-
-// _HasTypeList reports whether interface t has a type list, possibly from an embedded type.
-func (t *Interface) _HasTypeList() bool {
- if t.allMethods != nil {
- // interface is complete - quick test
- return t.allTypes != nil
- }
-
- return t.iterate(func(t *Interface) bool {
- return t.types != nil
- }, nil)
-}
-
-// _IsComparable reports whether interface t is or embeds the predeclared interface "comparable".
-func (t *Interface) _IsComparable() bool {
- if t.allMethods != nil {
- // interface is complete - quick test
- _, m := lookupMethod(t.allMethods, nil, "==")
- return m != nil
- }
-
- return t.iterate(func(t *Interface) bool {
- _, m := lookupMethod(t.methods, nil, "==")
- return m != nil
- }, nil)
-}
-
-// _IsConstraint reports t.HasTypeList() || t.IsComparable().
-func (t *Interface) _IsConstraint() bool {
- if t.allMethods != nil {
- // interface is complete - quick test
- if t.allTypes != nil {
- return true
- }
- _, m := lookupMethod(t.allMethods, nil, "==")
- return m != nil
- }
-
- return t.iterate(func(t *Interface) bool {
- if t.types != nil {
- return true
- }
- _, m := lookupMethod(t.methods, nil, "==")
- return m != nil
- }, nil)
-}
-
-// iterate calls f with t and then with any embedded interface of t, recursively, until f returns true.
-// iterate reports whether any call to f returned true.
-func (t *Interface) iterate(f func(*Interface) bool, seen map[*Interface]bool) bool {
- if f(t) {
- return true
- }
- for _, e := range t.embeddeds {
- // e should be an interface but be careful (it may be invalid)
- if e := asInterface(e); e != nil {
- // Cyclic interfaces such as "type E interface { E }" are not permitted
- // but they are still constructed and we need to detect such cycles.
- if seen[e] {
- continue
- }
- if seen == nil {
- seen = make(map[*Interface]bool)
- }
- seen[e] = true
- if e.iterate(f, seen) {
- return true
+ if su != nil {
+ u = match(su, u)
+ if u == nil {
+ return false
}
}
- }
- return false
-}
-
-// isSatisfiedBy reports whether interface t's type list is satisfied by the type typ.
-// If the type list is empty (absent), typ trivially satisfies the interface.
-// TODO(gri) This is not a great name. Eventually, we should have a more comprehensive
-// "implements" predicate.
-func (t *Interface) isSatisfiedBy(typ Type) bool {
- t.Complete()
- if t.allTypes == nil {
+ // su == nil || match(su, u) != nil
+ su = u
return true
+ }) {
+ return su
}
- types := unpackType(t.allTypes)
- return includes(types, typ) || includes(types, under(typ))
+ return nil
}
-// Complete computes the interface's method set. It must be called by users of
-// NewInterfaceType and NewInterface after the interface's embedded types are
-// fully defined and before using the interface type in any way other than to
-// form other types. The interface must not contain duplicate methods or a
-// panic occurs. Complete returns the receiver.
-func (t *Interface) Complete() *Interface {
- // TODO(gri) consolidate this method with Checker.completeInterface
- if t.allMethods != nil {
- return t
+// structuralString is like structuralType but also considers []byte
+// and strings as identical. In this case, if successful and we saw
+// a string, the result is of type (possibly untyped) string.
+func structuralString(t Type) Type {
+ tpar, _ := t.(*TypeParam)
+ if tpar == nil {
+ return under(t) // string or untyped string
}
- t.allMethods = markComplete // avoid infinite recursion
-
- var todo []*Func
- var methods []*Func
- var seen objset
- addMethod := func(m *Func, explicit bool) {
- switch other := seen.insert(m); {
- case other == nil:
- methods = append(methods, m)
- case explicit:
- panic("duplicate method " + m.name)
- default:
- // check method signatures after all locally embedded interfaces are computed
- todo = append(todo, m, other.(*Func))
+ var su Type
+ hasString := false
+ if tpar.underIs(func(u Type) bool {
+ if u == nil {
+ return false
}
- }
-
- for _, m := range t.methods {
- addMethod(m, true)
- }
-
- allTypes := t.types
-
- for _, typ := range t.embeddeds {
- utyp := under(typ)
- etyp := asInterface(utyp)
- if etyp == nil {
- if utyp != Typ[Invalid] {
- panic(fmt.Sprintf("%s is not an interface", typ))
- }
- continue
+ if isString(u) {
+ u = NewSlice(universeByte)
+ hasString = true
}
- etyp.Complete()
- for _, m := range etyp.allMethods {
- addMethod(m, false)
+ if su != nil {
+ u = match(su, u)
+ if u == nil {
+ return false
+ }
}
- allTypes = intersect(allTypes, etyp.allTypes)
- }
-
- for i := 0; i < len(todo); i += 2 {
- m := todo[i]
- other := todo[i+1]
- if !Identical(m.typ, other.typ) {
- panic("duplicate method " + m.name)
+ // su == nil || match(su, u) != nil
+ su = u
+ return true
+ }) {
+ if hasString {
+ return Typ[String]
}
+ return su
}
-
- if methods != nil {
- sortMethods(methods)
- t.allMethods = methods
- }
- t.allTypes = allTypes
-
- return t
-}
-
-// A Map represents a map type.
-type Map struct {
- key, elem Type
-}
-
-// NewMap returns a new map for the given key and element types.
-func NewMap(key, elem Type) *Map {
- return &Map{key: key, elem: elem}
-}
-
-// Key returns the key type of map m.
-func (m *Map) Key() Type { return m.key }
-
-// Elem returns the element type of map m.
-func (m *Map) Elem() Type { return m.elem }
-
-// A Chan represents a channel type.
-type Chan struct {
- dir ChanDir
- elem Type
-}
-
-// A ChanDir value indicates a channel direction.
-type ChanDir int
-
-// The direction of a channel is indicated by one of these constants.
-const (
- SendRecv ChanDir = iota
- SendOnly
- RecvOnly
-)
-
-// NewChan returns a new channel type for the given direction and element type.
-func NewChan(dir ChanDir, elem Type) *Chan {
- return &Chan{dir: dir, elem: elem}
-}
-
-// Dir returns the direction of channel c.
-func (c *Chan) Dir() ChanDir { return c.dir }
-
-// Elem returns the element type of channel c.
-func (c *Chan) Elem() Type { return c.elem }
-
-// A Named represents a named (defined) type.
-type Named struct {
- check *Checker // for Named.under implementation; nilled once under has been called
- info typeInfo // for cycle detection
- obj *TypeName // corresponding declared object
- orig Type // type (on RHS of declaration) this *Named type is derived of (for cycle reporting)
- underlying Type // possibly a *Named during setup; never a *Named once set up completely
- tparams []*TypeName // type parameters, or nil
- targs []Type // 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
-}
-
-// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
-// If the given type name obj doesn't have a type yet, its type is set to the returned named type.
-// The underlying type must not be a *Named.
-func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
- if _, ok := underlying.(*Named); ok {
- panic("types.NewNamed: underlying type must not be *Named")
- }
- return (*Checker)(nil).newNamed(obj, underlying, methods)
+ return nil
}
-func (check *Checker) newNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
- typ := &Named{check: check, obj: obj, orig: underlying, underlying: underlying, methods: methods}
- if obj.typ == nil {
- obj.typ = typ
- }
- // Ensure that typ is always expanded, at which point the check field can be
- // nilled out.
- //
- // Note that currently we cannot nil out check inside typ.under(), because
- // it's possible that typ is expanded multiple times.
- //
- // TODO(rFindley): clean this up so that under is the only function mutating
- // named types.
- if check != nil {
- check.later(func() {
- switch typ.under().(type) {
- case *Named, *instance:
- panic("internal error: unexpanded underlying type")
+// If x and y are identical, match returns x.
+// If x and y are identical channels but for their direction
+// and one of them is unrestricted, match returns the channel
+// with the restricted direction.
+// In all other cases, match returns nil.
+func match(x, y Type) Type {
+ // Common case: we don't have channels.
+ if Identical(x, y) {
+ return x
+ }
+
+ // We may have channels that differ in direction only.
+ if x, _ := x.(*Chan); x != nil {
+ if y, _ := y.(*Chan); y != nil && Identical(x.elem, y.elem) {
+ // We have channels that differ in direction only.
+ // If there's an unrestricted channel, select the restricted one.
+ switch {
+ case x.dir == SendRecv:
+ return y
+ case y.dir == SendRecv:
+ return x
}
- typ.check = nil
- })
- }
- return typ
-}
-
-// Obj returns the type name for the named type t.
-func (t *Named) Obj() *TypeName { return t.obj }
-
-// TODO(gri) Come up with a better representation and API to distinguish
-// between parameterized instantiated and non-instantiated types.
-
-// _TParams returns the type parameters of the named type t, or nil.
-// The result is non-nil for an (originally) parameterized type even if it is instantiated.
-func (t *Named) _TParams() []*TypeName { return t.tparams }
-
-// _TArgs returns the type arguments after instantiation of the named type t, or nil if not instantiated.
-func (t *Named) _TArgs() []Type { return t.targs }
-
-// _SetTArgs sets the type arguments of Named.
-func (t *Named) _SetTArgs(args []Type) { t.targs = args }
-
-// NumMethods returns the number of explicit methods whose receiver is named type t.
-func (t *Named) NumMethods() int { return len(t.methods) }
-
-// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
-func (t *Named) Method(i int) *Func { return t.methods[i] }
-
-// SetUnderlying sets the underlying type and marks t as complete.
-func (t *Named) SetUnderlying(underlying Type) {
- if underlying == nil {
- panic("types.Named.SetUnderlying: underlying type must not be nil")
- }
- if _, ok := underlying.(*Named); ok {
- panic("types.Named.SetUnderlying: underlying type must not be *Named")
- }
- t.underlying = underlying
-}
-
-// AddMethod adds method m unless it is already in the method list.
-func (t *Named) AddMethod(m *Func) {
- if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 {
- t.methods = append(t.methods, m)
- }
-}
-
-// Note: This is a uint32 rather than a uint64 because the
-// respective 64 bit atomic instructions are not available
-// on all platforms.
-var lastId uint32
-
-// nextId returns a value increasing monotonically by 1 with
-// each call, starting with 1. It may be called concurrently.
-func nextId() uint64 { return uint64(atomic.AddUint32(&lastId, 1)) }
-
-// A _TypeParam represents a type parameter type.
-type _TypeParam struct {
- check *Checker // for lazy type bound completion
- id uint64 // unique id
- obj *TypeName // corresponding type name
- index int // parameter index
- bound Type // *Named or *Interface; underlying type is always *Interface
-}
-
-// newTypeParam returns a new TypeParam.
-func (check *Checker) newTypeParam(obj *TypeName, index int, bound Type) *_TypeParam {
- assert(bound != nil)
- typ := &_TypeParam{check: check, id: nextId(), obj: obj, index: index, bound: bound}
- if obj.typ == nil {
- obj.typ = typ
- }
- return typ
-}
-
-func (t *_TypeParam) Bound() *Interface {
- iface := asInterface(t.bound)
- // use the type bound position if we have one
- pos := token.NoPos
- if n, _ := t.bound.(*Named); n != nil {
- pos = n.obj.pos
- }
- // TODO(rFindley) switch this to an unexported method on Checker.
- t.check.completeInterface(pos, iface)
- return iface
-}
-
-// optype returns a type's operational type. Except for
-// type parameters, the operational type is the same
-// as the underlying type (as returned by under). For
-// Type parameters, the operational type is determined
-// by the corresponding type bound's type list. The
-// result may be the bottom or top type, but it is never
-// the incoming type parameter.
-func optype(typ Type) Type {
- if t := asTypeParam(typ); t != nil {
- // If the optype is typ, return the top type as we have
- // no information. It also prevents infinite recursion
- // via the asTypeParam converter function. This can happen
- // for a type parameter list of the form:
- // (type T interface { type T }).
- // See also issue #39680.
- if u := t.Bound().allTypes; u != nil && u != typ {
- // u != typ and u is a type parameter => under(u) != typ, so this is ok
- return under(u)
- }
- return theTop
- }
- return under(typ)
-}
-
-// An instance represents an instantiated generic type syntactically
-// (without expanding the instantiation). Type instances appear only
-// during type-checking and are replaced by their fully instantiated
-// (expanded) types before the end of type-checking.
-type instance struct {
- check *Checker // for lazy instantiation
- pos token.Pos // position of type instantiation; for error reporting only
- base *Named // parameterized type to be instantiated
- targs []Type // type arguments
- poslist []token.Pos // position of each targ; for error reporting only
- value Type // base(targs...) after instantiation or Typ[Invalid]; nil if not yet set
-}
-
-// expand returns the instantiated (= expanded) type of t.
-// The result is either an instantiated *Named type, or
-// Typ[Invalid] if there was an error.
-func (t *instance) expand() Type {
- v := t.value
- if v == nil {
- v = t.check.instantiate(t.pos, t.base, t.targs, t.poslist)
- if v == nil {
- v = Typ[Invalid]
}
- t.value = v
- }
- // After instantiation we must have an invalid or a *Named type.
- if debug && v != Typ[Invalid] {
- _ = v.(*Named)
- }
- return v
-}
-
-// expand expands a type instance into its instantiated
-// type and leaves all other types alone. expand does
-// not recurse.
-func expand(typ Type) Type {
- if t, _ := typ.(*instance); t != nil {
- return t.expand()
- }
- return typ
-}
-
-// expandf is set to expand.
-// Call expandf when calling expand causes compile-time cycle error.
-var expandf func(Type) Type
-
-func init() { expandf = expand }
-
-// bottom represents the bottom of the type lattice.
-// It is the underlying type of a type parameter that
-// cannot be satisfied by any type, usually because
-// the intersection of type constraints left nothing).
-type bottom struct{}
-
-// theBottom is the singleton bottom type.
-var theBottom = &bottom{}
-
-// top represents the top of the type lattice.
-// It is the underlying type of a type parameter that
-// can be satisfied by any type (ignoring methods),
-// usually because the type constraint has no type
-// list.
-type top struct{}
-
-// theTop is the singleton top type.
-var theTop = &top{}
-
-// Type-specific implementations of Underlying.
-func (t *Basic) Underlying() Type { return t }
-func (t *Array) Underlying() Type { return t }
-func (t *Slice) Underlying() Type { return t }
-func (t *Struct) Underlying() Type { return t }
-func (t *Pointer) Underlying() Type { return t }
-func (t *Tuple) Underlying() Type { return t }
-func (t *Signature) Underlying() Type { return t }
-func (t *_Sum) Underlying() Type { return t }
-func (t *Interface) Underlying() Type { return t }
-func (t *Map) Underlying() Type { return t }
-func (t *Chan) Underlying() Type { return t }
-func (t *Named) Underlying() Type { return t.underlying }
-func (t *_TypeParam) Underlying() Type { return t }
-func (t *instance) Underlying() Type { return t }
-func (t *bottom) Underlying() Type { return t }
-func (t *top) Underlying() Type { return t }
-
-// Type-specific implementations of String.
-func (t *Basic) String() string { return TypeString(t, nil) }
-func (t *Array) String() string { return TypeString(t, nil) }
-func (t *Slice) String() string { return TypeString(t, nil) }
-func (t *Struct) String() string { return TypeString(t, nil) }
-func (t *Pointer) String() string { return TypeString(t, nil) }
-func (t *Tuple) String() string { return TypeString(t, nil) }
-func (t *Signature) String() string { return TypeString(t, nil) }
-func (t *_Sum) String() string { return TypeString(t, nil) }
-func (t *Interface) String() string { return TypeString(t, nil) }
-func (t *Map) String() string { return TypeString(t, nil) }
-func (t *Chan) String() string { return TypeString(t, nil) }
-func (t *Named) String() string { return TypeString(t, nil) }
-func (t *_TypeParam) String() string { return TypeString(t, nil) }
-func (t *instance) String() string { return TypeString(t, nil) }
-func (t *bottom) String() string { return TypeString(t, nil) }
-func (t *top) String() string { return TypeString(t, nil) }
-
-// under returns the true expanded underlying type.
-// If it doesn't exist, the result is Typ[Invalid].
-// 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 *Sum?
- if n := asNamed(t); n != nil {
- return n.under()
}
- return t
-}
-
-// Converters
-//
-// A converter must only be called when a type is
-// known to be fully set up. A converter returns
-// a type's operational type (see comment for optype)
-// or nil if the type argument is not of the
-// respective type.
-
-func asBasic(t Type) *Basic {
- op, _ := optype(t).(*Basic)
- return op
-}
-
-func asArray(t Type) *Array {
- op, _ := optype(t).(*Array)
- return op
-}
-
-func asSlice(t Type) *Slice {
- op, _ := optype(t).(*Slice)
- return op
-}
-
-func asStruct(t Type) *Struct {
- op, _ := optype(t).(*Struct)
- return op
-}
-
-func asPointer(t Type) *Pointer {
- op, _ := optype(t).(*Pointer)
- return op
-}
-func asTuple(t Type) *Tuple {
- op, _ := optype(t).(*Tuple)
- return op
-}
-
-func asSignature(t Type) *Signature {
- op, _ := optype(t).(*Signature)
- return op
-}
-
-func asSum(t Type) *_Sum {
- op, _ := optype(t).(*_Sum)
- return op
-}
-
-func asInterface(t Type) *Interface {
- op, _ := optype(t).(*Interface)
- return op
-}
-
-func asMap(t Type) *Map {
- op, _ := optype(t).(*Map)
- return op
-}
-
-func asChan(t Type) *Chan {
- op, _ := optype(t).(*Chan)
- return op
-}
-
-// If the argument to asNamed and asTypeParam is of the respective types
-// (possibly after expanding an instance type), these methods return that type.
-// Otherwise the result is nil.
-
-func asNamed(t Type) *Named {
- e, _ := expand(t).(*Named)
- return e
-}
-
-func asTypeParam(t Type) *_TypeParam {
- u, _ := under(t).(*_TypeParam)
- return u
+ // types are different
+ return nil
}
diff --git a/libgo/go/go/types/typelists.go b/libgo/go/go/types/typelists.go
new file mode 100644
index 0000000..0f24135
--- /dev/null
+++ b/libgo/go/go/types/typelists.go
@@ -0,0 +1,69 @@
+// 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.
+
+package types
+
+// TypeParamList holds a list of type parameters.
+type TypeParamList struct{ tparams []*TypeParam }
+
+// Len returns the number of type parameters in the list.
+// It is safe to call on a nil receiver.
+func (l *TypeParamList) Len() int { return len(l.list()) }
+
+// At returns the i'th type parameter in the list.
+func (l *TypeParamList) At(i int) *TypeParam { return l.tparams[i] }
+
+// list is for internal use where we expect a []*TypeParam.
+// TODO(rfindley): list should probably be eliminated: we can pass around a
+// TypeParamList instead.
+func (l *TypeParamList) list() []*TypeParam {
+ if l == nil {
+ return nil
+ }
+ return l.tparams
+}
+
+// TypeList holds a list of types.
+type TypeList struct{ types []Type }
+
+// newTypeList returns a new TypeList with the types in list.
+func newTypeList(list []Type) *TypeList {
+ if len(list) == 0 {
+ return nil
+ }
+ return &TypeList{list}
+}
+
+// Len returns the number of types in the list.
+// It is safe to call on a nil receiver.
+func (l *TypeList) Len() int { return len(l.list()) }
+
+// At returns the i'th type in the list.
+func (l *TypeList) At(i int) Type { return l.types[i] }
+
+// list is for internal use where we expect a []Type.
+// TODO(rfindley): list should probably be eliminated: we can pass around a
+// TypeList instead.
+func (l *TypeList) list() []Type {
+ if l == nil {
+ return nil
+ }
+ return l.types
+}
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+func bindTParams(list []*TypeParam) *TypeParamList {
+ if len(list) == 0 {
+ return nil
+ }
+ for i, typ := range list {
+ if typ.index >= 0 {
+ panic("type parameter bound more than once")
+ }
+ typ.index = i
+ }
+ return &TypeParamList{tparams: list}
+}
diff --git a/libgo/go/go/types/typeparam.go b/libgo/go/go/types/typeparam.go
new file mode 100644
index 0000000..03ba9be
--- /dev/null
+++ b/libgo/go/go/types/typeparam.go
@@ -0,0 +1,163 @@
+// Copyright 2011 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.
+
+package types
+
+import (
+ "go/token"
+ "sync/atomic"
+)
+
+// Note: This is a uint32 rather than a uint64 because the
+// respective 64 bit atomic instructions are not available
+// on all platforms.
+var lastID uint32
+
+// nextID returns a value increasing monotonically by 1 with
+// each call, starting with 1. It may be called concurrently.
+func nextID() uint64 { return uint64(atomic.AddUint32(&lastID, 1)) }
+
+// A TypeParam represents a type parameter type.
+type TypeParam struct {
+ check *Checker // for lazy type bound completion
+ id uint64 // unique id, for debugging only
+ obj *TypeName // corresponding type name
+ index int // type parameter index in source order, starting at 0
+ bound Type // any type, but underlying is eventually *Interface for correct programs (see TypeParam.iface)
+}
+
+// NewTypeParam returns a new TypeParam. Type parameters may be set on a Named
+// or Signature type by calling SetTypeParams. Setting a type parameter on more
+// than one type will result in a panic.
+//
+// The constraint argument can be nil, and set later via SetConstraint.
+func NewTypeParam(obj *TypeName, constraint Type) *TypeParam {
+ return (*Checker)(nil).newTypeParam(obj, constraint)
+}
+
+func (check *Checker) newTypeParam(obj *TypeName, constraint Type) *TypeParam {
+ // Always increment lastID, even if it is not used.
+ id := nextID()
+ if check != nil {
+ check.nextID++
+ id = check.nextID
+ }
+ typ := &TypeParam{check: check, id: id, obj: obj, index: -1, bound: constraint}
+ if obj.typ == nil {
+ obj.typ = typ
+ }
+ // iface may mutate typ.bound, so we must ensure that iface() is called
+ // at least once before the resulting TypeParam escapes.
+ if check != nil {
+ check.later(func() {
+ typ.iface()
+ })
+ } else if constraint != nil {
+ typ.iface()
+ }
+ return typ
+}
+
+// Index returns the index of the type param within its param list, or -1 if
+// the type parameter has not yet been bound to a type.
+func (t *TypeParam) Index() int {
+ return t.index
+}
+
+// Obj returns the type name for t.
+func (t *TypeParam) Obj() *TypeName { return t.obj }
+
+// Constraint returns the type constraint specified for t.
+func (t *TypeParam) Constraint() Type {
+ return t.bound
+}
+
+// SetConstraint sets the type constraint for t.
+//
+// SetConstraint should not be called concurrently, but once SetConstraint
+// returns the receiver t is safe for concurrent use.
+func (t *TypeParam) SetConstraint(bound Type) {
+ if bound == nil {
+ panic("nil constraint")
+ }
+ t.bound = bound
+ // iface may mutate t.bound (if bound is not an interface), so ensure that
+ // this is done before returning.
+ t.iface()
+}
+
+func (t *TypeParam) Underlying() Type {
+ return t.iface()
+}
+
+func (t *TypeParam) String() string { return TypeString(t, nil) }
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+// iface returns the constraint interface of t.
+// TODO(gri) If we make tparamIsIface the default, this should be renamed to under
+// (similar to Named.under).
+func (t *TypeParam) iface() *Interface {
+ bound := t.bound
+
+ // determine constraint interface
+ var ityp *Interface
+ switch u := under(bound).(type) {
+ case *Basic:
+ if u == Typ[Invalid] {
+ // error is reported elsewhere
+ return &emptyInterface
+ }
+ case *Interface:
+ if isTypeParam(bound) {
+ // error is reported in Checker.collectTypeParams
+ return &emptyInterface
+ }
+ ityp = u
+ }
+
+ // If we don't have an interface, wrap constraint into an implicit interface.
+ if ityp == nil {
+ ityp = NewInterfaceType(nil, []Type{bound})
+ ityp.implicit = true
+ t.bound = ityp // update t.bound for next time (optimization)
+ }
+
+ // compute type set if necessary
+ if ityp.tset == nil {
+ // use the (original) type bound position if we have one
+ pos := token.NoPos
+ if n, _ := bound.(*Named); n != nil {
+ pos = n.obj.pos
+ }
+ computeInterfaceTypeSet(t.check, pos, ityp)
+ }
+
+ return ityp
+}
+
+// singleType returns the single type of the type parameter constraint; or nil.
+func (t *TypeParam) singleType() Type {
+ return t.iface().typeSet().singleType()
+}
+
+// hasTerms reports whether the type parameter constraint has specific type terms.
+func (t *TypeParam) hasTerms() bool {
+ return t.iface().typeSet().hasTerms()
+}
+
+// is calls f with the specific type terms of t's constraint and reports whether
+// all calls to f returned true. If there are no specific terms, is
+// returns the result of f(nil).
+func (t *TypeParam) is(f func(*term) bool) bool {
+ return t.iface().typeSet().is(f)
+}
+
+// underIs calls f with the underlying types of the specific type terms
+// of t's constraint and reports whether all calls to f returned true.
+// If there are no specific terms, underIs returns the result of f(nil).
+func (t *TypeParam) underIs(f func(Type) bool) bool {
+ return t.iface().typeSet().underIs(f)
+}
diff --git a/libgo/go/go/types/types_test.go b/libgo/go/go/types/types_test.go
deleted file mode 100644
index 25cd996..0000000
--- a/libgo/go/go/types/types_test.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// 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.
-
-package types
-
-import "sync/atomic"
-
-// Upon calling ResetId, nextId starts with 1 again.
-// It may be called concurrently. This is only needed
-// for tests where we may want to have a consistent
-// numbering for each individual test case.
-func ResetId() { atomic.StoreUint32(&lastId, 0) }
-
-// SetGoVersion sets the unexported goVersion field on config, so that tests
-// which assert on behavior for older Go versions can set it.
-func SetGoVersion(config *Config, goVersion string) {
- config.goVersion = goVersion
-}
diff --git a/libgo/go/go/types/typeset.go b/libgo/go/go/types/typeset.go
new file mode 100644
index 0000000..4598daa
--- /dev/null
+++ b/libgo/go/go/types/typeset.go
@@ -0,0 +1,404 @@
+// 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.
+
+package types
+
+import (
+ "bytes"
+ "fmt"
+ "go/token"
+ "sort"
+)
+
+// ----------------------------------------------------------------------------
+// API
+
+// A _TypeSet represents the type set of an interface.
+type _TypeSet struct {
+ comparable bool // if set, the interface is or embeds comparable
+ // TODO(gri) consider using a set for the methods for faster lookup
+ methods []*Func // all methods of the interface; sorted by unique ID
+ terms termlist // type terms of the type set
+}
+
+// IsEmpty reports whether type set s is the empty set.
+func (s *_TypeSet) IsEmpty() bool { return s.terms.isEmpty() }
+
+// IsAll reports whether type set s is the set of all types (corresponding to the empty interface).
+func (s *_TypeSet) IsAll() bool { return !s.comparable && len(s.methods) == 0 && s.terms.isAll() }
+
+// IsMethodSet reports whether the interface t is fully described by its method set.
+func (s *_TypeSet) IsMethodSet() bool { return !s.comparable && s.terms.isAll() }
+
+// IsComparable reports whether each type in the set is comparable.
+func (s *_TypeSet) IsComparable(seen map[Type]bool) bool {
+ if s.terms.isAll() {
+ return s.comparable
+ }
+ return s.is(func(t *term) bool {
+ return t != nil && comparable(t.typ, seen)
+ })
+}
+
+// TODO(gri) IsTypeSet is not a great name for this predicate. Find a better one.
+
+// IsTypeSet reports whether the type set s is represented by a finite set of underlying types.
+func (s *_TypeSet) IsTypeSet() bool {
+ return !s.comparable && len(s.methods) == 0
+}
+
+// NumMethods returns the number of methods available.
+func (s *_TypeSet) NumMethods() int { return len(s.methods) }
+
+// Method returns the i'th method of type set s for 0 <= i < s.NumMethods().
+// The methods are ordered by their unique ID.
+func (s *_TypeSet) Method(i int) *Func { return s.methods[i] }
+
+// LookupMethod returns the index of and method with matching package and name, or (-1, nil).
+func (s *_TypeSet) LookupMethod(pkg *Package, name string) (int, *Func) {
+ // TODO(gri) s.methods is sorted - consider binary search
+ return lookupMethod(s.methods, pkg, name)
+}
+
+func (s *_TypeSet) String() string {
+ switch {
+ case s.IsEmpty():
+ return "∅"
+ case s.IsAll():
+ return "𝓤"
+ }
+
+ hasMethods := len(s.methods) > 0
+ hasTerms := s.hasTerms()
+
+ var buf bytes.Buffer
+ buf.WriteByte('{')
+ if s.comparable {
+ buf.WriteString("comparable")
+ if hasMethods || hasTerms {
+ buf.WriteString("; ")
+ }
+ }
+ for i, m := range s.methods {
+ if i > 0 {
+ buf.WriteString("; ")
+ }
+ buf.WriteString(m.String())
+ }
+ if hasMethods && hasTerms {
+ buf.WriteString("; ")
+ }
+ if hasTerms {
+ buf.WriteString(s.terms.String())
+ }
+ buf.WriteString("}")
+ return buf.String()
+}
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+// hasTerms reports whether the type set has specific type terms.
+func (s *_TypeSet) hasTerms() bool { return !s.terms.isEmpty() && !s.terms.isAll() }
+
+// singleType returns the single type in s if there is exactly one; otherwise the result is nil.
+func (s *_TypeSet) singleType() Type { return s.terms.singleType() }
+
+// subsetOf reports whether s1 ⊆ s2.
+func (s1 *_TypeSet) subsetOf(s2 *_TypeSet) bool { return s1.terms.subsetOf(s2.terms) }
+
+// TODO(gri) TypeSet.is and TypeSet.underIs should probably also go into termlist.go
+
+// is calls f with the specific type terms of s and reports whether
+// all calls to f returned true. If there are no specific terms, is
+// returns the result of f(nil).
+func (s *_TypeSet) is(f func(*term) bool) bool {
+ if !s.hasTerms() {
+ return f(nil)
+ }
+ for _, t := range s.terms {
+ assert(t.typ != nil)
+ if !f(t) {
+ return false
+ }
+ }
+ return true
+}
+
+// underIs calls f with the underlying types of the specific type terms
+// of s and reports whether all calls to f returned true. If there are
+// no specific terms, underIs returns the result of f(nil).
+func (s *_TypeSet) underIs(f func(Type) bool) bool {
+ if !s.hasTerms() {
+ return f(nil)
+ }
+ for _, t := range s.terms {
+ assert(t.typ != nil)
+ // x == under(x) for ~x terms
+ u := t.typ
+ if !t.tilde {
+ u = under(u)
+ }
+ if debug {
+ assert(Identical(u, under(u)))
+ }
+ if !f(u) {
+ return false
+ }
+ }
+ return true
+}
+
+// topTypeSet may be used as type set for the empty interface.
+var topTypeSet = _TypeSet{terms: allTermlist}
+
+// computeInterfaceTypeSet may be called with check == nil.
+func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_TypeSet {
+ if ityp.tset != nil {
+ return ityp.tset
+ }
+
+ // If the interface is not fully set up yet, the type set will
+ // not be complete, which may lead to errors when using the
+ // type set (e.g. missing method). Don't compute a partial type
+ // set (and don't store it!), so that we still compute the full
+ // type set eventually. Instead, return the top type set and
+ // let any follow-on errors play out.
+ //
+ // TODO(gri) Consider recording when this happens and reporting
+ // it as an error (but only if there were no other errors so to
+ // to not have unnecessary follow-on errors).
+ if !ityp.complete {
+ return &topTypeSet
+ }
+
+ if check != nil && trace {
+ // Types don't generally have position information.
+ // If we don't have a valid pos provided, try to use
+ // one close enough.
+ if !pos.IsValid() && len(ityp.methods) > 0 {
+ pos = ityp.methods[0].pos
+ }
+
+ check.trace(pos, "type set for %s", ityp)
+ check.indent++
+ defer func() {
+ check.indent--
+ check.trace(pos, "=> %s ", ityp.typeSet())
+ }()
+ }
+
+ // An infinitely expanding interface (due to a cycle) is detected
+ // elsewhere (Checker.validType), so here we simply assume we only
+ // have valid interfaces. Mark the interface as complete to avoid
+ // infinite recursion if the validType check occurs later for some
+ // reason.
+ ityp.tset = &_TypeSet{terms: allTermlist} // TODO(gri) is this sufficient?
+
+ var unionSets map[*Union]*_TypeSet
+ if check != nil {
+ if check.unionTypeSets == nil {
+ check.unionTypeSets = make(map[*Union]*_TypeSet)
+ }
+ unionSets = check.unionTypeSets
+ } else {
+ unionSets = make(map[*Union]*_TypeSet)
+ }
+
+ // Methods of embedded interfaces are collected unchanged; i.e., the identity
+ // of a method I.m's Func Object of an interface I is the same as that of
+ // the method m in an interface that embeds interface I. On the other hand,
+ // if a method is embedded via multiple overlapping embedded interfaces, we
+ // don't provide a guarantee which "original m" got chosen for the embedding
+ // interface. See also issue #34421.
+ //
+ // If we don't care to provide this identity guarantee anymore, instead of
+ // reusing the original method in embeddings, we can clone the method's Func
+ // Object and give it the position of a corresponding embedded interface. Then
+ // we can get rid of the mpos map below and simply use the cloned method's
+ // position.
+
+ var todo []*Func
+ var seen objset
+ var methods []*Func
+ mpos := make(map[*Func]token.Pos) // method specification or method embedding position, for good error messages
+ addMethod := func(pos token.Pos, m *Func, explicit bool) {
+ switch other := seen.insert(m); {
+ case other == nil:
+ methods = append(methods, m)
+ mpos[m] = pos
+ case explicit:
+ if check == nil {
+ panic(fmt.Sprintf("%v: duplicate method %s", m.pos, m.name))
+ }
+ // check != nil
+ check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name)
+ check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
+ default:
+ // We have a duplicate method name in an embedded (not explicitly declared) method.
+ // Check method signatures after all types are computed (issue #33656).
+ // If we're pre-go1.14 (overlapping embeddings are not permitted), report that
+ // error here as well (even though we could do it eagerly) because it's the same
+ // error message.
+ if check == nil {
+ // check method signatures after all locally embedded interfaces are computed
+ todo = append(todo, m, other.(*Func))
+ break
+ }
+ // check != nil
+ check.later(func() {
+ if !check.allowVersion(m.pkg, 1, 14) || !Identical(m.typ, other.Type()) {
+ check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name)
+ check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
+ }
+ })
+ }
+ }
+
+ for _, m := range ityp.methods {
+ addMethod(m.pos, m, true)
+ }
+
+ // collect embedded elements
+ var allTerms = allTermlist
+ for i, typ := range ityp.embeddeds {
+ // The embedding position is nil for imported interfaces
+ // and also for interface copies after substitution (but
+ // in that case we don't need to report errors again).
+ var pos token.Pos // embedding position
+ if ityp.embedPos != nil {
+ pos = (*ityp.embedPos)[i]
+ }
+ var terms termlist
+ switch u := under(typ).(type) {
+ case *Interface:
+ // For now we don't permit type parameters as constraints.
+ assert(!isTypeParam(typ))
+ 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(atPos(pos), _UnsupportedFeature, "embedding constraint interface %s requires go1.18 or later", typ)
+ continue
+ }
+ if tset.comparable {
+ ityp.tset.comparable = true
+ }
+ for _, m := range tset.methods {
+ addMethod(pos, m, false) // use embedding position pos rather than m.pos
+ }
+ terms = tset.terms
+ case *Union:
+ if check != nil && !check.allowVersion(check.pkg, 1, 18) {
+ check.errorf(atPos(pos), _InvalidIfaceEmbed, "embedding interface element %s requires go1.18 or later", u)
+ continue
+ }
+ tset := computeUnionTypeSet(check, unionSets, pos, u)
+ if tset == &invalidTypeSet {
+ continue // ignore invalid unions
+ }
+ terms = tset.terms
+ default:
+ if u == Typ[Invalid] {
+ continue
+ }
+ if check != nil && !check.allowVersion(check.pkg, 1, 18) {
+ check.errorf(atPos(pos), _InvalidIfaceEmbed, "embedding non-interface type %s requires go1.18 or later", typ)
+ continue
+ }
+ terms = termlist{{false, typ}}
+ }
+ // The type set of an interface is the intersection
+ // of the type sets of all its elements.
+ // Intersection cannot produce longer termlists and
+ // thus cannot overflow.
+ allTerms = allTerms.intersect(terms)
+ }
+ ityp.embedPos = nil // not needed anymore (errors have been reported)
+
+ // process todo's (this only happens if check == nil)
+ for i := 0; i < len(todo); i += 2 {
+ m := todo[i]
+ other := todo[i+1]
+ if !Identical(m.typ, other.typ) {
+ panic(fmt.Sprintf("%v: duplicate method %s", m.pos, m.name))
+ }
+ }
+
+ if methods != nil {
+ sort.Sort(byUniqueMethodName(methods))
+ ityp.tset.methods = methods
+ }
+ ityp.tset.terms = allTerms
+
+ return ityp.tset
+}
+
+func sortMethods(list []*Func) {
+ sort.Sort(byUniqueMethodName(list))
+}
+
+func assertSortedMethods(list []*Func) {
+ if !debug {
+ panic("assertSortedMethods called outside debug mode")
+ }
+ if !sort.IsSorted(byUniqueMethodName(list)) {
+ panic("methods not sorted")
+ }
+}
+
+// byUniqueMethodName method lists can be sorted by their unique method names.
+type byUniqueMethodName []*Func
+
+func (a byUniqueMethodName) Len() int { return len(a) }
+func (a byUniqueMethodName) Less(i, j int) bool { return a[i].Id() < a[j].Id() }
+func (a byUniqueMethodName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+// invalidTypeSet is a singleton type set to signal an invalid type set
+// due to an error. It's also a valid empty type set, so consumers of
+// type sets may choose to ignore it.
+var invalidTypeSet _TypeSet
+
+// computeUnionTypeSet may be called with check == nil.
+// The result is &invalidTypeSet if the union overflows.
+func computeUnionTypeSet(check *Checker, unionSets map[*Union]*_TypeSet, pos token.Pos, utyp *Union) *_TypeSet {
+ if tset, _ := unionSets[utyp]; tset != nil {
+ return tset
+ }
+
+ // avoid infinite recursion (see also computeInterfaceTypeSet)
+ unionSets[utyp] = new(_TypeSet)
+
+ var allTerms termlist
+ for _, t := range utyp.terms {
+ var terms termlist
+ u := under(t.typ)
+ if ui, _ := u.(*Interface); ui != nil {
+ // For now we don't permit type parameters as constraints.
+ assert(!isTypeParam(t.typ))
+ terms = computeInterfaceTypeSet(check, pos, ui).terms
+ } else if t.typ == Typ[Invalid] {
+ continue
+ } else {
+ if t.tilde && !Identical(t.typ, u) {
+ // There is no underlying type which is t.typ.
+ // The corresponding type set is empty.
+ t = nil // ∅ term
+ }
+ terms = termlist{(*term)(t)}
+ }
+ // The type set of a union expression is the union
+ // of the type sets of each term.
+ allTerms = allTerms.union(terms)
+ if len(allTerms) > maxTermCount {
+ if check != nil {
+ check.errorf(atPos(pos), _InvalidUnion, "cannot handle more than %d union terms (implementation limitation)", maxTermCount)
+ }
+ unionSets[utyp] = &invalidTypeSet
+ return unionSets[utyp]
+ }
+ }
+ unionSets[utyp].terms = allTerms
+
+ return unionSets[utyp]
+}
diff --git a/libgo/go/go/types/typeset_test.go b/libgo/go/go/types/typeset_test.go
new file mode 100644
index 0000000..1c0eece
--- /dev/null
+++ b/libgo/go/go/types/typeset_test.go
@@ -0,0 +1,81 @@
+// 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.
+
+package types
+
+import (
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "testing"
+)
+
+func TestInvalidTypeSet(t *testing.T) {
+ if !invalidTypeSet.IsEmpty() {
+ t.Error("invalidTypeSet is not empty")
+ }
+}
+
+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
+ src := "package p; type T interface" + body
+ fset := token.NewFileSet()
+ file, err := parser.ParseFile(fset, "p.go", src, parser.AllErrors)
+ if file == nil {
+ t.Fatalf("%s: %v (invalid test case)", body, err)
+ }
+
+ // type check
+ var conf Config
+ pkg, err := conf.Check(file.Name.Name, fset, []*ast.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/libgo/go/go/types/typestring.go b/libgo/go/go/types/typestring.go
index fe27f0f..80210a2 100644
--- a/libgo/go/go/types/typestring.go
+++ b/libgo/go/go/types/typestring.go
@@ -10,6 +10,9 @@ import (
"bytes"
"fmt"
"go/token"
+ "sort"
+ "strconv"
+ "strings"
"unicode/utf8"
)
@@ -40,33 +43,18 @@ func RelativeTo(pkg *Package) Qualifier {
}
}
-// If gcCompatibilityMode is set, printing of types is modified
-// to match the representation of some types in the gc compiler:
-//
-// - byte and rune lose their alias name and simply stand for
-// uint8 and int32 respectively
-// - embedded interfaces get flattened (the embedding info is lost,
-// and certain recursive interface types cannot be printed anymore)
-//
-// This makes it easier to compare packages computed with the type-
-// checker vs packages imported from gc export data.
-//
-// Caution: This flag affects all uses of WriteType, globally.
-// It is only provided for testing in conjunction with
-// gc-generated data.
-//
-// This flag is exported in the x/tools/go/types package. We don't
-// need it at the moment in the std repo and so we don't export it
-// anymore. We should eventually try to remove it altogether.
-// TODO(gri) remove this
-var gcCompatibilityMode bool
-
// TypeString returns the string representation of typ.
// The Qualifier controls the printing of
// package-level objects, and may be nil.
func TypeString(typ Type, qf Qualifier) string {
+ return typeString(typ, qf, false)
+}
+
+func typeString(typ Type, qf Qualifier, debug bool) string {
var buf bytes.Buffer
- WriteType(&buf, typ, qf)
+ w := newTypeWriter(&buf, qf)
+ w.debug = debug
+ w.typ(typ)
return buf.String()
}
@@ -74,172 +62,196 @@ func TypeString(typ Type, qf Qualifier) string {
// The Qualifier controls the printing of
// package-level objects, and may be nil.
func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
- writeType(buf, typ, qf, make([]Type, 0, 8))
+ newTypeWriter(buf, qf).typ(typ)
+}
+
+// WriteSignature writes the representation of the signature sig to buf,
+// without a leading "func" keyword.
+// The Qualifier controls the printing of
+// package-level objects, and may be nil.
+func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
+ newTypeWriter(buf, qf).signature(sig)
+}
+
+type typeWriter struct {
+ buf *bytes.Buffer
+ seen map[Type]bool
+ qf Qualifier
+ ctxt *Context // if non-nil, we are type hashing
+ tparams *TypeParamList // local type parameters
+ debug bool // if true, write debug annotations
+}
+
+func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter {
+ return &typeWriter{buf, make(map[Type]bool), qf, nil, nil, false}
+}
+
+func newTypeHasher(buf *bytes.Buffer, ctxt *Context) *typeWriter {
+ assert(ctxt != nil)
+ return &typeWriter{buf, make(map[Type]bool), nil, ctxt, nil, false}
}
-// instanceMarker is the prefix for an instantiated type
-// in "non-evaluated" instance form.
-const instanceMarker = '#'
-
-func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
- // Theoretically, this is a quadratic lookup algorithm, but in
- // practice deeply nested composite types with unnamed component
- // types are uncommon. This code is likely more efficient than
- // using a map.
- for _, t := range visited {
- if t == typ {
- fmt.Fprintf(buf, "○%T", goTypeName(typ)) // cycle to typ
- return
+func (w *typeWriter) byte(b byte) {
+ if w.ctxt != nil {
+ if b == ' ' {
+ b = '#'
}
+ w.buf.WriteByte(b)
+ return
+ }
+ w.buf.WriteByte(b)
+ if b == ',' || b == ';' {
+ w.buf.WriteByte(' ')
+ }
+}
+
+func (w *typeWriter) string(s string) {
+ w.buf.WriteString(s)
+}
+
+func (w *typeWriter) error(msg string) {
+ if w.ctxt != nil {
+ panic(msg)
}
- visited = append(visited, typ)
+ w.buf.WriteString("<" + msg + ">")
+}
+
+func (w *typeWriter) typ(typ Type) {
+ if w.seen[typ] {
+ w.error("cycle to " + goTypeName(typ))
+ return
+ }
+ w.seen[typ] = true
+ defer delete(w.seen, typ)
switch t := typ.(type) {
case nil:
- buf.WriteString("<nil>")
+ w.error("nil")
case *Basic:
// exported basic types go into package unsafe
// (currently this is just unsafe.Pointer)
if token.IsExported(t.name) {
if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil {
- writeTypeName(buf, obj, qf)
+ w.typeName(obj)
break
}
}
-
- if gcCompatibilityMode {
- // forget the alias names
- switch t.kind {
- case Byte:
- t = Typ[Uint8]
- case Rune:
- t = Typ[Int32]
- }
- }
- buf.WriteString(t.name)
+ w.string(t.name)
case *Array:
- fmt.Fprintf(buf, "[%d]", t.len)
- writeType(buf, t.elem, qf, visited)
+ w.byte('[')
+ w.string(strconv.FormatInt(t.len, 10))
+ w.byte(']')
+ w.typ(t.elem)
case *Slice:
- buf.WriteString("[]")
- writeType(buf, t.elem, qf, visited)
+ w.string("[]")
+ w.typ(t.elem)
case *Struct:
- buf.WriteString("struct{")
+ w.string("struct{")
for i, f := range t.fields {
if i > 0 {
- buf.WriteString("; ")
+ w.byte(';')
}
// This doesn't do the right thing for embedded type
// aliases where we should print the alias name, not
// the aliased type (see issue #44410).
if !f.embedded {
- buf.WriteString(f.name)
- buf.WriteByte(' ')
+ w.string(f.name)
+ w.byte(' ')
}
- writeType(buf, f.typ, qf, visited)
+ w.typ(f.typ)
if tag := t.Tag(i); tag != "" {
- fmt.Fprintf(buf, " %q", tag)
+ w.byte(' ')
+ // TODO(rfindley) If tag contains blanks, replacing them with '#'
+ // in Context.TypeHash may produce another tag
+ // accidentally.
+ w.string(strconv.Quote(tag))
}
}
- buf.WriteByte('}')
+ w.byte('}')
case *Pointer:
- buf.WriteByte('*')
- writeType(buf, t.base, qf, visited)
+ w.byte('*')
+ w.typ(t.base)
case *Tuple:
- writeTuple(buf, t, false, qf, visited)
+ w.tuple(t, false)
case *Signature:
- buf.WriteString("func")
- writeSignature(buf, t, qf, visited)
-
- case *_Sum:
- for i, t := range t.types {
+ w.string("func")
+ w.signature(t)
+
+ case *Union:
+ // Unions only appear as (syntactic) embedded elements
+ // in interfaces and syntactically cannot be empty.
+ if t.Len() == 0 {
+ w.error("empty union")
+ break
+ }
+ for i, t := range t.terms {
if i > 0 {
- buf.WriteString(", ")
+ w.byte('|')
}
- writeType(buf, t, qf, visited)
+ if t.tilde {
+ w.byte('~')
+ }
+ w.typ(t.typ)
}
case *Interface:
- // We write the source-level methods and embedded types rather
- // than the actual method set since resolved method signatures
- // may have non-printable cycles if parameters have embedded
- // interface types that (directly or indirectly) embed the
- // current interface. For instance, consider the result type
- // of m:
- //
- // type T interface{
- // m() interface{ T }
- // }
- //
- buf.WriteString("interface{")
- empty := true
- if gcCompatibilityMode {
- // print flattened interface
- // (useful to compare against gc-generated interfaces)
- for i, m := range t.allMethods {
- if i > 0 {
- buf.WriteString("; ")
- }
- buf.WriteString(m.name)
- writeSignature(buf, m.typ.(*Signature), qf, visited)
- empty = false
+ if w.ctxt == nil {
+ if t == universeAny.Type() {
+ // When not hashing, we can try to improve type strings by writing "any"
+ // for a type that is pointer-identical to universeAny. This logic should
+ // be deprecated by more robust handling for aliases.
+ w.string("any")
+ break
}
- if !empty && t.allTypes != nil {
- buf.WriteString("; ")
+ if t == universeComparable.Type().(*Named).underlying {
+ w.string("interface{comparable}")
+ break
}
- if t.allTypes != nil {
- buf.WriteString("type ")
- writeType(buf, t.allTypes, qf, visited)
+ }
+ if t.implicit {
+ if len(t.methods) == 0 && len(t.embeddeds) == 1 {
+ w.typ(t.embeddeds[0])
+ break
}
+ // Something's wrong with the implicit interface.
+ // Print it as such and continue.
+ w.string("/* implicit */ ")
+ }
+ w.string("interface{")
+ first := true
+ if w.ctxt != nil {
+ w.typeSet(t.typeSet())
} else {
- // print explicit interface methods and embedded types
- for i, m := range t.methods {
- if i > 0 {
- buf.WriteString("; ")
+ for _, m := range t.methods {
+ if !first {
+ w.byte(';')
}
- buf.WriteString(m.name)
- writeSignature(buf, m.typ.(*Signature), qf, visited)
- empty = false
+ first = false
+ w.string(m.name)
+ w.signature(m.typ.(*Signature))
}
- if !empty && t.types != nil {
- buf.WriteString("; ")
- }
- if t.types != nil {
- buf.WriteString("type ")
- writeType(buf, t.types, qf, visited)
- empty = false
- }
- if !empty && len(t.embeddeds) > 0 {
- buf.WriteString("; ")
- }
- for i, typ := range t.embeddeds {
- if i > 0 {
- buf.WriteString("; ")
+ for _, typ := range t.embeddeds {
+ if !first {
+ w.byte(';')
}
- writeType(buf, typ, qf, visited)
- empty = false
- }
- }
- if t.allMethods == nil || len(t.methods) > len(t.allMethods) {
- if !empty {
- buf.WriteByte(' ')
+ first = false
+ w.typ(typ)
}
- buf.WriteString("/* incomplete */")
}
- buf.WriteByte('}')
+ w.byte('}')
case *Map:
- buf.WriteString("map[")
- writeType(buf, t.key, qf, visited)
- buf.WriteByte(']')
- writeType(buf, t.elem, qf, visited)
+ w.string("map[")
+ w.typ(t.key)
+ w.byte(']')
+ w.typ(t.elem)
case *Chan:
var s string
@@ -256,158 +268,187 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
case RecvOnly:
s = "<-chan "
default:
- panic("unreachable")
+ w.error("unknown channel direction")
}
- buf.WriteString(s)
+ w.string(s)
if parens {
- buf.WriteByte('(')
+ w.byte('(')
}
- writeType(buf, t.elem, qf, visited)
+ w.typ(t.elem)
if parens {
- buf.WriteByte(')')
+ w.byte(')')
}
case *Named:
- writeTypeName(buf, t.obj, qf)
+ // If hashing, write a unique prefix for t to represent its identity, since
+ // named type identity is pointer identity.
+ if w.ctxt != nil {
+ w.string(strconv.Itoa(w.ctxt.getID(t)))
+ }
+ w.typeName(t.obj) // when hashing written for readability of the hash only
if t.targs != nil {
// instantiated type
- buf.WriteByte('[')
- writeTypeList(buf, t.targs, qf, visited)
- buf.WriteByte(']')
- } else if t.tparams != nil {
+ w.typeList(t.targs.list())
+ } else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams
// parameterized type
- writeTParamList(buf, t.tparams, qf, visited)
+ w.tParamList(t.TypeParams().list())
}
- case *_TypeParam:
- s := "?"
- if t.obj != nil {
- s = t.obj.name
+ case *TypeParam:
+ if t.obj == nil {
+ w.error("unnamed type parameter")
+ break
+ }
+ if i := tparamIndex(w.tparams.list(), t); i >= 0 {
+ // The names of type parameters that are declared by the type being
+ // hashed are not part of the type identity. Replace them with a
+ // placeholder indicating their index.
+ w.string(fmt.Sprintf("$%d", i))
+ } else {
+ w.string(t.obj.name)
+ if w.debug || w.ctxt != nil {
+ w.string(subscript(t.id))
+ }
}
- buf.WriteString(s + subscript(t.id))
-
- case *instance:
- buf.WriteByte(instanceMarker) // indicate "non-evaluated" syntactic instance
- writeTypeName(buf, t.base.obj, qf)
- buf.WriteByte('[')
- writeTypeList(buf, t.targs, qf, visited)
- buf.WriteByte(']')
-
- case *bottom:
- buf.WriteString("⊥")
-
- case *top:
- buf.WriteString("⊤")
default:
// For externally defined implementations of Type.
- buf.WriteString(t.String())
+ // Note: In this case cycles won't be caught.
+ w.string(t.String())
}
}
-func writeTypeList(buf *bytes.Buffer, list []Type, qf Qualifier, visited []Type) {
+// typeSet writes a canonical hash for an interface type set.
+func (w *typeWriter) typeSet(s *_TypeSet) {
+ assert(w.ctxt != nil)
+ first := true
+ for _, m := range s.methods {
+ if !first {
+ w.byte(';')
+ }
+ first = false
+ w.string(m.name)
+ w.signature(m.typ.(*Signature))
+ }
+ switch {
+ case s.terms.isAll():
+ // nothing to do
+ case s.terms.isEmpty():
+ w.string(s.terms.String())
+ default:
+ var termHashes []string
+ for _, term := range s.terms {
+ // terms are not canonically sorted, so we sort their hashes instead.
+ var buf bytes.Buffer
+ if term.tilde {
+ buf.WriteByte('~')
+ }
+ newTypeHasher(&buf, w.ctxt).typ(term.typ)
+ termHashes = append(termHashes, buf.String())
+ }
+ sort.Strings(termHashes)
+ if !first {
+ w.byte(';')
+ }
+ w.string(strings.Join(termHashes, "|"))
+ }
+}
+
+func (w *typeWriter) typeList(list []Type) {
+ w.byte('[')
for i, typ := range list {
if i > 0 {
- buf.WriteString(", ")
+ w.byte(',')
}
- writeType(buf, typ, qf, visited)
+ w.typ(typ)
}
+ w.byte(']')
}
-func writeTParamList(buf *bytes.Buffer, list []*TypeName, qf Qualifier, visited []Type) {
- // TODO(rFindley) compare this with the corresponding implementation in types2
- buf.WriteString("[")
+func (w *typeWriter) tParamList(list []*TypeParam) {
+ w.byte('[')
var prev Type
- for i, p := range list {
- // TODO(rFindley) support 'any' sugar here.
- var b Type = &emptyInterface
- if t, _ := p.typ.(*_TypeParam); t != nil && t.bound != nil {
- b = t.bound
+ for i, tpar := range list {
+ // Determine the type parameter and its constraint.
+ // list is expected to hold type parameter names,
+ // but don't crash if that's not the case.
+ if tpar == nil {
+ w.error("nil type parameter")
+ continue
}
if i > 0 {
- if b != prev {
- // type bound changed - write previous one before advancing
- buf.WriteByte(' ')
- writeType(buf, prev, qf, visited)
+ if tpar.bound != prev {
+ // bound changed - write previous one before advancing
+ w.byte(' ')
+ w.typ(prev)
}
- buf.WriteString(", ")
- }
- prev = b
-
- if t, _ := p.typ.(*_TypeParam); t != nil {
- writeType(buf, t, qf, visited)
- } else {
- buf.WriteString(p.name)
+ w.byte(',')
}
+ prev = tpar.bound
+ w.typ(tpar)
}
if prev != nil {
- buf.WriteByte(' ')
- writeType(buf, prev, qf, visited)
+ w.byte(' ')
+ w.typ(prev)
}
- buf.WriteByte(']')
+ w.byte(']')
}
-func writeTypeName(buf *bytes.Buffer, obj *TypeName, qf Qualifier) {
- s := "<Named w/o object>"
- if obj != nil {
- if obj.pkg != nil {
- writePackage(buf, obj.pkg, qf)
- }
- // TODO(gri): function-local named types should be displayed
- // differently from named types at package level to avoid
- // ambiguity.
- s = obj.name
+func (w *typeWriter) typeName(obj *TypeName) {
+ if obj.pkg != nil {
+ writePackage(w.buf, obj.pkg, w.qf)
}
- buf.WriteString(s)
+ w.string(obj.name)
}
-func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visited []Type) {
- buf.WriteByte('(')
+func (w *typeWriter) tuple(tup *Tuple, variadic bool) {
+ w.byte('(')
if tup != nil {
for i, v := range tup.vars {
if i > 0 {
- buf.WriteString(", ")
+ w.byte(',')
}
- if v.name != "" {
- buf.WriteString(v.name)
- buf.WriteByte(' ')
+ // parameter names are ignored for type identity and thus type hashes
+ if w.ctxt == nil && v.name != "" {
+ w.string(v.name)
+ w.byte(' ')
}
typ := v.typ
if variadic && i == len(tup.vars)-1 {
if s, ok := typ.(*Slice); ok {
- buf.WriteString("...")
+ w.string("...")
typ = s.elem
} else {
// special case:
// append(s, "foo"...) leads to signature func([]byte, string...)
- if t := asBasic(typ); t == nil || t.kind != String {
- panic("internal error: string type expected")
+ if t, _ := under(typ).(*Basic); t == nil || t.kind != String {
+ w.error("expected string type")
+ continue
}
- writeType(buf, typ, qf, visited)
- buf.WriteString("...")
+ w.typ(typ)
+ w.string("...")
continue
}
}
- writeType(buf, typ, qf, visited)
+ w.typ(typ)
}
}
- buf.WriteByte(')')
+ w.byte(')')
}
-// WriteSignature writes the representation of the signature sig to buf,
-// without a leading "func" keyword.
-// The Qualifier controls the printing of
-// package-level objects, and may be nil.
-func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
- writeSignature(buf, sig, qf, make([]Type, 0, 8))
-}
-
-func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) {
- if sig.tparams != nil {
- writeTParamList(buf, sig.tparams, qf, visited)
+func (w *typeWriter) signature(sig *Signature) {
+ if sig.TypeParams().Len() != 0 {
+ if w.ctxt != nil {
+ assert(w.tparams == nil)
+ w.tparams = sig.TypeParams()
+ defer func() {
+ w.tparams = nil
+ }()
+ }
+ w.tParamList(sig.TypeParams().list())
}
- writeTuple(buf, sig.params, sig.variadic, qf, visited)
+ w.tuple(sig.params, sig.variadic)
n := sig.results.Len()
if n == 0 {
@@ -415,15 +456,15 @@ func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []T
return
}
- buf.WriteByte(' ')
- if n == 1 && sig.results.vars[0].name == "" {
- // single unnamed result
- writeType(buf, sig.results.vars[0].typ, qf, visited)
+ w.byte(' ')
+ if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") {
+ // single unnamed result (if type hashing, name must be ignored)
+ w.typ(sig.results.vars[0].typ)
return
}
// multiple or named result(s)
- writeTuple(buf, sig.results, false, qf, visited)
+ w.tuple(sig.results, false)
}
// subscript returns the decimal (utf8) representation of x using subscript digits.
diff --git a/libgo/go/go/types/typestring_test.go b/libgo/go/go/types/typestring_test.go
index 07ed735..19e2a32 100644
--- a/libgo/go/go/types/typestring_test.go
+++ b/libgo/go/go/types/typestring_test.go
@@ -95,10 +95,13 @@ var independentTestTypes = []testEntry{
dup("interface{}"),
dup("interface{m()}"),
dup(`interface{String() string; m(int) float32}`),
-
- // TODO(rFindley) uncomment this once this AST is accepted, and add more test
- // cases.
- // dup(`interface{type int, float32, complex128}`),
+ dup("interface{int|float32|complex128}"),
+ dup("interface{int|~float32|~complex128}"),
+ dup("any"),
+ dup("interface{comparable}"),
+ // TODO(gri) adjust test for EvalCompositeTest
+ // {"comparable", "interface{comparable}"},
+ // {"error", "interface{Error() string}"},
// maps
dup("map[string]int"),
@@ -135,65 +138,18 @@ func TestTypeString(t *testing.T) {
t.Errorf("%s: %s", src, err)
continue
}
- typ := pkg.Scope().Lookup("T").Type().Underlying()
+ obj := pkg.Scope().Lookup("T")
+ if obj == nil {
+ t.Errorf("%s: T not found", test.src)
+ continue
+ }
+ typ := obj.Type().Underlying()
if got := typ.String(); got != test.str {
t.Errorf("%s: got %s, want %s", test.src, got, test.str)
}
}
}
-func TestIncompleteInterfaces(t *testing.T) {
- sig := NewSignature(nil, nil, nil, false)
- m := NewFunc(token.NoPos, nil, "m", sig)
- for _, test := range []struct {
- typ *Interface
- want string
- }{
- {new(Interface), "interface{/* incomplete */}"},
- {new(Interface).Complete(), "interface{}"},
-
- {NewInterface(nil, nil), "interface{}"},
- {NewInterface(nil, nil).Complete(), "interface{}"},
- {NewInterface([]*Func{}, nil), "interface{}"},
- {NewInterface([]*Func{}, nil).Complete(), "interface{}"},
- {NewInterface(nil, []*Named{}), "interface{}"},
- {NewInterface(nil, []*Named{}).Complete(), "interface{}"},
- {NewInterface([]*Func{m}, nil), "interface{m() /* incomplete */}"},
- {NewInterface([]*Func{m}, nil).Complete(), "interface{m()}"},
- {NewInterface(nil, []*Named{newDefined(new(Interface).Complete())}), "interface{T /* incomplete */}"},
- {NewInterface(nil, []*Named{newDefined(new(Interface).Complete())}).Complete(), "interface{T}"},
- {NewInterface(nil, []*Named{newDefined(NewInterface([]*Func{m}, nil))}), "interface{T /* incomplete */}"},
- {NewInterface(nil, []*Named{newDefined(NewInterface([]*Func{m}, nil).Complete())}), "interface{T /* incomplete */}"},
- {NewInterface(nil, []*Named{newDefined(NewInterface([]*Func{m}, nil).Complete())}).Complete(), "interface{T}"},
-
- {NewInterfaceType(nil, nil), "interface{}"},
- {NewInterfaceType(nil, nil).Complete(), "interface{}"},
- {NewInterfaceType([]*Func{}, nil), "interface{}"},
- {NewInterfaceType([]*Func{}, nil).Complete(), "interface{}"},
- {NewInterfaceType(nil, []Type{}), "interface{}"},
- {NewInterfaceType(nil, []Type{}).Complete(), "interface{}"},
- {NewInterfaceType([]*Func{m}, nil), "interface{m() /* incomplete */}"},
- {NewInterfaceType([]*Func{m}, nil).Complete(), "interface{m()}"},
- {NewInterfaceType(nil, []Type{new(Interface).Complete()}), "interface{interface{} /* incomplete */}"},
- {NewInterfaceType(nil, []Type{new(Interface).Complete()}).Complete(), "interface{interface{}}"},
- {NewInterfaceType(nil, []Type{NewInterfaceType([]*Func{m}, nil)}), "interface{interface{m() /* incomplete */} /* incomplete */}"},
- {NewInterfaceType(nil, []Type{NewInterfaceType([]*Func{m}, nil).Complete()}), "interface{interface{m()} /* incomplete */}"},
- {NewInterfaceType(nil, []Type{NewInterfaceType([]*Func{m}, nil).Complete()}).Complete(), "interface{interface{m()}}"},
- } {
- got := test.typ.String()
- if got != test.want {
- t.Errorf("got: %s, want: %s", got, test.want)
- }
- }
-}
-
-// newDefined creates a new defined type named T with the given underlying type.
-// Helper function for use with TestIncompleteInterfaces only.
-func newDefined(underlying Type) *Named {
- tname := NewTypeName(token.NoPos, nil, "T", nil)
- return NewNamed(tname, underlying, nil)
-}
-
func TestQualifiedTypeString(t *testing.T) {
t.Skip("skipping for gccgo--no importer")
p, _ := pkgFor("p.go", "package p; type T int", nil)
diff --git a/libgo/go/go/types/typeterm.go b/libgo/go/go/types/typeterm.go
new file mode 100644
index 0000000..6b67821
--- /dev/null
+++ b/libgo/go/go/types/typeterm.go
@@ -0,0 +1,166 @@
+// 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.
+
+package types
+
+// A term describes elementary type sets:
+//
+// ∅: (*term)(nil) == ∅ // set of no types (empty set)
+// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse)
+// T: &term{false, T} == {T} // set of type T
+// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t
+//
+type term struct {
+ tilde bool // valid if typ != nil
+ typ Type
+}
+
+func (x *term) String() string {
+ switch {
+ case x == nil:
+ return "∅"
+ case x.typ == nil:
+ return "𝓤"
+ case x.tilde:
+ return "~" + x.typ.String()
+ default:
+ return x.typ.String()
+ }
+}
+
+// equal reports whether x and y represent the same type set.
+func (x *term) equal(y *term) bool {
+ // easy cases
+ switch {
+ case x == nil || y == nil:
+ return x == y
+ case x.typ == nil || y.typ == nil:
+ return x.typ == y.typ
+ }
+ // ∅ ⊂ x, y ⊂ 𝓤
+
+ return x.tilde == y.tilde && Identical(x.typ, y.typ)
+}
+
+// union returns the union x ∪ y: zero, one, or two non-nil terms.
+func (x *term) union(y *term) (_, _ *term) {
+ // easy cases
+ switch {
+ case x == nil && y == nil:
+ return nil, nil // ∅ ∪ ∅ == ∅
+ case x == nil:
+ return y, nil // ∅ ∪ y == y
+ case y == nil:
+ return x, nil // x ∪ ∅ == x
+ case x.typ == nil:
+ return x, nil // 𝓤 ∪ y == 𝓤
+ case y.typ == nil:
+ return y, nil // x ∪ 𝓤 == 𝓤
+ }
+ // ∅ ⊂ x, y ⊂ 𝓤
+
+ if x.disjoint(y) {
+ return x, y // x ∪ y == (x, y) if x ∩ y == ∅
+ }
+ // x.typ == y.typ
+
+ // ~t ∪ ~t == ~t
+ // ~t ∪ T == ~t
+ // T ∪ ~t == ~t
+ // T ∪ T == T
+ if x.tilde || !y.tilde {
+ return x, nil
+ }
+ return y, nil
+}
+
+// intersect returns the intersection x ∩ y.
+func (x *term) intersect(y *term) *term {
+ // easy cases
+ switch {
+ case x == nil || y == nil:
+ return nil // ∅ ∩ y == ∅ and ∩ ∅ == ∅
+ case x.typ == nil:
+ return y // 𝓤 ∩ y == y
+ case y.typ == nil:
+ return x // x ∩ 𝓤 == x
+ }
+ // ∅ ⊂ x, y ⊂ 𝓤
+
+ if x.disjoint(y) {
+ return nil // x ∩ y == ∅ if x ∩ y == ∅
+ }
+ // x.typ == y.typ
+
+ // ~t ∩ ~t == ~t
+ // ~t ∩ T == T
+ // T ∩ ~t == T
+ // T ∩ T == T
+ if !x.tilde || y.tilde {
+ return x
+ }
+ return y
+}
+
+// includes reports whether t ∈ x.
+func (x *term) includes(t Type) bool {
+ // easy cases
+ switch {
+ case x == nil:
+ return false // t ∈ ∅ == false
+ case x.typ == nil:
+ return true // t ∈ 𝓤 == true
+ }
+ // ∅ ⊂ x ⊂ 𝓤
+
+ u := t
+ if x.tilde {
+ u = under(u)
+ }
+ return Identical(x.typ, u)
+}
+
+// subsetOf reports whether x ⊆ y.
+func (x *term) subsetOf(y *term) bool {
+ // easy cases
+ switch {
+ case x == nil:
+ return true // ∅ ⊆ y == true
+ case y == nil:
+ return false // x ⊆ ∅ == false since x != ∅
+ case y.typ == nil:
+ return true // x ⊆ 𝓤 == true
+ case x.typ == nil:
+ return false // 𝓤 ⊆ y == false since y != 𝓤
+ }
+ // ∅ ⊂ x, y ⊂ 𝓤
+
+ if x.disjoint(y) {
+ return false // x ⊆ y == false if x ∩ y == ∅
+ }
+ // x.typ == y.typ
+
+ // ~t ⊆ ~t == true
+ // ~t ⊆ T == false
+ // T ⊆ ~t == true
+ // T ⊆ T == true
+ return !x.tilde || y.tilde
+}
+
+// disjoint reports whether x ∩ y == ∅.
+// x.typ and y.typ must not be nil.
+func (x *term) disjoint(y *term) bool {
+ if debug && (x.typ == nil || y.typ == nil) {
+ panic("invalid argument(s)")
+ }
+ ux := x.typ
+ if y.tilde {
+ ux = under(ux)
+ }
+ uy := y.typ
+ if x.tilde {
+ uy = under(uy)
+ }
+ return !Identical(ux, uy)
+}
diff --git a/libgo/go/go/types/typeterm_test.go b/libgo/go/go/types/typeterm_test.go
new file mode 100644
index 0000000..24a1410
--- /dev/null
+++ b/libgo/go/go/types/typeterm_test.go
@@ -0,0 +1,240 @@
+// 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.
+
+package types
+
+import (
+ "go/token"
+ "strings"
+ "testing"
+)
+
+var myInt = func() Type {
+ tname := NewTypeName(token.NoPos, nil, "myInt", nil)
+ return NewNamed(tname, Typ[Int], nil)
+}()
+
+var testTerms = map[string]*term{
+ "∅": nil,
+ "𝓤": {},
+ "int": {false, Typ[Int]},
+ "~int": {true, Typ[Int]},
+ "string": {false, Typ[String]},
+ "~string": {true, Typ[String]},
+ "myInt": {false, myInt},
+}
+
+func TestTermString(t *testing.T) {
+ for want, x := range testTerms {
+ if got := x.String(); got != want {
+ t.Errorf("%v.String() == %v; want %v", x, got, want)
+ }
+ }
+}
+
+func split(s string, n int) []string {
+ r := strings.Split(s, " ")
+ if len(r) != n {
+ panic("invalid test case: " + s)
+ }
+ return r
+}
+
+func testTerm(name string) *term {
+ r, ok := testTerms[name]
+ if !ok {
+ panic("invalid test argument: " + name)
+ }
+ return r
+}
+
+func TestTermEqual(t *testing.T) {
+ for _, test := range []string{
+ "∅ ∅ T",
+ "𝓤 𝓤 T",
+ "int int T",
+ "~int ~int T",
+ "myInt myInt T",
+ "∅ 𝓤 F",
+ "∅ int F",
+ "∅ ~int F",
+ "𝓤 int F",
+ "𝓤 ~int F",
+ "𝓤 myInt F",
+ "int ~int F",
+ "int myInt F",
+ "~int myInt F",
+ } {
+ args := split(test, 3)
+ x := testTerm(args[0])
+ y := testTerm(args[1])
+ want := args[2] == "T"
+ if got := x.equal(y); got != want {
+ t.Errorf("%v.equal(%v) = %v; want %v", x, y, got, want)
+ }
+ // equal is symmetric
+ x, y = y, x
+ if got := x.equal(y); got != want {
+ t.Errorf("%v.equal(%v) = %v; want %v", x, y, got, want)
+ }
+ }
+}
+
+func TestTermUnion(t *testing.T) {
+ for _, test := range []string{
+ "∅ ∅ ∅ ∅",
+ "∅ 𝓤 𝓤 ∅",
+ "∅ int int ∅",
+ "∅ ~int ~int ∅",
+ "∅ myInt myInt ∅",
+ "𝓤 𝓤 𝓤 ∅",
+ "𝓤 int 𝓤 ∅",
+ "𝓤 ~int 𝓤 ∅",
+ "𝓤 myInt 𝓤 ∅",
+ "int int int ∅",
+ "int ~int ~int ∅",
+ "int string int string",
+ "int ~string int ~string",
+ "int myInt int myInt",
+ "~int ~string ~int ~string",
+ "~int myInt ~int ∅",
+
+ // union is symmetric, but the result order isn't - repeat symmetric cases explicitly
+ "𝓤 ∅ 𝓤 ∅",
+ "int ∅ int ∅",
+ "~int ∅ ~int ∅",
+ "myInt ∅ myInt ∅",
+ "int 𝓤 𝓤 ∅",
+ "~int 𝓤 𝓤 ∅",
+ "myInt 𝓤 𝓤 ∅",
+ "~int int ~int ∅",
+ "string int string int",
+ "~string int ~string int",
+ "myInt int myInt int",
+ "~string ~int ~string ~int",
+ "myInt ~int ~int ∅",
+ } {
+ args := split(test, 4)
+ x := testTerm(args[0])
+ y := testTerm(args[1])
+ want1 := testTerm(args[2])
+ want2 := testTerm(args[3])
+ if got1, got2 := x.union(y); !got1.equal(want1) || !got2.equal(want2) {
+ t.Errorf("%v.union(%v) = %v, %v; want %v, %v", x, y, got1, got2, want1, want2)
+ }
+ }
+}
+
+func TestTermIntersection(t *testing.T) {
+ for _, test := range []string{
+ "∅ ∅ ∅",
+ "∅ 𝓤 ∅",
+ "∅ int ∅",
+ "∅ ~int ∅",
+ "∅ myInt ∅",
+ "𝓤 𝓤 𝓤",
+ "𝓤 int int",
+ "𝓤 ~int ~int",
+ "𝓤 myInt myInt",
+ "int int int",
+ "int ~int int",
+ "int string ∅",
+ "int ~string ∅",
+ "int string ∅",
+ "~int ~string ∅",
+ "~int myInt myInt",
+ } {
+ args := split(test, 3)
+ x := testTerm(args[0])
+ y := testTerm(args[1])
+ want := testTerm(args[2])
+ if got := x.intersect(y); !got.equal(want) {
+ t.Errorf("%v.intersect(%v) = %v; want %v", x, y, got, want)
+ }
+ // intersect is symmetric
+ x, y = y, x
+ if got := x.intersect(y); !got.equal(want) {
+ t.Errorf("%v.intersect(%v) = %v; want %v", x, y, got, want)
+ }
+ }
+}
+
+func TestTermIncludes(t *testing.T) {
+ for _, test := range []string{
+ "∅ int F",
+ "𝓤 int T",
+ "int int T",
+ "~int int T",
+ "~int myInt T",
+ "string int F",
+ "~string int F",
+ "myInt int F",
+ } {
+ args := split(test, 3)
+ x := testTerm(args[0])
+ y := testTerm(args[1]).typ
+ want := args[2] == "T"
+ if got := x.includes(y); got != want {
+ t.Errorf("%v.includes(%v) = %v; want %v", x, y, got, want)
+ }
+ }
+}
+
+func TestTermSubsetOf(t *testing.T) {
+ for _, test := range []string{
+ "∅ ∅ T",
+ "𝓤 𝓤 T",
+ "int int T",
+ "~int ~int T",
+ "myInt myInt T",
+ "∅ 𝓤 T",
+ "∅ int T",
+ "∅ ~int T",
+ "∅ myInt T",
+ "𝓤 int F",
+ "𝓤 ~int F",
+ "𝓤 myInt F",
+ "int ~int T",
+ "int myInt F",
+ "~int myInt F",
+ "myInt int F",
+ "myInt ~int T",
+ } {
+ args := split(test, 3)
+ x := testTerm(args[0])
+ y := testTerm(args[1])
+ want := args[2] == "T"
+ if got := x.subsetOf(y); got != want {
+ t.Errorf("%v.subsetOf(%v) = %v; want %v", x, y, got, want)
+ }
+ }
+}
+
+func TestTermDisjoint(t *testing.T) {
+ for _, test := range []string{
+ "int int F",
+ "~int ~int F",
+ "int ~int F",
+ "int string T",
+ "int ~string T",
+ "int myInt T",
+ "~int ~string T",
+ "~int myInt F",
+ "string myInt T",
+ "~string myInt T",
+ } {
+ args := split(test, 3)
+ x := testTerm(args[0])
+ y := testTerm(args[1])
+ want := args[2] == "T"
+ if got := x.disjoint(y); got != want {
+ t.Errorf("%v.disjoint(%v) = %v; want %v", x, y, got, want)
+ }
+ // disjoint is symmetric
+ x, y = y, x
+ if got := x.disjoint(y); got != want {
+ t.Errorf("%v.disjoint(%v) = %v; want %v", x, y, got, want)
+ }
+ }
+}
diff --git a/libgo/go/go/types/typexpr.go b/libgo/go/go/types/typexpr.go
index 1738d86..00c250b 100644
--- a/libgo/go/go/types/typexpr.go
+++ b/libgo/go/go/types/typexpr.go
@@ -11,9 +11,6 @@ import (
"go/ast"
"go/constant"
"go/internal/typeparams"
- "go/token"
- "sort"
- "strconv"
"strings"
)
@@ -29,13 +26,27 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool)
// Note that we cannot use check.lookup here because the returned scope
// may be different from obj.Parent(). See also Scope.LookupParent doc.
scope, obj := check.scope.LookupParent(e.Name, check.pos)
- if obj == nil {
+ switch obj {
+ case nil:
if e.Name == "_" {
- check.errorf(e, _InvalidBlank, "cannot use _ as value or type")
+ // Blank identifiers are never declared, but the current identifier may
+ // be a placeholder for a receiver type parameter. In this case we can
+ // resolve its type and object from Checker.recvTParamMap.
+ if tpar := check.recvTParamMap[e]; tpar != nil {
+ x.mode = typexpr
+ x.typ = tpar
+ } else {
+ check.error(e, _InvalidBlank, "cannot use _ as value or type")
+ }
} else {
check.errorf(e, _UndeclaredName, "undeclared name: %s", e.Name)
}
return
+ case universeAny, universeComparable:
+ if !check.allowVersion(check.pkg, 1, 18) {
+ check.errorf(e, _UndeclaredName, "undeclared name: %s (requires version go1.18 or later)", e.Name)
+ return // avoid follow-on errors
+ }
}
check.recordUse(e, obj)
@@ -58,7 +69,7 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool)
// If so, mark the respective package as used.
// (This code is only needed for dot-imports. Without them,
// we only have to mark variables, see *Var case below).
- if pkgName := check.dotImportMap[dotImportKey{scope, obj}]; pkgName != nil {
+ if pkgName := check.dotImportMap[dotImportKey{scope, obj.Name()}]; pkgName != nil {
pkgName.used = true
}
@@ -85,6 +96,10 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool)
x.mode = constant_
case *TypeName:
+ if check.isBrokenAlias(obj) {
+ check.errorf(e, _InvalidDeclCycle, "invalid use of type alias %s in recursive type (see issue #50729)", obj.name)
+ return
+ }
x.mode = typexpr
case *Var:
@@ -125,41 +140,32 @@ func (check *Checker) typ(e ast.Expr) Type {
}
// varType type-checks the type expression e and returns its type, or Typ[Invalid].
-// The type must not be an (uninstantiated) generic type and it must be ordinary
-// (see ordinaryType).
+// The type must not be an (uninstantiated) generic type and it must not be a
+// constraint interface.
func (check *Checker) varType(e ast.Expr) Type {
typ := check.definedType(e, nil)
- check.ordinaryType(e, typ)
- return typ
-}
-// ordinaryType reports an error if typ is an interface type containing
-// type lists or is (or embeds) the predeclared type comparable.
-func (check *Checker) ordinaryType(pos positioner, typ Type) {
- // We don't want to call under() (via asInterface) or complete interfaces
- // while we are in the middle of type-checking parameter declarations that
- // might belong to interface methods. Delay this check to the end of
- // type-checking.
+ // If we have a type parameter there's nothing to do.
+ if isTypeParam(typ) {
+ return typ
+ }
+
+ // We don't want to call under() or complete interfaces while we are in
+ // the middle of type-checking parameter declarations that might belong
+ // to interface methods. Delay this check to the end of type-checking.
check.later(func() {
- if t := asInterface(typ); t != nil {
- check.completeInterface(pos.Pos(), t) // TODO(gri) is this the correct position?
- if t.allTypes != nil {
- check.softErrorf(pos, _Todo, "interface contains type constraints (%s)", t.allTypes)
- return
- }
- if t._IsComparable() {
- check.softErrorf(pos, _Todo, "interface is (or embeds) comparable")
+ if t, _ := under(typ).(*Interface); t != nil {
+ tset := computeInterfaceTypeSet(check, e.Pos(), t) // TODO(gri) is this the correct position?
+ if !tset.IsMethodSet() {
+ if tset.comparable {
+ check.softErrorf(e, _MisplacedConstraintIface, "interface is (or embeds) comparable")
+ } else {
+ check.softErrorf(e, _MisplacedConstraintIface, "interface contains type constraints")
+ }
}
}
})
-}
-// anyType type-checks the type expression e and returns its type, or Typ[Invalid].
-// The type may be generic or instantiated.
-func (check *Checker) anyType(e ast.Expr) Type {
- typ := check.typInternal(e, nil)
- assert(isTyped(typ))
- check.recordTypeAndValue(e, typexpr, typ, nil)
return typ
}
@@ -172,20 +178,22 @@ func (check *Checker) definedType(e ast.Expr, def *Named) Type {
typ := check.typInternal(e, def)
assert(isTyped(typ))
if isGeneric(typ) {
- check.errorf(e, _Todo, "cannot use generic type %s without instantiation", typ)
+ check.errorf(e, _WrongTypeArgCount, "cannot use generic type %s without instantiation", typ)
typ = Typ[Invalid]
}
check.recordTypeAndValue(e, typexpr, typ, nil)
return typ
}
-// genericType is like typ but the type must be an (uninstantiated) generic type.
-func (check *Checker) genericType(e ast.Expr, reportErr bool) Type {
+// genericType is like typ but the type must be an (uninstantiated) generic
+// type. If reason is non-nil and the type expression was a valid type but not
+// generic, reason will be populated with a message describing the error.
+func (check *Checker) genericType(e ast.Expr, reason *string) Type {
typ := check.typInternal(e, nil)
assert(isTyped(typ))
if typ != Typ[Invalid] && !isGeneric(typ) {
- if reportErr {
- check.errorf(e, _Todo, "%s is not a generic type", typ)
+ if reason != nil {
+ *reason = check.sprintf("%s is not a generic type", typ)
}
typ = Typ[Invalid]
}
@@ -194,205 +202,6 @@ func (check *Checker) genericType(e ast.Expr, reportErr bool) Type {
return typ
}
-// isubst returns an x with identifiers substituted per the substitution map smap.
-// isubst only handles the case of (valid) method receiver type expressions correctly.
-func isubst(x ast.Expr, smap map[*ast.Ident]*ast.Ident) ast.Expr {
- switch n := x.(type) {
- case *ast.Ident:
- if alt := smap[n]; alt != nil {
- return alt
- }
- case *ast.StarExpr:
- X := isubst(n.X, smap)
- if X != n.X {
- new := *n
- new.X = X
- return &new
- }
- case *ast.IndexExpr:
- elems := typeparams.UnpackExpr(n.Index)
- var newElems []ast.Expr
- for i, elem := range elems {
- new := isubst(elem, smap)
- if new != elem {
- if newElems == nil {
- newElems = make([]ast.Expr, len(elems))
- copy(newElems, elems)
- }
- newElems[i] = new
- }
- }
- if newElems != nil {
- index := typeparams.PackExpr(newElems)
- new := *n
- new.Index = index
- return &new
- }
- case *ast.ParenExpr:
- return isubst(n.X, smap) // no need to keep parentheses
- default:
- // Other receiver type expressions are invalid.
- // It's fine to ignore those here as they will
- // be checked elsewhere.
- }
- return x
-}
-
-// funcType type-checks a function or method type.
-func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast.FuncType) {
- check.openScope(ftyp, "function")
- check.scope.isFunc = true
- check.recordScope(ftyp, check.scope)
- sig.scope = check.scope
- defer check.closeScope()
-
- var recvTyp ast.Expr // rewritten receiver type; valid if != nil
- if recvPar != nil && len(recvPar.List) > 0 {
- // collect generic receiver type parameters, if any
- // - a receiver type parameter is like any other type parameter, except that it is declared implicitly
- // - the receiver specification acts as local declaration for its type parameters, which may be blank
- _, rname, rparams := check.unpackRecv(recvPar.List[0].Type, true)
- if len(rparams) > 0 {
- // Blank identifiers don't get declared and regular type-checking of the instantiated
- // parameterized receiver type expression fails in Checker.collectParams of receiver.
- // Identify blank type parameters and substitute each with a unique new identifier named
- // "n_" (where n is the parameter index) and which cannot conflict with any user-defined
- // name.
- var smap map[*ast.Ident]*ast.Ident // substitution map from "_" to "n_" identifiers
- for i, p := range rparams {
- if p.Name == "_" {
- new := *p
- new.Name = fmt.Sprintf("%d_", i)
- rparams[i] = &new // use n_ identifier instead of _ so it can be looked up
- if smap == nil {
- smap = make(map[*ast.Ident]*ast.Ident)
- }
- smap[p] = &new
- }
- }
- if smap != nil {
- // blank identifiers were found => use rewritten receiver type
- recvTyp = isubst(recvPar.List[0].Type, smap)
- }
- sig.rparams = check.declareTypeParams(nil, rparams)
- // determine receiver type to get its type parameters
- // and the respective type parameter bounds
- var recvTParams []*TypeName
- if rname != nil {
- // recv should be a Named type (otherwise an error is reported elsewhere)
- // Also: Don't report an error via genericType since it will be reported
- // again when we type-check the signature.
- // TODO(gri) maybe the receiver should be marked as invalid instead?
- if recv := asNamed(check.genericType(rname, false)); recv != nil {
- recvTParams = recv.tparams
- }
- }
- // provide type parameter bounds
- // - only do this if we have the right number (otherwise an error is reported elsewhere)
- if len(sig.rparams) == len(recvTParams) {
- // We have a list of *TypeNames but we need a list of Types.
- list := make([]Type, len(sig.rparams))
- for i, t := range sig.rparams {
- list[i] = t.typ
- }
- smap := makeSubstMap(recvTParams, list)
- for i, tname := range sig.rparams {
- bound := recvTParams[i].typ.(*_TypeParam).bound
- // 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(tname.pos, bound, smap)
- tname.typ.(*_TypeParam).bound = bound
- }
- }
- }
- }
- }
-
- if tparams := typeparams.Get(ftyp); tparams != nil {
- sig.tparams = check.collectTypeParams(tparams)
- // Always type-check method type parameters but complain that they are not allowed.
- // (A separate check is needed when type-checking interface method signatures because
- // they don't have a receiver specification.)
- if recvPar != nil {
- check.errorf(tparams, _Todo, "methods cannot have type parameters")
- }
- }
-
- // Value (non-type) parameters' scope starts in the function body. Use a temporary scope for their
- // declarations and then squash that scope into the parent scope (and report any redeclarations at
- // that time).
- scope := NewScope(check.scope, token.NoPos, token.NoPos, "function body (temp. scope)")
- recvList, _ := check.collectParams(scope, recvPar, recvTyp, false) // use rewritten receiver type, if any
- params, variadic := check.collectParams(scope, ftyp.Params, nil, true)
- results, _ := check.collectParams(scope, ftyp.Results, nil, false)
- scope.squash(func(obj, alt Object) {
- check.errorf(obj, _DuplicateDecl, "%s redeclared in this block", obj.Name())
- check.reportAltDecl(alt)
- })
-
- if recvPar != nil {
- // recv parameter list present (may be empty)
- // spec: "The receiver is specified via an extra parameter section preceding the
- // method name. That parameter section must declare a single parameter, the receiver."
- var recv *Var
- switch len(recvList) {
- case 0:
- // error reported by resolver
- recv = NewParam(0, nil, "", Typ[Invalid]) // ignore recv below
- default:
- // more than one receiver
- check.error(recvList[len(recvList)-1], _BadRecv, "method must have exactly one receiver")
- fallthrough // continue with first receiver
- case 1:
- recv = recvList[0]
- }
-
- // TODO(gri) We should delay rtyp expansion to when we actually need the
- // receiver; thus all checks here should be delayed to later.
- rtyp, _ := deref(recv.typ)
- rtyp = expand(rtyp)
-
- // spec: "The receiver type must be of the form T or *T where T is a type name."
- // (ignore invalid types - error was reported before)
- if t := rtyp; t != Typ[Invalid] {
- var err string
- if T := asNamed(t); T != nil {
- // spec: "The type denoted by T is called the receiver base type; it must not
- // be a pointer or interface type and it must be declared in the same package
- // as the method."
- if T.obj.pkg != check.pkg {
- err = "type not defined in this package"
- } else {
- switch u := optype(T).(type) {
- case *Basic:
- // unsafe.Pointer is treated like a regular pointer
- if u.kind == UnsafePointer {
- err = "unsafe.Pointer"
- }
- case *Pointer, *Interface:
- err = "pointer or interface type"
- }
- }
- } else {
- err = "basic or unnamed type"
- }
- if err != "" {
- check.errorf(recv, _InvalidRecv, "invalid receiver %s (%s)", recv.typ, err)
- // ok to continue
- }
- }
- sig.recv = recv
- }
-
- sig.params = NewTuple(params...)
- sig.results = NewTuple(results...)
- sig.variadic = variadic
-}
-
// goTypeName returns the Go type name for typ and
// removes any occurrences of "types." from that name.
func goTypeName(typ Type) string {
@@ -404,7 +213,7 @@ func goTypeName(typ Type) string {
//
func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
if trace {
- check.trace(e0.Pos(), "type %s", e0)
+ check.trace(e0.Pos(), "-- type %s", e0)
check.indent++
defer func() {
check.indent--
@@ -412,9 +221,7 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
if T != nil {
// Calling under() here may lead to endless instantiations.
// Test case: type T[P any] *T[P]
- // TODO(gri) investigate if that's a bug or to be expected
- // (see also analogous comment in Checker.instantiate).
- under = T.Underlying()
+ under = safeUnderlying(T)
}
if T == under {
check.trace(e0.Pos(), "=> %s // %s", T, goTypeName(T))
@@ -462,13 +269,12 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
check.errorf(&x, _NotAType, "%s is not a type", &x)
}
- case *ast.IndexExpr:
- if typeparams.Enabled {
- exprs := typeparams.UnpackExpr(e.Index)
- return check.instantiatedType(e.X, exprs, def)
+ case *ast.IndexExpr, *ast.IndexListExpr:
+ ix := typeparams.UnpackIndexExpr(e)
+ if !check.allowVersion(check.pkg, 1, 18) {
+ check.softErrorf(inNode(e, ix.Lbrack), _UnsupportedFeature, "type instantiation requires go1.18 or later")
}
- check.errorf(e0, _NotAType, "%s is not a type", e0)
- check.use(e.X)
+ return check.instantiatedType(ix, def)
case *ast.ParenExpr:
// Generic types must be instantiated before they can be used in any form.
@@ -476,18 +282,20 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
return check.definedType(e.X, def)
case *ast.ArrayType:
- if e.Len != nil {
- typ := new(Array)
+ if e.Len == nil {
+ typ := new(Slice)
def.setUnderlying(typ)
- typ.len = check.arrayLength(e.Len)
typ.elem = check.varType(e.Elt)
return typ
}
- typ := new(Slice)
+ typ := new(Array)
def.setUnderlying(typ)
+ typ.len = check.arrayLength(e.Len)
typ.elem = check.varType(e.Elt)
- return typ
+ if typ.len >= 0 {
+ return typ
+ }
case *ast.Ellipsis:
// dots are handled explicitly where they are legal
@@ -503,6 +311,7 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
case *ast.StarExpr:
typ := new(Pointer)
+ typ.base = Typ[Invalid] // avoid nil base in invalid recursive type declaration
def.setUnderlying(typ)
typ.base = check.varType(e.X)
return typ
@@ -538,7 +347,7 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
check.later(func() {
if !Comparable(typ.key) {
var why string
- if asTypeParam(typ.key) != nil {
+ if isTypeParam(typ.key) {
why = " (missing comparable constraint)"
}
check.errorf(e.Key, _IncomparableMapKey, "incomparable map key type %s%s", typ.key, why)
@@ -577,80 +386,115 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
return typ
}
-// typeOrNil type-checks the type expression (or nil value) e
-// and returns the type of e, or nil. If e is a type, it must
-// not be an (uninstantiated) generic type.
-// If e is neither a type nor nil, typeOrNil returns Typ[Invalid].
-// TODO(gri) should we also disallow non-var types?
-func (check *Checker) typeOrNil(e ast.Expr) Type {
- var x operand
- check.rawExpr(&x, e, nil)
- switch x.mode {
- case invalid:
- // ignore - error reported before
- case novalue:
- check.errorf(&x, _NotAType, "%s used as type", &x)
- case typexpr:
- check.instantiatedOperand(&x)
- return x.typ
- case value:
- if x.isNil() {
- return nil
- }
- fallthrough
- default:
- check.errorf(&x, _NotAType, "%s is not a type", &x)
+func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (res Type) {
+ pos := ix.X.Pos()
+ if trace {
+ check.trace(pos, "-- instantiating %s with %s", ix.X, ix.Indices)
+ check.indent++
+ defer func() {
+ check.indent--
+ // Don't format the underlying here. It will always be nil.
+ check.trace(pos, "=> %s", res)
+ }()
}
- return Typ[Invalid]
-}
-func (check *Checker) instantiatedType(x ast.Expr, targs []ast.Expr, def *Named) Type {
- b := check.genericType(x, true) // TODO(gri) what about cycles?
- if b == Typ[Invalid] {
- return b // error already reported
+ var reason string
+ gtyp := check.genericType(ix.X, &reason)
+ if reason != "" {
+ check.invalidOp(ix.Orig, _NotAGenericType, "%s (%s)", ix.Orig, reason)
}
- base := asNamed(b)
- if base == nil {
- unreachable() // should have been caught by genericType
+ if gtyp == Typ[Invalid] {
+ return gtyp // error already reported
}
- // create a new type instance rather than instantiate the type
- // TODO(gri) should do argument number check here rather than
- // when instantiating the type?
- typ := new(instance)
- def.setUnderlying(typ)
-
- typ.check = check
- typ.pos = x.Pos()
- typ.base = base
+ orig, _ := gtyp.(*Named)
+ if orig == nil {
+ panic(fmt.Sprintf("%v: cannot instantiate %v", ix.Pos(), gtyp))
+ }
- // evaluate arguments (always)
- typ.targs = check.typeList(targs)
- if typ.targs == nil {
+ // evaluate arguments
+ targs := check.typeList(ix.Indices)
+ if targs == nil {
def.setUnderlying(Typ[Invalid]) // avoid later errors due to lazy instantiation
return Typ[Invalid]
}
- // determine argument positions (for error reporting)
- typ.poslist = make([]token.Pos, len(targs))
- for i, arg := range targs {
- typ.poslist[i] = arg.Pos()
+ // create the instance
+ ctxt := check.bestContext(nil)
+ h := ctxt.instanceHash(orig, targs)
+ // targs may be incomplete, and require inference. In any case we should de-duplicate.
+ inst, _ := ctxt.lookup(h, orig, targs).(*Named)
+ // If inst is non-nil, we can't just return here. Inst may have been
+ // constructed via recursive substitution, in which case we wouldn't do the
+ // validation below. Ensure that the validation (and resulting errors) runs
+ // for each instantiated type in the source.
+ if inst == nil {
+ // x may be a selector for an imported type; use its start pos rather than x.Pos().
+ tname := NewTypeName(ix.Pos(), orig.obj.pkg, orig.obj.name, nil)
+ inst = check.newNamed(tname, orig, nil, nil, nil) // underlying, methods and tparams are set when named is resolved
+ inst.targs = newTypeList(targs)
+ inst = ctxt.update(h, orig, targs, inst).(*Named)
+ }
+ def.setUnderlying(inst)
+
+ inst.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, *methodList) {
+ tparams := orig.TypeParams().list()
+
+ inferred := targs
+ if len(targs) < len(tparams) {
+ // If inference fails, len(inferred) will be 0, and inst.underlying will
+ // be set to Typ[Invalid] in expandNamed.
+ inferred = check.infer(ix.Orig, tparams, targs, nil, nil)
+ if len(inferred) > len(targs) {
+ inst.targs = newTypeList(inferred)
+ }
+ }
+
+ check.recordInstance(ix.Orig, inferred, inst)
+ return expandNamed(ctxt, n, pos)
}
- // make sure we check instantiation works at least once
- // and that the resulting type is valid
+ // orig.tparams may not be set up, so we need to do expansion later.
check.later(func() {
- t := typ.expand()
- check.validType(t, nil)
+ // This is an instance from the source, not from recursive substitution,
+ // and so it must be resolved during type-checking so that we can report
+ // errors.
+ inst.resolve(ctxt)
+ // Since check is non-nil, we can still mutate inst. Unpinning the resolver
+ // frees some memory.
+ inst.resolver = nil
+
+ if check.validateTArgLen(pos, inst.tparams.Len(), inst.targs.Len()) {
+ if i, err := check.verify(pos, inst.tparams.list(), inst.targs.list()); err != nil {
+ // best position for error reporting
+ pos := ix.Pos()
+ if i < len(ix.Indices) {
+ pos = ix.Indices[i].Pos()
+ }
+ check.softErrorf(atPos(pos), _InvalidTypeArg, err.Error())
+ } else {
+ check.mono.recordInstance(check.pkg, pos, inst.tparams.list(), inst.targs.list(), ix.Indices)
+ }
+ }
+
+ check.validType(inst)
})
- return typ
+ return inst
}
// arrayLength type-checks the array length expression e
// and returns the constant length >= 0, or a value < 0
// to indicate an error (and thus an unknown length).
func (check *Checker) arrayLength(e ast.Expr) int64 {
+ // If e is an undeclared identifier, the array declaration might be an
+ // attempt at a parameterized type declaration with missing constraint.
+ // Provide a better error message than just "undeclared name: X".
+ if name, _ := e.(*ast.Ident); name != nil && check.lookup(name.Name) == nil {
+ check.errorf(name, _InvalidArrayLen, "undeclared name %s for array length", name.Name)
+ return -1
+ }
+
var x operand
check.expr(&x, e)
if x.mode != constant_ {
@@ -659,6 +503,7 @@ func (check *Checker) arrayLength(e ast.Expr) int64 {
}
return -1
}
+
if isUntyped(x.typ) || isInteger(x.typ) {
if val := constant.ToInt(x.val); val.Kind() == constant.Int {
if representableConst(val, check, Typ[Int], nil) {
@@ -670,6 +515,7 @@ func (check *Checker) arrayLength(e ast.Expr) int64 {
}
}
}
+
check.errorf(&x, _InvalidArrayLen, "array length %s must be integer", &x)
return -1
}
@@ -689,518 +535,3 @@ func (check *Checker) typeList(list []ast.Expr) []Type {
}
return res
}
-
-// collectParams declares the parameters of list in scope and returns the corresponding
-// variable list. If type0 != nil, it is used instead of the first type in list.
-func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, type0 ast.Expr, variadicOk bool) (params []*Var, variadic bool) {
- if list == nil {
- return
- }
-
- var named, anonymous bool
- for i, field := range list.List {
- ftype := field.Type
- if i == 0 && type0 != nil {
- ftype = type0
- }
- if t, _ := ftype.(*ast.Ellipsis); t != nil {
- ftype = t.Elt
- if variadicOk && i == len(list.List)-1 && len(field.Names) <= 1 {
- variadic = true
- } else {
- check.softErrorf(t, _MisplacedDotDotDot, "can only use ... with final parameter in list")
- // ignore ... and continue
- }
- }
- typ := check.varType(ftype)
- // The parser ensures that f.Tag is nil and we don't
- // care if a constructed AST contains a non-nil tag.
- if len(field.Names) > 0 {
- // named parameter
- for _, name := range field.Names {
- if name.Name == "" {
- check.invalidAST(name, "anonymous parameter")
- // ok to continue
- }
- par := NewParam(name.Pos(), check.pkg, name.Name, typ)
- check.declare(scope, name, par, scope.pos)
- params = append(params, par)
- }
- named = true
- } else {
- // anonymous parameter
- par := NewParam(ftype.Pos(), check.pkg, "", typ)
- check.recordImplicit(field, par)
- params = append(params, par)
- anonymous = true
- }
- }
-
- if named && anonymous {
- check.invalidAST(list, "list contains both named and anonymous parameters")
- // ok to continue
- }
-
- // For a variadic function, change the last parameter's type from T to []T.
- // Since we type-checked T rather than ...T, we also need to retro-actively
- // record the type for ...T.
- if variadic {
- last := params[len(params)-1]
- last.typ = &Slice{elem: last.typ}
- check.recordTypeAndValue(list.List[len(list.List)-1].Type, typexpr, last.typ, nil)
- }
-
- return
-}
-
-func (check *Checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool {
- if alt := oset.insert(obj); alt != nil {
- check.errorf(atPos(pos), _DuplicateDecl, "%s redeclared", obj.Name())
- check.reportAltDecl(alt)
- return false
- }
- return true
-}
-
-func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, def *Named) {
- var tlist *ast.Ident // "type" name of first entry in a type list declaration
- var types []ast.Expr
- for _, f := range iface.Methods.List {
- if len(f.Names) > 0 {
- // We have a method with name f.Names[0], or a type
- // of a type list (name.Name == "type").
- // (The parser ensures that there's only one method
- // and we don't care if a constructed AST has more.)
- name := f.Names[0]
- if name.Name == "_" {
- check.errorf(name, _BlankIfaceMethod, "invalid method name _")
- continue // ignore
- }
-
- if name.Name == "type" {
- // Always collect all type list entries, even from
- // different type lists, under the assumption that
- // the author intended to include all types.
- types = append(types, f.Type)
- if tlist != nil && tlist != name {
- check.errorf(name, _Todo, "cannot have multiple type lists in an interface")
- }
- tlist = name
- continue
- }
-
- typ := check.typ(f.Type)
- sig, _ := typ.(*Signature)
- if sig == nil {
- if typ != Typ[Invalid] {
- check.invalidAST(f.Type, "%s is not a method signature", typ)
- }
- continue // ignore
- }
-
- // Always type-check method type parameters but complain if they are not enabled.
- // (This extra check is needed here because interface method signatures don't have
- // a receiver specification.)
- if sig.tparams != nil {
- var at positioner = f.Type
- if tparams := typeparams.Get(f.Type); tparams != nil {
- at = tparams
- }
- check.errorf(at, _Todo, "methods cannot have type parameters")
- }
-
- // use named receiver type if available (for better error messages)
- var recvTyp Type = ityp
- if def != nil {
- recvTyp = def
- }
- sig.recv = NewVar(name.Pos(), check.pkg, "", recvTyp)
-
- m := NewFunc(name.Pos(), check.pkg, name.Name, sig)
- check.recordDef(name, m)
- ityp.methods = append(ityp.methods, m)
- } else {
- // We have an embedded type. completeInterface will
- // eventually verify that we have an interface.
- ityp.embeddeds = append(ityp.embeddeds, check.typ(f.Type))
- check.posMap[ityp] = append(check.posMap[ityp], f.Type.Pos())
- }
- }
-
- // type constraints
- ityp.types = _NewSum(check.collectTypeConstraints(iface.Pos(), types))
-
- if len(ityp.methods) == 0 && ityp.types == nil && len(ityp.embeddeds) == 0 {
- // empty interface
- ityp.allMethods = markComplete
- return
- }
-
- // sort for API stability
- sortMethods(ityp.methods)
- sortTypes(ityp.embeddeds)
-
- check.later(func() { check.completeInterface(iface.Pos(), ityp) })
-}
-
-func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) {
- if ityp.allMethods != nil {
- return
- }
-
- // completeInterface may be called via the LookupFieldOrMethod,
- // MissingMethod, Identical, or IdenticalIgnoreTags external API
- // in which case check will be nil. In this case, type-checking
- // must be finished and all interfaces should have been completed.
- if check == nil {
- panic("internal error: incomplete interface")
- }
-
- if trace {
- // Types don't generally have position information.
- // If we don't have a valid pos provided, try to use
- // one close enough.
- if !pos.IsValid() && len(ityp.methods) > 0 {
- pos = ityp.methods[0].pos
- }
-
- check.trace(pos, "complete %s", ityp)
- check.indent++
- defer func() {
- check.indent--
- check.trace(pos, "=> %s (methods = %v, types = %v)", ityp, ityp.allMethods, ityp.allTypes)
- }()
- }
-
- // An infinitely expanding interface (due to a cycle) is detected
- // elsewhere (Checker.validType), so here we simply assume we only
- // have valid interfaces. Mark the interface as complete to avoid
- // infinite recursion if the validType check occurs later for some
- // reason.
- ityp.allMethods = markComplete
-
- // Methods of embedded interfaces are collected unchanged; i.e., the identity
- // of a method I.m's Func Object of an interface I is the same as that of
- // the method m in an interface that embeds interface I. On the other hand,
- // if a method is embedded via multiple overlapping embedded interfaces, we
- // don't provide a guarantee which "original m" got chosen for the embedding
- // interface. See also issue #34421.
- //
- // If we don't care to provide this identity guarantee anymore, instead of
- // reusing the original method in embeddings, we can clone the method's Func
- // Object and give it the position of a corresponding embedded interface. Then
- // we can get rid of the mpos map below and simply use the cloned method's
- // position.
-
- var seen objset
- var methods []*Func
- mpos := make(map[*Func]token.Pos) // method specification or method embedding position, for good error messages
- addMethod := func(pos token.Pos, m *Func, explicit bool) {
- switch other := seen.insert(m); {
- case other == nil:
- methods = append(methods, m)
- mpos[m] = pos
- case explicit:
- check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name)
- check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
- default:
- // We have a duplicate method name in an embedded (not explicitly declared) method.
- // Check method signatures after all types are computed (issue #33656).
- // If we're pre-go1.14 (overlapping embeddings are not permitted), report that
- // error here as well (even though we could do it eagerly) because it's the same
- // error message.
- check.later(func() {
- if !check.allowVersion(m.pkg, 1, 14) || !check.identical(m.typ, other.Type()) {
- check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name)
- check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
- }
- })
- }
- }
-
- for _, m := range ityp.methods {
- addMethod(m.pos, m, true)
- }
-
- // collect types
- allTypes := ityp.types
-
- posList := check.posMap[ityp]
- for i, typ := range ityp.embeddeds {
- pos := posList[i] // embedding position
- utyp := under(typ)
- etyp := asInterface(utyp)
- if etyp == nil {
- if utyp != Typ[Invalid] {
- var format string
- if _, ok := utyp.(*_TypeParam); ok {
- format = "%s is a type parameter, not an interface"
- } else {
- format = "%s is not an interface"
- }
- // TODO: correct error code.
- check.errorf(atPos(pos), _InvalidIfaceEmbed, format, typ)
- }
- continue
- }
- check.completeInterface(pos, etyp)
- for _, m := range etyp.allMethods {
- addMethod(pos, m, false) // use embedding position pos rather than m.pos
- }
- allTypes = intersect(allTypes, etyp.allTypes)
- }
-
- if methods != nil {
- sort.Sort(byUniqueMethodName(methods))
- ityp.allMethods = methods
- }
- ityp.allTypes = allTypes
-}
-
-// intersect computes the intersection of the types x and y.
-// Note: A incomming nil type stands for the top type. A top
-// type result is returned as nil.
-func intersect(x, y Type) (r Type) {
- defer func() {
- if r == theTop {
- r = nil
- }
- }()
-
- switch {
- case x == theBottom || y == theBottom:
- return theBottom
- case x == nil || x == theTop:
- return y
- case y == nil || x == theTop:
- return x
- }
-
- xtypes := unpackType(x)
- ytypes := unpackType(y)
- // Compute the list rtypes which includes only
- // types that are in both xtypes and ytypes.
- // Quadratic algorithm, but good enough for now.
- // TODO(gri) fix this
- var rtypes []Type
- for _, x := range xtypes {
- if includes(ytypes, x) {
- rtypes = append(rtypes, x)
- }
- }
-
- if rtypes == nil {
- return theBottom
- }
- return _NewSum(rtypes)
-}
-
-func sortTypes(list []Type) {
- sort.Stable(byUniqueTypeName(list))
-}
-
-// byUniqueTypeName named type lists can be sorted by their unique type names.
-type byUniqueTypeName []Type
-
-func (a byUniqueTypeName) Len() int { return len(a) }
-func (a byUniqueTypeName) Less(i, j int) bool { return sortName(a[i]) < sortName(a[j]) }
-func (a byUniqueTypeName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-
-func sortName(t Type) string {
- if named := asNamed(t); named != nil {
- return named.obj.Id()
- }
- return ""
-}
-
-func sortMethods(list []*Func) {
- sort.Sort(byUniqueMethodName(list))
-}
-
-func assertSortedMethods(list []*Func) {
- if !debug {
- panic("internal error: assertSortedMethods called outside debug mode")
- }
- if !sort.IsSorted(byUniqueMethodName(list)) {
- panic("internal error: methods not sorted")
- }
-}
-
-// byUniqueMethodName method lists can be sorted by their unique method names.
-type byUniqueMethodName []*Func
-
-func (a byUniqueMethodName) Len() int { return len(a) }
-func (a byUniqueMethodName) Less(i, j int) bool { return a[i].Id() < a[j].Id() }
-func (a byUniqueMethodName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-
-func (check *Checker) tag(t *ast.BasicLit) string {
- if t != nil {
- if t.Kind == token.STRING {
- if val, err := strconv.Unquote(t.Value); err == nil {
- return val
- }
- }
- check.invalidAST(t, "incorrect tag syntax: %q", t.Value)
- }
- return ""
-}
-
-func (check *Checker) structType(styp *Struct, e *ast.StructType) {
- list := e.Fields
- if list == nil {
- return
- }
-
- // struct fields and tags
- var fields []*Var
- var tags []string
-
- // for double-declaration checks
- var fset objset
-
- // current field typ and tag
- var typ Type
- var tag string
- add := func(ident *ast.Ident, embedded bool, pos token.Pos) {
- if tag != "" && tags == nil {
- tags = make([]string, len(fields))
- }
- if tags != nil {
- tags = append(tags, tag)
- }
-
- name := ident.Name
- fld := NewField(pos, check.pkg, name, typ, embedded)
- // spec: "Within a struct, non-blank field names must be unique."
- if name == "_" || check.declareInSet(&fset, pos, fld) {
- fields = append(fields, fld)
- check.recordDef(ident, fld)
- }
- }
-
- // addInvalid adds an embedded field of invalid type to the struct for
- // fields with errors; this keeps the number of struct fields in sync
- // with the source as long as the fields are _ or have different names
- // (issue #25627).
- addInvalid := func(ident *ast.Ident, pos token.Pos) {
- typ = Typ[Invalid]
- tag = ""
- add(ident, true, pos)
- }
-
- for _, f := range list.List {
- typ = check.varType(f.Type)
- tag = check.tag(f.Tag)
- if len(f.Names) > 0 {
- // named fields
- for _, name := range f.Names {
- add(name, false, name.Pos())
- }
- } else {
- // embedded field
- // spec: "An embedded type must be specified as a type name T or as a
- // pointer to a non-interface type name *T, and T itself may not be a
- // pointer type."
- pos := f.Type.Pos()
- name := embeddedFieldIdent(f.Type)
- if name == nil {
- // TODO(rFindley): using invalidAST here causes test failures (all
- // errors should have codes). Clean this up.
- check.errorf(f.Type, _Todo, "invalid AST: embedded field type %s has no name", f.Type)
- name = ast.NewIdent("_")
- name.NamePos = pos
- addInvalid(name, pos)
- continue
- }
- add(name, true, pos)
-
- // Because we have a name, typ must be of the form T or *T, where T is the name
- // of a (named or alias) type, and t (= deref(typ)) must be the type of T.
- // We must delay this check to the end because we don't want to instantiate
- // (via under(t)) a possibly incomplete type.
-
- // for use in the closure below
- embeddedTyp := typ
- embeddedPos := f.Type
-
- check.later(func() {
- t, isPtr := deref(embeddedTyp)
- switch t := optype(t).(type) {
- case *Basic:
- if t == Typ[Invalid] {
- // error was reported before
- return
- }
- // unsafe.Pointer is treated like a regular pointer
- if t.kind == UnsafePointer {
- check.errorf(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be unsafe.Pointer")
- }
- case *Pointer:
- check.errorf(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be a pointer")
- case *Interface:
- if isPtr {
- check.errorf(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be a pointer to an interface")
- }
- }
- })
- }
- }
-
- styp.fields = fields
- styp.tags = tags
-}
-
-func embeddedFieldIdent(e ast.Expr) *ast.Ident {
- switch e := e.(type) {
- case *ast.Ident:
- return e
- case *ast.StarExpr:
- // *T is valid, but **T is not
- if _, ok := e.X.(*ast.StarExpr); !ok {
- return embeddedFieldIdent(e.X)
- }
- case *ast.SelectorExpr:
- return e.Sel
- case *ast.IndexExpr:
- return embeddedFieldIdent(e.X)
- }
- return nil // invalid embedded field
-}
-
-func (check *Checker) collectTypeConstraints(pos token.Pos, types []ast.Expr) []Type {
- list := make([]Type, 0, len(types)) // assume all types are correct
- for _, texpr := range types {
- if texpr == nil {
- check.invalidAST(atPos(pos), "missing type constraint")
- continue
- }
- list = append(list, check.varType(texpr))
- }
-
- // Ensure that each type is only present once in the type list. Types may be
- // interfaces, which may not be complete yet. It's ok to do this check at the
- // end because it's not a requirement for correctness of the code.
- // Note: This is a quadratic algorithm, but type lists tend to be short.
- check.later(func() {
- for i, t := range list {
- if t := asInterface(t); t != nil {
- check.completeInterface(types[i].Pos(), t)
- }
- if includes(list[:i], t) {
- check.softErrorf(types[i], _Todo, "duplicate type %s in type list", t)
- }
- }
- })
-
- return list
-}
-
-// includes reports whether typ is in list.
-func includes(list []Type, typ Type) bool {
- for _, e := range list {
- if Identical(typ, e) {
- return true
- }
- }
- return false
-}
diff --git a/libgo/go/go/types/unify.go b/libgo/go/go/types/unify.go
index db06e21..ad6d316 100644
--- a/libgo/go/go/types/unify.go
+++ b/libgo/go/go/types/unify.go
@@ -8,8 +8,7 @@ package types
import (
"bytes"
- "go/token"
- "sort"
+ "fmt"
)
// The unifier maintains two separate sets of type parameters x and y
@@ -34,14 +33,18 @@ import (
// by setting up one of them (using init) and then assigning its value
// to the other.
+// Upper limit for recursion depth. Used to catch infinite recursions
+// due to implementation issues (e.g., see issues #48619, #48656).
+const unificationDepthLimit = 50
+
// A unifier maintains the current type parameters for x and y
// and the respective types inferred for each type parameter.
// A unifier is created by calling newUnifier.
type unifier struct {
- check *Checker
exact bool
x, y tparamsList // x and y must initialized via tparamsList.init
types []Type // inferred types, shared by x and y
+ depth int // recursion depth during unification
}
// newUnifier returns a new unifier.
@@ -49,8 +52,8 @@ type unifier struct {
// exactly. If exact is not set, a named type's underlying type
// is considered if unification would fail otherwise, and the
// direction of channels is ignored.
-func newUnifier(check *Checker, exact bool) *unifier {
- u := &unifier{check: check, exact: exact}
+func newUnifier(exact bool) *unifier {
+ u := &unifier{exact: exact}
u.x.unifier = u
u.y.unifier = u
return u
@@ -64,7 +67,7 @@ func (u *unifier) unify(x, y Type) bool {
// A tparamsList describes a list of type parameters and the types inferred for them.
type tparamsList struct {
unifier *unifier
- tparams []*TypeName
+ tparams []*TypeParam
// For each tparams element, there is a corresponding type slot index in indices.
// index < 0: unifier.types[-index-1] == nil
// index == 0: no type slot allocated yet
@@ -78,29 +81,30 @@ type tparamsList struct {
// String returns a string representation for a tparamsList. For debugging.
func (d *tparamsList) String() string {
var buf bytes.Buffer
- buf.WriteByte('[')
- for i, tname := range d.tparams {
+ w := newTypeWriter(&buf, nil)
+ w.byte('[')
+ for i, tpar := range d.tparams {
if i > 0 {
- buf.WriteString(", ")
+ w.string(", ")
}
- writeType(&buf, tname.typ, nil, nil)
- buf.WriteString(": ")
- writeType(&buf, d.at(i), nil, nil)
+ w.typ(tpar)
+ w.string(": ")
+ w.typ(d.at(i))
}
- buf.WriteByte(']')
+ w.byte(']')
return buf.String()
}
// init initializes d with the given type parameters.
// The type parameters must be in the order in which they appear in their declaration
// (this ensures that the tparams indices match the respective type parameter index).
-func (d *tparamsList) init(tparams []*TypeName) {
+func (d *tparamsList) init(tparams []*TypeParam) {
if len(tparams) == 0 {
return
}
if debug {
for i, tpar := range tparams {
- assert(i == tpar.typ.(*_TypeParam).index)
+ assert(i == tpar.index)
}
}
d.tparams = tparams
@@ -109,7 +113,7 @@ func (d *tparamsList) init(tparams []*TypeName) {
// join unifies the i'th type parameter of x with the j'th type parameter of y.
// If both type parameters already have a type associated with them and they are
-// not joined, join fails and return false.
+// not joined, join fails and returns false.
func (u *unifier) join(i, j int) bool {
ti := u.x.indices[i]
tj := u.y.indices[j]
@@ -133,13 +137,18 @@ func (u *unifier) join(i, j int) bool {
break
case ti > 0 && tj > 0:
// Both type parameters have (possibly different) inferred types. Cannot join.
+ // TODO(gri) Should we check if types are identical? Investigate.
return false
case ti > 0:
// Only the type parameter for x has an inferred type. Use x slot for y.
u.y.setIndex(j, ti)
+ // This case is handled like the default case.
+ // case tj > 0:
+ // // Only the type parameter for y has an inferred type. Use y slot for x.
+ // u.x.setIndex(i, tj)
default:
- // Either the type parameter for y has an inferred type, or neither type
- // parameter has an inferred type. In either case, use y slot for x.
+ // Neither type parameter has an inferred type. Use y slot for x
+ // (or x slot for y, it doesn't matter).
u.x.setIndex(i, tj)
}
return true
@@ -148,10 +157,23 @@ func (u *unifier) join(i, j int) bool {
// If typ is a type parameter of d, index returns the type parameter index.
// Otherwise, the result is < 0.
func (d *tparamsList) index(typ Type) int {
- if t, ok := typ.(*_TypeParam); ok {
- if i := t.index; i < len(d.tparams) && d.tparams[i].typ == t {
- return i
- }
+ if tpar, ok := typ.(*TypeParam); ok {
+ return tparamIndex(d.tparams, tpar)
+ }
+ return -1
+}
+
+// If tpar is a type parameter in list, tparamIndex returns the type parameter index.
+// Otherwise, the result is < 0. tpar must not be nil.j
+func tparamIndex(list []*TypeParam, tpar *TypeParam) int {
+ // Once a type parameter is bound its index is >= 0. However, there are some
+ // code paths (namely tracing and type hashing) by which it is possible to
+ // arrive here with a type parameter that has not been bound, hence the check
+ // for 0 <= i below.
+ // TODO(rfindley): investigate a better approach for guarding against using
+ // unbound type parameters.
+ if i := tpar.index; 0 <= i && i < len(list) && list[i] == tpar {
+ return i
}
return -1
}
@@ -216,26 +238,32 @@ func (u *unifier) nifyEq(x, y Type, p *ifacePair) bool {
}
// nify implements the core unification algorithm which is an
-// adapted version of Checker.identical0. For changes to that
+// adapted version of Checker.identical. For changes to that
// code the corresponding changes should be made here.
// Must not be called directly from outside the unifier.
func (u *unifier) nify(x, y Type, p *ifacePair) bool {
- // types must be expanded for comparison
- x = expand(x)
- y = expand(y)
+ // Stop gap for cases where unification fails.
+ if u.depth >= unificationDepthLimit {
+ if debug {
+ panic("unification reached recursion depth limit")
+ }
+ return false
+ }
+ u.depth++
+ defer func() {
+ u.depth--
+ }()
if !u.exact {
// If exact unification is known to fail because we attempt to
// match a type name against an unnamed type literal, consider
// the underlying type of the named type.
- // (Subtle: We use isNamed to include any type with a name (incl.
- // basic types and type parameters. We use asNamed() because we only
- // want *Named types.)
- switch {
- case !isNamed(x) && y != nil && asNamed(y) != nil:
- return u.nify(x, under(y), p)
- case x != nil && asNamed(x) != nil && !isNamed(y):
- return u.nify(under(x), y, p)
+ // (We use !hasName to exclude any type with a name, including
+ // basic types and type parameters; the rest are unamed types.)
+ if nx, _ := x.(*Named); nx != nil && !hasName(y) {
+ return u.nify(nx.under(), y, p)
+ } else if ny, _ := y.(*Named); ny != nil && !hasName(x) {
+ return u.nify(x, ny.under(), p)
}
}
@@ -352,25 +380,18 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
u.nify(x.results, y.results, p)
}
- case *_Sum:
- // This should not happen with the current internal use of sum types.
- panic("type inference across sum types not implemented")
-
case *Interface:
// Two interface types are identical if they have the same set of methods with
// the same names and identical function types. Lower-case method names from
// different packages are always different. The order of the methods is irrelevant.
if y, ok := y.(*Interface); ok {
- // If identical0 is called (indirectly) via an external API entry point
- // (such as Identical, IdenticalIgnoreTags, etc.), check is nil. But in
- // that case, interfaces are expected to be complete and lazy completion
- // here is not needed.
- if u.check != nil {
- u.check.completeInterface(token.NoPos, x)
- u.check.completeInterface(token.NoPos, y)
+ xset := x.typeSet()
+ yset := y.typeSet()
+ if !xset.terms.equal(yset.terms) {
+ return false
}
- a := x.allMethods
- b := y.allMethods
+ a := xset.methods
+ b := yset.methods
if len(a) == len(b) {
// Interface types are the only types where cycles can occur
// that are not "terminated" via named types; and such cycles
@@ -402,8 +423,8 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
p = p.prev
}
if debug {
- assert(sort.IsSorted(byUniqueMethodName(a)))
- assert(sort.IsSorted(byUniqueMethodName(b)))
+ assertSortedMethods(a)
+ assertSortedMethods(b)
}
for i, f := range a {
g := b[i]
@@ -428,19 +449,18 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
}
case *Named:
- // Two named types are identical if their type names originate
- // in the same type declaration.
- // if y, ok := y.(*Named); ok {
- // return x.obj == y.obj
- // }
+ // TODO(gri) This code differs now from the parallel code in Checker.identical. Investigate.
if y, ok := y.(*Named); ok {
+ 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(x.targs) == len(y.targs))
- for i, x := range x.targs {
- if !u.nify(x, y.targs[i], p) {
+ assert(len(xargs) == len(yargs))
+ for i, x := range xargs {
+ if !u.nify(x, yargs[i], p) {
return false
}
}
@@ -448,21 +468,17 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
}
}
- case *_TypeParam:
+ case *TypeParam:
// Two type parameters (which are not part of the type parameters of the
// enclosing type as those are handled in the beginning of this function)
// are identical if they originate in the same declaration.
return x == y
- // case *instance:
- // unreachable since types are expanded
-
case nil:
// avoid a crash in case of nil type
default:
- u.check.dump("### u.nify(%s, %s), u.x.tparams = %s", x, y, u.x.tparams)
- unreachable()
+ panic(fmt.Sprintf("### u.nify(%s, %s), u.x.tparams = %s", x, y, u.x.tparams))
}
return false
diff --git a/libgo/go/go/types/union.go b/libgo/go/go/types/union.go
new file mode 100644
index 0000000..9c59279
--- /dev/null
+++ b/libgo/go/go/types/union.go
@@ -0,0 +1,189 @@
+// 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.
+
+package types
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+// ----------------------------------------------------------------------------
+// API
+
+// A Union represents a union of terms embedded in an interface.
+type Union struct {
+ terms []*Term // list of syntactical terms (not a canonicalized termlist)
+}
+
+// NewUnion returns a new Union type with the given terms.
+// It is an error to create an empty union; they are syntactically not possible.
+func NewUnion(terms []*Term) *Union {
+ if len(terms) == 0 {
+ panic("empty union")
+ }
+ return &Union{terms}
+}
+
+func (u *Union) Len() int { return len(u.terms) }
+func (u *Union) Term(i int) *Term { return u.terms[i] }
+
+func (u *Union) Underlying() Type { return u }
+func (u *Union) String() string { return TypeString(u, nil) }
+
+// A Term represents a term in a Union.
+type Term term
+
+// NewTerm returns a new union term.
+func NewTerm(tilde bool, typ Type) *Term { return &Term{tilde, typ} }
+
+func (t *Term) Tilde() bool { return t.tilde }
+func (t *Term) Type() Type { return t.typ }
+func (t *Term) String() string { return (*term)(t).String() }
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+// Avoid excessive type-checking times due to quadratic termlist operations.
+const maxTermCount = 100
+
+// parseUnion parses uexpr as a union of expressions.
+// The result is a Union type, or Typ[Invalid] for some errors.
+func parseUnion(check *Checker, uexpr ast.Expr) Type {
+ blist, tlist := flattenUnion(nil, uexpr)
+ assert(len(blist) == len(tlist)-1)
+
+ var terms []*Term
+
+ var u Type
+ for i, x := range tlist {
+ term := parseTilde(check, x)
+ if len(tlist) == 1 && !term.tilde {
+ // Single type. Ok to return early because all relevant
+ // checks have been performed in parseTilde (no need to
+ // run through term validity check below).
+ return term.typ // typ already recorded through check.typ in parseTilde
+ }
+ if len(terms) >= maxTermCount {
+ if u != Typ[Invalid] {
+ check.errorf(x, _InvalidUnion, "cannot handle more than %d union terms (implementation limitation)", maxTermCount)
+ u = Typ[Invalid]
+ }
+ } else {
+ terms = append(terms, term)
+ u = &Union{terms}
+ }
+
+ if i > 0 {
+ check.recordTypeAndValue(blist[i-1], typexpr, u, nil)
+ }
+ }
+
+ if u == Typ[Invalid] {
+ return u
+ }
+
+ // Check validity of terms.
+ // Do this check later because it requires types to be set up.
+ // Note: This is a quadratic algorithm, but unions tend to be short.
+ check.later(func() {
+ for i, t := range terms {
+ if t.typ == Typ[Invalid] {
+ continue
+ }
+
+ u := under(t.typ)
+ f, _ := u.(*Interface)
+ if t.tilde {
+ if f != nil {
+ check.errorf(tlist[i], _InvalidUnion, "invalid use of ~ (%s is an interface)", t.typ)
+ continue // don't report another error for t
+ }
+
+ if !Identical(u, t.typ) {
+ check.errorf(tlist[i], _InvalidUnion, "invalid use of ~ (underlying type of %s is %s)", t.typ, u)
+ continue // don't report another error for t
+ }
+ }
+
+ // Stand-alone embedded interfaces are ok and are handled by the single-type case
+ // in the beginning. Embedded interfaces with tilde are excluded above. If we reach
+ // here, we must have at least two terms in the union.
+ if f != nil && !f.typeSet().IsTypeSet() {
+ switch {
+ case f.typeSet().NumMethods() != 0:
+ check.errorf(tlist[i], _InvalidUnion, "cannot use %s in union (%s contains methods)", t, t)
+ case t.typ == universeComparable.Type():
+ check.error(tlist[i], _InvalidUnion, "cannot use comparable in union")
+ case f.typeSet().comparable:
+ check.errorf(tlist[i], _InvalidUnion, "cannot use %s in union (%s embeds comparable)", t, t)
+ default:
+ panic("not a type set but no methods and not comparable")
+ }
+ continue // don't report another error for t
+ }
+
+ // Report overlapping (non-disjoint) terms such as
+ // a|a, a|~a, ~a|~a, and ~a|A (where under(A) == a).
+ if j := overlappingTerm(terms[:i], t); j >= 0 {
+ check.softErrorf(tlist[i], _InvalidUnion, "overlapping terms %s and %s", t, terms[j])
+ }
+ }
+ })
+
+ return u
+}
+
+func parseTilde(check *Checker, tx ast.Expr) *Term {
+ x := tx
+ var tilde bool
+ if op, _ := x.(*ast.UnaryExpr); op != nil && op.Op == token.TILDE {
+ x = op.X
+ tilde = true
+ }
+ typ := check.typ(x)
+ // Embedding stand-alone type parameters is not permitted (issue #47127).
+ // We don't need this restriction anymore if we make the underlying type of a type
+ // parameter its constraint interface: if we embed a lone type parameter, we will
+ // simply use its underlying type (like we do for other named, embedded interfaces),
+ // and since the underlying type is an interface the embedding is well defined.
+ if isTypeParam(typ) {
+ check.error(x, _MisplacedTypeParam, "cannot embed a type parameter")
+ typ = Typ[Invalid]
+ }
+ term := NewTerm(tilde, typ)
+ if tilde {
+ check.recordTypeAndValue(tx, typexpr, &Union{[]*Term{term}}, nil)
+ }
+ return term
+}
+
+// overlappingTerm reports the index of the term x in terms which is
+// overlapping (not disjoint) from y. The result is < 0 if there is no
+// such term.
+func overlappingTerm(terms []*Term, y *Term) int {
+ for i, x := range terms {
+ // disjoint requires non-nil, non-top arguments
+ if debug {
+ if x == nil || x.typ == nil || y == nil || y.typ == nil {
+ panic("empty or top union term")
+ }
+ }
+ if !(*term)(x).disjoint((*term)(y)) {
+ return i
+ }
+ }
+ return -1
+}
+
+// flattenUnion walks a union type expression of the form A | B | C | ...,
+// extracting both the binary exprs (blist) and leaf types (tlist).
+func flattenUnion(list []ast.Expr, x ast.Expr) (blist, tlist []ast.Expr) {
+ if o, _ := x.(*ast.BinaryExpr); o != nil && o.Op == token.OR {
+ blist, tlist = flattenUnion(list, o.X)
+ blist = append(blist, o)
+ x = o.Y
+ }
+ return blist, append(tlist, x)
+}
diff --git a/libgo/go/go/types/universe.go b/libgo/go/go/types/universe.go
index d7feb2c..3421634 100644
--- a/libgo/go/go/types/universe.go
+++ b/libgo/go/go/types/universe.go
@@ -8,7 +8,6 @@ package types
import (
"go/constant"
- "go/internal/typeparams"
"go/token"
"strings"
)
@@ -22,11 +21,12 @@ var Universe *Scope
var Unsafe *Package
var (
- universeIota *Const
- universeByte *Basic // uint8 alias, but has name "byte"
- universeRune *Basic // int32 alias, but has name "rune"
- universeAny *Interface
- universeError *Named
+ universeIota Object
+ universeByte Type // uint8 alias, but has name "byte"
+ universeRune Type // int32 alias, but has name "rune"
+ universeAny Object
+ universeError Type
+ universeComparable Object
)
// Typ contains the predeclared *Basic types indexed by their
@@ -79,20 +79,43 @@ func defPredeclaredTypes() {
def(NewTypeName(token.NoPos, nil, t.name, t))
}
- // any
- // (Predeclared and entered into universe scope so we do all the
- // usual checks; but removed again from scope later since it's
- // only visible as constraint in a type parameter list.)
- def(NewTypeName(token.NoPos, nil, "any", &emptyInterface))
+ // type any = interface{}
+ // Note: don't use &emptyInterface for the type of any. Using a unique
+ // pointer allows us to detect any and format it as "any" rather than
+ // interface{}, which clarifies user-facing error messages significantly.
+ def(NewTypeName(token.NoPos, nil, "any", &Interface{complete: true, tset: &topTypeSet}))
- // Error has a nil package in its qualified name since it is in no package
+ // type error interface{ Error() string }
{
+ obj := NewTypeName(token.NoPos, nil, "error", nil)
+ obj.setColor(black)
+ typ := NewNamed(obj, nil, nil)
+
+ // error.Error() string
+ recv := NewVar(token.NoPos, nil, "", typ)
res := NewVar(token.NoPos, nil, "", Typ[String])
- sig := &Signature{results: NewTuple(res)}
+ sig := NewSignatureType(recv, nil, nil, nil, NewTuple(res), false)
err := NewFunc(token.NoPos, nil, "Error", sig)
- typ := &Named{underlying: NewInterfaceType([]*Func{err}, nil).Complete()}
- sig.recv = NewVar(token.NoPos, nil, "", typ)
- def(NewTypeName(token.NoPos, nil, "error", typ))
+
+ // interface{ Error() string }
+ ityp := &Interface{obj: obj, methods: []*Func{err}, complete: true}
+ computeInterfaceTypeSet(nil, token.NoPos, ityp) // prevent races due to lazy computation of tset
+
+ typ.SetUnderlying(ityp)
+ def(obj)
+ }
+
+ // type comparable interface{} // marked as comparable
+ {
+ obj := NewTypeName(token.NoPos, nil, "comparable", nil)
+ obj.setColor(black)
+ typ := NewNamed(obj, nil, nil)
+
+ // interface{} // marked as comparable
+ ityp := &Interface{obj: obj, complete: true, tset: &_TypeSet{true, nil, allTermlist}}
+
+ typ.SetUnderlying(ityp)
+ def(obj)
}
}
@@ -202,33 +225,6 @@ func DefPredeclaredTestFuncs() {
def(newBuiltin(_Trace))
}
-func defPredeclaredComparable() {
- // The "comparable" interface can be imagined as defined like
- //
- // type comparable interface {
- // == () untyped bool
- // != () untyped bool
- // }
- //
- // == and != cannot be user-declared but we can declare
- // a magic method == and check for its presence when needed.
-
- // Define interface { == () }. We don't care about the signature
- // for == so leave it empty except for the receiver, which is
- // set up later to match the usual interface method assumptions.
- sig := new(Signature)
- eql := NewFunc(token.NoPos, nil, "==", sig)
- iface := NewInterfaceType([]*Func{eql}, nil).Complete()
-
- // set up the defined type for the interface
- obj := NewTypeName(token.NoPos, nil, "comparable", nil)
- named := NewNamed(obj, iface, nil)
- obj.color_ = black
- sig.recv = NewVar(token.NoPos, nil, "", named) // complete == signature
-
- def(obj)
-}
-
func init() {
Universe = NewScope(nil, token.NoPos, token.NoPos, "universe")
Unsafe = NewPackage("unsafe", "unsafe")
@@ -238,18 +234,13 @@ func init() {
defPredeclaredConsts()
defPredeclaredNil()
defPredeclaredFuncs()
- if typeparams.Enabled {
- defPredeclaredComparable()
- }
-
- universeIota = Universe.Lookup("iota").(*Const)
- universeByte = Universe.Lookup("byte").(*TypeName).typ.(*Basic)
- universeRune = Universe.Lookup("rune").(*TypeName).typ.(*Basic)
- universeAny = Universe.Lookup("any").(*TypeName).typ.(*Interface)
- universeError = Universe.Lookup("error").(*TypeName).typ.(*Named)
- // "any" is only visible as constraint in a type parameter list
- delete(Universe.elems, "any")
+ universeIota = Universe.Lookup("iota")
+ universeByte = Universe.Lookup("byte").Type()
+ universeRune = Universe.Lookup("rune").Type()
+ universeAny = Universe.Lookup("any")
+ universeError = Universe.Lookup("error").Type()
+ universeComparable = Universe.Lookup("comparable")
}
// Objects with names containing blanks are internal and not entered into
@@ -263,7 +254,7 @@ func def(obj Object) {
return // nothing to do
}
// fix Obj link for named types
- if typ := asNamed(obj.Type()); typ != nil {
+ if typ, _ := obj.Type().(*Named); typ != nil {
typ.obj = obj.(*TypeName)
}
// exported identifiers go into package unsafe
@@ -281,6 +272,6 @@ func def(obj Object) {
}
}
if scope.Insert(obj) != nil {
- panic("internal error: double declaration")
+ panic("double declaration of predeclared identifier")
}
}
diff --git a/libgo/go/go/types/validtype.go b/libgo/go/go/types/validtype.go
new file mode 100644
index 0000000..c4ec2f2
--- /dev/null
+++ b/libgo/go/go/types/validtype.go
@@ -0,0 +1,147 @@
+// Copyright 2022 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.
+
+package types
+
+// validType verifies that the given type does not "expand" indefinitely
+// producing a cycle in the type graph. Cycles are detected by marking
+// defined types.
+// (Cycles involving alias types, as in "type A = [10]A" are detected
+// earlier, via the objDecl cycle detection mechanism.)
+func (check *Checker) validType(typ *Named) {
+ check.validType0(typ, nil, nil)
+}
+
+type typeInfo uint
+
+// validType0 checks if the given type is valid. If typ is a type parameter
+// its value is looked up in the provided environment. The environment is
+// nil if typ is not part of (the RHS of) an instantiated type, in that case
+// any type parameter encountered must be from an enclosing function and can
+// be ignored. The path is the list of type names that lead to the current typ.
+func (check *Checker) validType0(typ Type, env *tparamEnv, path []Object) typeInfo {
+ const (
+ unknown typeInfo = iota
+ marked
+ valid
+ invalid
+ )
+
+ switch t := typ.(type) {
+ case nil:
+ // We should never see a nil type but be conservative and panic
+ // only in debug mode.
+ if debug {
+ panic("validType0(nil)")
+ }
+
+ case *Array:
+ return check.validType0(t.elem, env, path)
+
+ case *Struct:
+ for _, f := range t.fields {
+ if check.validType0(f.typ, env, path) == invalid {
+ return invalid
+ }
+ }
+
+ case *Union:
+ for _, t := range t.terms {
+ if check.validType0(t.typ, env, path) == invalid {
+ return invalid
+ }
+ }
+
+ case *Interface:
+ for _, etyp := range t.embeddeds {
+ if check.validType0(etyp, env, path) == invalid {
+ return invalid
+ }
+ }
+
+ case *Named:
+ // Don't report a 2nd error if we already know the type is invalid
+ // (e.g., if a cycle was detected earlier, via under).
+ if t.underlying == Typ[Invalid] {
+ check.infoMap[t] = invalid
+ return invalid
+ }
+
+ switch check.infoMap[t] {
+ case unknown:
+ check.infoMap[t] = marked
+ check.infoMap[t] = check.validType0(t.orig.fromRHS, env.push(t), append(path, t.obj))
+ case marked:
+ // We have seen type t before and thus must have a cycle.
+ check.infoMap[t] = invalid
+ // t cannot be in an imported package otherwise that package
+ // would have reported a type cycle and couldn't have been
+ // imported in the first place.
+ assert(t.obj.pkg == check.pkg)
+ t.underlying = Typ[Invalid] // t is in the current package (no race possibilty)
+ // Find the starting point of the cycle and report it.
+ for i, tn := range path {
+ if tn == t.obj {
+ check.cycleError(path[i:])
+ return invalid
+ }
+ }
+ panic("cycle start not found")
+ }
+ return check.infoMap[t]
+
+ case *TypeParam:
+ // A type parameter stands for the type (argument) it was instantiated with.
+ // Check the corresponding type argument for validity if we have one.
+ if env != nil {
+ if targ := env.tmap[t]; targ != nil {
+ // Type arguments found in targ must be looked
+ // up in the enclosing environment env.link.
+ return check.validType0(targ, env.link, path)
+ }
+ }
+ }
+
+ return valid
+}
+
+// A tparamEnv provides the environment for looking up the type arguments
+// with which type parameters for a given instance were instantiated.
+// If we don't have an instance, the corresponding tparamEnv is nil.
+type tparamEnv struct {
+ tmap substMap
+ link *tparamEnv
+}
+
+func (env *tparamEnv) push(typ *Named) *tparamEnv {
+ // If typ is not an instantiated type there are no typ-specific
+ // type parameters to look up and we don't need an environment.
+ targs := typ.TypeArgs()
+ if targs == nil {
+ return nil // no instance => nil environment
+ }
+
+ // Populate tmap: remember the type argument for each type parameter.
+ // We cannot use makeSubstMap because the number of type parameters
+ // and arguments may not match due to errors in the source (too many
+ // or too few type arguments). Populate tmap "manually".
+ tparams := typ.TypeParams()
+ n, m := targs.Len(), tparams.Len()
+ if n > m {
+ n = m // too many targs
+ }
+ tmap := make(substMap, n)
+ for i := 0; i < n; i++ {
+ tmap[tparams.At(i)] = targs.At(i)
+ }
+
+ return &tparamEnv{tmap: tmap, link: env}
+}
+
+// TODO(gri) Alternative implementation:
+// We may not need to build a stack of environments to
+// look up the type arguments for type parameters. The
+// same information should be available via the path:
+// We should be able to just walk the path backwards
+// and find the type arguments in the instance objects.