diff options
author | Katie Hockman <katie@golang.org> | 2021-09-20 15:45:51 -0400 |
---|---|---|
committer | Katie Hockman <katie@golang.org> | 2021-09-20 15:46:10 -0400 |
commit | c6c884be3854f3c94f1ee93a273544c9c5c35df5 (patch) | |
tree | 12e3ece09595d359f9edc99025c3f3192aea2213 /src/cmd/compile/internal/types2 | |
parent | 51ca5706ab2074a624f8a2590a2a81e6a5821e48 (diff) | |
parent | af72ddfcd7826df9aefb2207b8ac270bb91fea2f (diff) | |
download | go-c6c884be3854f3c94f1ee93a273544c9c5c35df5.tar.gz go-c6c884be3854f3c94f1ee93a273544c9c5c35df5.zip |
[dev.fuzz] all: merge master (af72ddf) into dev.fuzz
This now includes the fix in CL 350729, which means
we no longer need to skip the test in dev.fuzz.
Conflicts:
- src/cmd/compile/internal/noder/unified_test.go
Merge List:
+ 2021-09-20 af72ddfcd7 cmd/compile: extend dump-to-file to handle "genssa" (asm) case.
+ 2021-09-20 3c764babe7 cmd/go: write go.mod requirements more consistently for go 1.17+
+ 2021-09-20 6268468e02 cmd/link: generate DIE for types referenced only through dictionaries
+ 2021-09-20 6acac8b685 cmd/compile: delay all transforms for generic funcs/methods
+ 2021-09-20 988f18d61d go/types: export Named._Orig as Named.Origin
+ 2021-09-20 b6dddaccd7 cmd/compile: fix transform.AssignOp to deal with tricky case
+ 2021-09-20 9e60c37147 cmd/compile: document register-based ABI for ppc64
+ 2021-09-20 79159f2e83 cmd/compile: fix simplification rules on arm/arm64
+ 2021-09-20 eff27e858b cmd/compile: ensure constant shift amounts are in range for arm
+ 2021-09-20 9ebe7c8ec6 go/test: add a test for issue 48344
+ 2021-09-20 6f35430faa cmd/compile: allow rotates to be merged with logical ops on arm64
+ 2021-09-20 2d9b486420 cmd/compile: update doc at top of iexport.go on the changes for typeparams
+ 2021-09-20 a81b0dc6ee cmd/compile: rename instType -> instanceType
+ 2021-09-20 119213566a cmd/cgo: remove hardcoded '-pie' ldflag for linux/arm
+ 2021-09-20 a83a558733 cmd/compile: fix export/import of range loop.
+ 2021-09-19 315dbd10c9 cmd/compile: fold double negate on arm64
+ 2021-09-19 83b36ffb10 cmd/compile: implement constant rotates on arm64
+ 2021-09-19 771b8ea4f4 cmd/compile: fix missing markHiddenClosureDead in deadcode pass
+ 2021-09-18 c894b442d1 net/rpc: remove warnings on incompatible methods at registration
+ 2021-09-17 4b654c0eec cmd/compile: SSA ".this" variable
+ 2021-09-17 f01721efb9 cmd/compile: remove self copies in tail-call wrappers
+ 2021-09-17 163871feb1 time: re-add space-padded day of year to docs
+ 2021-09-17 ac7c34767d time: support fractional timezone minutes in MarshalBinary
+ 2021-09-17 07b30a4f77 cmd/compile: delay transformAssign if lhs/rhs have typeparam
+ 2021-09-17 c10b980220 cmd/compile: restore tail call for method wrappers
+ 2021-09-17 50e4508269 cmd/compile: fix import/export of Init and Def fields.
+ 2021-09-17 3fa35b5f97 go/types: ensure that we always get a new signature in expandNamed
+ 2021-09-17 3fa7dbeff5 cmd/go: fix GOARCH value in GOAMD64 docs
+ 2021-09-17 974b0166d6 syscall: implement Pipe using pipe2 syscall on all linux platforms
+ 2021-09-17 1a49dcb82f syscall: remove //sysnb comment generating Setreuid for linux/arm64
+ 2021-09-17 cea7a71d40 cmd/compile: fix generic type handling in crawler
+ 2021-09-17 74e384f50d internal/poll: inject a hook into the runtime finalizer to count the closed pipes
+ 2021-09-17 323c6f74d3 log: don't format if writing to io.Discard
+ 2021-09-17 7f36ef0aff cmd/compile/internal/noder: hide TestUnifiedCompare behind -cmp flag
+ 2021-09-17 70493b3eb0 runtime/cgo: save and restore X3 (aka GP) for crosscall1 on riscv64
+ 2021-09-17 6d02ce8584 runtime: fix prettyprinting of parametric types in gdb
+ 2021-09-17 6602c86a38 cmd/internal/obj/riscv: improve instruction validation
+ 2021-09-17 14e812bfc5 syscall: do not use handle lists on windows when NoInheritHandles is true
+ 2021-09-16 8d2a9c32a2 all: remove incorrectly repeated words in comments
+ 2021-09-16 af9da137a9 A+C: update name to real name and add to AUTHORS
+ 2021-09-16 265b59aefd cmd/cgo: for godefs, don't let field prefix removal cause duplicates
+ 2021-09-16 4efdaa7bc7 testing: skip panics when picking the line number for decoration
+ 2021-09-16 e09dcc211a go/types, types2: add an additional shift test case
+ 2021-09-16 5402b4376c spec: fix incorrect type in a shift example
+ 2021-09-16 d09e09bc61 cmd/compile: fixing writebarrier.go for -G=3
+ 2021-09-16 bcdc61d830 cmd/compile: preserve statements better in expandCalls
+ 2021-09-16 48e2b1ea91 cmd/compile: fix LocResults formatting
+ 2021-09-16 b1bedc0774 cmd/go: add GOAMD64 environment variable
+ 2021-09-16 04f5116c98 cmd/go: clean paths before checking same directory
+ 2021-09-16 e7dbe3908e cmd/cgo: add missing tab in exports for a result of void
+ 2021-09-15 cfa233d76b cmd/compile: remove unneeded early transforms, with dictionary change
+ 2021-09-15 59a9a035ff cmd/compile: switch to computing dict format on instantiated functions
+ 2021-09-15 0edc6c4fa0 cmd/internal/obj/ppc64: generate prologue code compatible with new ABI
+ 2021-09-15 03df68d3c3 runtime: fix setting of cpu features for amd64
+ 2021-09-15 6196979365 cmd/go/internal/modload: prevent tidy downgrading disambiguating modules
+ 2021-09-15 72bb8185b5 cmd/compile: emit DWARF info about dictionary entries
+ 2021-09-15 5b48fca1fa cmd/compile: mark wrapper functions with DW_AT_trampoline
+ 2021-09-15 e4dfd788e6 go/internal/gcimporter,cmd/compile: minor clean-up in iimport.go
+ 2021-09-15 4847c47cb8 cmd/compile/internal/types2: eliminate Named.instPos
+ 2021-09-15 3100f54f20 cmd/compile/internal/types2: merge Named type loading and expansion
+ 2021-09-15 738cebb174 cmd/compile/internal/types2: implement Identical for *Union types
+ 2021-09-15 b26d325cb1 cmd/compile/internal/types2: remove some unnecessary loading/expansion of Named types
+ 2021-09-15 9fc28892cb cmd/compile/internal/types2: export TypeHash, return value without blanks
+ 2021-09-15 2da3375e9b runtime: in adjustTimers back up as far as necessary
+ 2021-09-15 c7f2f51fed cmd/go: remove subcommand prefix from error messages
+ 2021-09-15 0bb40b08c4 go/types: implement Identical for *Union types
+ 2021-09-15 cb4e1de021 go/types: minor cleanup of instantiation
+ 2021-09-15 a0f3129466 go/types: instantiate methods when instantiating Named types
+ 2021-09-14 bf26e43d0f go/types: eliminate Named.instPos
+ 2021-09-14 2933c451a0 go/types: merge Named type loading and expansion
+ 2021-09-14 137543bb93 cmd/compile: set IsShape based on type being in the Shapes pkg
+ 2021-09-14 3a72175cdc cmd/compile: fix test/typeparam/mdempsky/4.go for -G=3
+ 2021-09-14 b2c04f0d48 runtime: avoid loop variable capture in test
+ 2021-09-14 181e8cde30 go/internal/gcimporter: remove outdated comment
+ 2021-09-14 8699425b55 syscall: remove use of IN_KUBERNETES in test
+ 2021-09-14 b3c6de9dcd cmd/internal/obj/ppc64: allow VR register arguments to VS registers
+ 2021-09-14 ee91bb8319 cmd/compile: prevent typecheck importer reading type parameter twice
+ 2021-09-14 2953cd0083 go/internal/gcimporter: prevent importReader reading type parameter twice
+ 2021-09-14 b8c802b116 cmd/compile: prevent importReader reading type parameter twice
+ 2021-09-14 4a4221e818 all: remove some unused code
+ 2021-09-14 71adc658de runtime: change time.now to ABIInternal
+ 2021-09-14 146e8d4994 reflect: use Value.Len instead of conversion to slice header
+ 2021-09-13 9a58aa267e spec: fix prose about terminating statements
+ 2021-09-13 42057e9848 cmd/compile: save the note of fields when translating struct
+ 2021-09-13 960d036f8f cmd/go: add missing parenthesis in a call to "PrintVersion"
+ 2021-09-13 81a4fe6fd2 cmd/link/internal/ld: re-enable DWARF tests on solaris/illumos
+ 2021-09-13 f93a63addb reflect: add a floating point section to DeepEqual tests
+ 2021-09-13 a0c409cbc8 reflect: add fast paths for common, simple Kinds to DeepEqual
+ 2021-09-13 ac40c9872f reflect: fix _faststr optimization
+ 2021-09-13 c8a58f29dc cmd/go: add test to check for a potential workspace loading issue
+ 2021-09-13 e74e363a6b strings: add Clone function
+ 2021-09-13 bced369a50 cmd/link: minor code cleanup in dwarf gen
+ 2021-09-13 c3b217a0e5 cmd/go: document 'go install cmd@version' ignores vendor directories
+ 2021-09-12 ad97d204f0 go/types: remove some unnecessary loading/expansion of Named types
+ 2021-09-12 0d8a4bfc96 bufio: add Writer.AvailableBuffer
+ 2021-09-11 23832ba2e2 reflect: optimize for maps with string keys
+ 2021-09-11 a50225a0dc bufio: make Reader.Reset and Writer.Reset work on the zero value
+ 2021-09-10 cf2fe5d6f1 doc/asm: fix HTML markup
+ 2021-09-10 1bf2cd1291 debug/elf: retain original error message when getSymbols fails.
+ 2021-09-10 5a4b9f9494 time: reference -tags=timetzdata in testing panic
+ 2021-09-10 025308fe08 testing: increase alternation precedence
+ 2021-09-10 5a94a90d84 cmd/compile/internal/types2: better error message for invalid array decls
+ 2021-09-10 da1aa65053 cmd/compile/internal/syntax: correct follow token for type parameter lists
+ 2021-09-10 96ab854ab0 cmd/compile/internal: better AST line highlight in ssa.html
+ 2021-09-10 90c5660616 embed: guarantee the returned file of FS.Open implements io.Seeker
+ 2021-09-10 c69f5c0d76 cmd/compile: add support for Abs and Copysign intrinsics on riscv64
+ 2021-09-10 2091bd3f26 cmd/compile: simiplify arm64 bitfield optimizations
+ 2021-09-09 b32209d22d cmd/compile: fix test case for unified IR (fix build)
+ 2021-09-09 1a708bcf1d cmd/compile: don't crash while reporting invalid alias cycle
+ 2021-09-09 426ff3746f cmd/cgo, runtime/cgo: avoid GCC/clang conversion warnings
+ 2021-09-09 73483df406 cmd/compile/internal/syntax: better error message for missing type constraint
+ 2021-09-09 e1c3f2158f time: propagate "," separator for fractional seconds into Format
+ 2021-09-09 c981874a5a cmd/compile: fix implement for closure in a global assignment
+ 2021-09-09 2c4f389c02 cmd/link: enable internal linker in more cases for ppc64le
+ 2021-09-09 fb84e99eb7 test: add compiler regress tests for #46461
+ 2021-09-09 b9e1a24581 cmd/compile: fix case where init info of OAS node is dropped
+ 2021-09-09 f9271e4f85 go/types, types2: rename RParams -> RecvTypeParams
+ 2021-09-09 ea434450c2 reflect: add hooks for dealing with narrow width floats
+ 2021-09-09 a53e3d5f88 net: deprecate (net.Error).Temporary
+ 2021-09-09 19457a58e5 cmd/compile: stenciled conversions might be NOPs
+ 2021-09-09 a295b3cec8 test: re-enable AsmCheck tests for types2-based frontends
+ 2021-09-09 66f0d35f71 go/types: reduce number of delayed functions
+ 2021-09-09 d2a77f1c76 go/types: handle recursive type parameter constraints
+ 2021-09-09 9e1eea6f8b go/types: detect constraint type inference cycles
+ 2021-09-09 b86e8dd0f3 test/typeparam: fix issue48094b test build
+ 2021-09-09 c84f3a4004 syscall: drop fallback to pipe in Pipe on linux/arm
+ 2021-09-09 376a079762 cmd/compile: fix unified IR panic when expanding nested inline function
+ 2021-09-09 6edc57983a internal/poll: report open fds when TestSplicePipePool fails
+ 2021-09-09 2481f6e367 cmd/compile: fix wrong instantiated type for embedded receiver
+ 2021-09-09 d62866ef79 cmd/compile: move checkptr alignment to SSA generation
+ 2021-09-09 8fad81cd62 cmd/compile: fold handling OCONV logic to separate function
+ 2021-09-09 9cbc76bdf9 cmd/internal/obj/arm64: add checks for incorrect use of REGTMP register
+ 2021-09-09 42563f89d7 cmd/compile: remove 'ext' fields from unified IR reader/writer types
+ 2021-09-09 4c52eac49b cmd/compile: simplify value coding for unified IR
+ 2021-09-09 e30a09013b cmd/compile: extrapolate $GOROOT in unified IR
+ 2021-09-08 a1f6208e56 go/types, types2: add Environment to Config
+ 2021-09-08 f5f8a911d8 cmd/compile/internal/types2: spell out 'Type' in type parameter APIs
+ 2021-09-08 bff39cf6cb cmd/compile: add automated rewrite cycle detection
+ 2021-09-08 b61e1ed863 cmd/compile/internal/types2: temporarily pin the Checker to Interface during checking
+ 2021-09-08 47f3e1e02c cmd/compile/internal/types2: move NewTypeParam off of Checker
+ 2021-09-08 ccc927b8f6 cmd/compile/internal/types2: move typeHash to environment.go
+ 2021-09-08 30e9bfbcef cmd/compile/internal/types2: implement deduplication of instances using the Environment
+ 2021-09-08 0406d3a8e5 go/ast: rename MultiIndexExpr to IndexListExpr
Change-Id: I7f917d45b0507c122c212305144b0b455618ff54
Diffstat (limited to 'src/cmd/compile/internal/types2')
33 files changed, 555 insertions, 340 deletions
diff --git a/src/cmd/compile/internal/types2/api.go b/src/cmd/compile/internal/types2/api.go index b2938b84da1..6914e6c89f7 100644 --- a/src/cmd/compile/internal/types2/api.go +++ b/src/cmd/compile/internal/types2/api.go @@ -108,6 +108,11 @@ 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 { + // Environment is the environment used for resolving global + // identifiers. If nil, the type checker will initialize this + // field with a newly created environment. + Environment *Environment + // GoVersion describes the accepted Go language version. The string // must follow the format "go%d.%d" (e.g. "go1.12") or ist must be // empty; an empty string indicates the latest language version. diff --git a/src/cmd/compile/internal/types2/api_test.go b/src/cmd/compile/internal/types2/api_test.go index 039a6c0e5e7..cd5a61332a7 100644 --- a/src/cmd/compile/internal/types2/api_test.go +++ b/src/cmd/compile/internal/types2/api_test.go @@ -145,6 +145,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 { @@ -1645,6 +1646,48 @@ func TestIdentical_issue15173(t *testing.T) { } } +func TestIdenticalUnions(t *testing.T) { + tname := NewTypeName(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)" f, err := parseSrc("issue15305.go", src) @@ -1871,7 +1914,7 @@ func TestInstantiate(t *testing.T) { // type T should have one type parameter T := pkg.Scope().Lookup("T").Type().(*Named) - if n := T.TParams().Len(); n != 1 { + if n := T.TypeParams().Len(); n != 1 { t.Fatalf("expected 1 type parameter; found %d", n) } diff --git a/src/cmd/compile/internal/types2/assignments.go b/src/cmd/compile/internal/types2/assignments.go index 6184fc2ea50..29d63cf819a 100644 --- a/src/cmd/compile/internal/types2/assignments.go +++ b/src/cmd/compile/internal/types2/assignments.go @@ -68,7 +68,7 @@ func (check *Checker) assignment(x *operand, T Type, context string) { // x.typ is typed // A generic (non-instantiated) function value cannot be assigned to a variable. - if sig := asSignature(x.typ); sig != nil && sig.TParams().Len() > 0 { + if sig := asSignature(x.typ); sig != nil && sig.TypeParams().Len() > 0 { check.errorf(x, "cannot use generic function %s without instantiation in %s", x, context) } diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go index e3844d5163e..3b8d85859a7 100644 --- a/src/cmd/compile/internal/types2/builtins.go +++ b/src/cmd/compile/internal/types2/builtins.go @@ -826,7 +826,7 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type { // type param is placed in the current package so export/import // works as expected. tpar := NewTypeName(nopos, check.pkg, "<type parameter>", nil) - ptyp := check.NewTypeParam(tpar, NewInterfaceType(nil, []Type{NewUnion(terms)})) // assigns type to tpar as a side-effect + ptyp := check.newTypeParam(tpar, NewInterfaceType(nil, []Type{NewUnion(terms)})) // assigns type to tpar as a side-effect ptyp.index = tp.index return ptyp diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go index 5bf17876c15..ba3bb475a31 100644 --- a/src/cmd/compile/internal/types2/call.go +++ b/src/cmd/compile/internal/types2/call.go @@ -30,7 +30,7 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) { // check number of type arguments (got) vs number of type parameters (want) sig := x.typ.(*Signature) - got, want := len(targs), sig.TParams().Len() + got, want := len(targs), sig.TypeParams().Len() if !useConstraintTypeInference && got != want || got > want { check.errorf(xlist[got-1], "got %d type arguments but want %d", got, want) x.mode = invalid @@ -41,7 +41,7 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) { // if we don't have enough type arguments, try type inference inferred := false if got < want { - targs = check.infer(inst.Pos(), sig.TParams().list(), targs, nil, nil, true) + targs = check.infer(inst.Pos(), sig.TypeParams().list(), targs, nil, nil, true) if targs == nil { // error was already reported x.mode = invalid @@ -61,7 +61,7 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) { // instantiate function signature res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature) - assert(res.TParams().Len() == 0) // signature is not generic anymore + assert(res.TypeParams().Len() == 0) // signature is not generic anymore if inferred { check.recordInferred(inst, targs, res) } @@ -166,7 +166,7 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind { assert(len(targs) == len(xlist)) // check number of type arguments (got) vs number of type parameters (want) - got, want := len(targs), sig.TParams().Len() + got, want := len(targs), sig.TypeParams().Len() if got > want { check.errorf(xlist[want], "got %d type arguments but want %d", got, want) check.use(call.ArgList...) @@ -200,7 +200,7 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind { // if type inference failed, a parametrized result must be invalidated // (operands cannot have a parametrized type) - if x.mode == value && sig.TParams().Len() > 0 && isParameterized(sig.TParams().list(), x.typ) { + if x.mode == value && sig.TypeParams().Len() > 0 && isParameterized(sig.TypeParams().list(), x.typ) { x.mode = invalid } @@ -328,7 +328,7 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T } // infer type arguments and instantiate signature if necessary - if sig.TParams().Len() > 0 { + if sig.TypeParams().Len() > 0 { if !check.allowVersion(check.pkg, 1, 18) { if iexpr, _ := call.Fun.(*syntax.IndexExpr); iexpr != nil { check.softErrorf(iexpr.Pos(), "function instantiation requires go1.18 or later") @@ -338,21 +338,21 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T } // TODO(gri) provide position information for targs so we can feed // it to the instantiate call for better error reporting - targs := check.infer(call.Pos(), sig.TParams().list(), targs, sigParams, args, true) + targs := check.infer(call.Pos(), sig.TypeParams().list(), targs, sigParams, args, true) if targs == nil { return // error already reported } // compute result signature rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature) - assert(rsig.TParams().Len() == 0) // signature is not generic anymore + assert(rsig.TypeParams().Len() == 0) // signature is not generic anymore check.recordInferred(call, 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().list(), targs), nil).(*Tuple) + sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.TypeParams().list(), targs), nil).(*Tuple) } else { sigParams = rsig.params } @@ -535,7 +535,7 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) { // the signature accordingly. // TODO(gri) factor this code out sig := m.typ.(*Signature) - if sig.RParams().Len() > 0 { + if sig.RecvTypeParams().Len() > 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 @@ -564,7 +564,7 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) { // 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.pos, sig.RParams().list(), nil, NewTuple(sig.recv), []*operand{&arg}, false /* no error reporting */) + targs := check.infer(m.pos, sig.RecvTypeParams().list(), nil, NewTuple(sig.recv), []*operand{&arg}, false /* no error reporting */) //check.dump("### inferred targs = %s", targs) if targs == nil { // We may reach here if there were other errors (see issue #40056). @@ -574,7 +574,7 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) { // (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().list(), targs), nil) + copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.RecvTypeParams().list(), targs), nil) obj = © } // TODO(gri) we also need to do substitution for parameterized interface methods diff --git a/src/cmd/compile/internal/types2/check.go b/src/cmd/compile/internal/types2/check.go index 4226b4de820..24a05e6b370 100644 --- a/src/cmd/compile/internal/types2/check.go +++ b/src/cmd/compile/internal/types2/check.go @@ -86,7 +86,6 @@ type Checker struct { 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 - typMap map[string]*Named // maps an instantiated named type hash to a *Named type // 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 @@ -171,6 +170,11 @@ func NewChecker(conf *Config, pkg *Package, info *Info) *Checker { conf = new(Config) } + // make sure we have an environment + if conf.Environment == nil { + conf.Environment = NewEnvironment() + } + // make sure we have an info struct if info == nil { info = new(Info) @@ -188,7 +192,6 @@ func NewChecker(conf *Config, pkg *Package, info *Info) *Checker { version: version, objMap: make(map[Object]*declInfo), impMap: make(map[importKey]*Package), - typMap: make(map[string]*Named), } } diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go index cd97080824a..26e050511e2 100644 --- a/src/cmd/compile/internal/types2/decl.go +++ b/src/cmd/compile/internal/types2/decl.go @@ -317,7 +317,7 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo { } case *Named: - t.expand(check.typMap) + t.resolve(check.conf.Environment) // 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) @@ -592,13 +592,13 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named named.underlying = under(named) // If the RHS is a type parameter, it must be from this type declaration. - if tpar, _ := named.underlying.(*TypeParam); tpar != nil && tparamIndex(named.TParams().list(), tpar) < 0 { + if tpar, _ := named.underlying.(*TypeParam); tpar != nil && tparamIndex(named.TypeParams().list(), tpar) < 0 { check.errorf(tdecl.Type, "cannot use function type parameter %s as RHS in type declaration", tpar) named.underlying = Typ[Invalid] } } -func (check *Checker) collectTypeParams(dst **TParamList, list []*syntax.Field) { +func (check *Checker) collectTypeParams(dst **TypeParamList, list []*syntax.Field) { tparams := make([]*TypeParam, len(list)) // Declare type parameters up-front. @@ -648,7 +648,7 @@ func (check *Checker) declareTypeParam(name *syntax.Name) *TypeParam { // constraints to make sure we don't rely on them if they // are not properly set yet. tname := NewTypeName(name.Pos(), check.pkg, name.Value, nil) - tpar := check.NewTypeParam(tname, Typ[Invalid]) // assigns type to tname as a side-effect + tpar := check.newTypeParam(tname, Typ[Invalid]) // assigns type to tname as a side-effect check.declare(check.scope, name, tname, check.scope.pos) // TODO(gri) check scope position return tpar } @@ -715,7 +715,7 @@ func (check *Checker) collectMethods(obj *TypeName) { } if base != nil { - base.load() // TODO(mdempsky): Probably unnecessary. + base.resolve(nil) // TODO(mdempsky): Probably unnecessary. base.methods = append(base.methods, m) } } diff --git a/src/cmd/compile/internal/types2/environment.go b/src/cmd/compile/internal/types2/environment.go new file mode 100644 index 00000000000..fe9a3099fee --- /dev/null +++ b/src/cmd/compile/internal/types2/environment.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 types2 + +import ( + "bytes" + "strings" + "sync" +) + +// An Environment is an opaque type checking environment. It may be used to +// share identical type instances across type-checked packages or calls to +// Instantiate. +// +// It is safe for concurrent use. +type Environment struct { + mu sync.Mutex + typeMap map[string]*Named // type hash -> instance + nextID int // next unique ID + seen map[*Named]int // assigned unique IDs +} + +// NewEnvironment creates a new Environment. +func NewEnvironment() *Environment { + return &Environment{ + typeMap: make(map[string]*Named), + seen: make(map[*Named]int), + } +} + +// TypeHash returns a string representation of typ, which can be used as an exact +// type hash: types that are identical produce identical string representations. +// If typ is a *Named type and targs is not empty, typ is printed as if it were +// instantiated with targs. The result is guaranteed to not contain blanks (" "). +func (env *Environment) TypeHash(typ Type, targs []Type) string { + assert(env != nil) + assert(typ != nil) + var buf bytes.Buffer + + h := newTypeHasher(&buf, env) + if named, _ := typ.(*Named); named != nil && len(targs) > 0 { + // Don't use WriteType because we need to use the provided targs + // and not any targs that might already be with the *Named type. + h.typePrefix(named) + h.typeName(named.obj) + h.typeList(targs) + } else { + assert(targs == nil) + h.typ(typ) + } + + return strings.Replace(buf.String(), " ", "#", -1) // ReplaceAll is not available in Go1.4 +} + +// typeForHash returns the recorded type for the type hash h, if it exists. +// If no type exists for h and n is non-nil, n is recorded for h. +func (env *Environment) typeForHash(h string, n *Named) *Named { + env.mu.Lock() + defer env.mu.Unlock() + if existing := env.typeMap[h]; existing != nil { + return existing + } + if n != nil { + env.typeMap[h] = n + } + return n +} + +// idForType returns a unique ID for the pointer n. +func (env *Environment) idForType(n *Named) int { + env.mu.Lock() + defer env.mu.Unlock() + id, ok := env.seen[n] + if !ok { + id = env.nextID + env.seen[n] = id + env.nextID++ + } + return id +} diff --git a/src/cmd/compile/internal/types2/errors.go b/src/cmd/compile/internal/types2/errors.go index a68273271b7..ea43fab1782 100644 --- a/src/cmd/compile/internal/types2/errors.go +++ b/src/cmd/compile/internal/types2/errors.go @@ -246,7 +246,7 @@ func stripAnnotations(s string) string { var b bytes.Buffer 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/src/cmd/compile/internal/types2/errors_test.go b/src/cmd/compile/internal/types2/errors_test.go index e1f0e83fc97..72a2ce3655a 100644 --- a/src/cmd/compile/internal/types2/errors_test.go +++ b/src/cmd/compile/internal/types2/errors_test.go @@ -35,7 +35,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/src/cmd/compile/internal/types2/index.go b/src/cmd/compile/internal/types2/index.go index febfd21ea33..848a70dea8f 100644 --- a/src/cmd/compile/internal/types2/index.go +++ b/src/cmd/compile/internal/types2/index.go @@ -34,7 +34,7 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo return false case value: - if sig := asSignature(x.typ); sig != nil && sig.TParams().Len() > 0 { + if sig := asSignature(x.typ); sig != nil && sig.TypeParams().Len() > 0 { // function instantiation return true } diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go index bb7270b346a..c2a8155dc7b 100644 --- a/src/cmd/compile/internal/types2/infer.go +++ b/src/cmd/compile/internal/types2/infer.go @@ -562,7 +562,7 @@ func (w *cycleFinder) typ(typ Type) { w.typ(t.elem) case *Named: - for _, tpar := range t.TArgs().list() { + for _, tpar := range t.TypeArgs().list() { w.typ(tpar) } diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index c882699d1d5..7a9279943c1 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -13,21 +13,6 @@ import ( "fmt" ) -// An Environment is an opaque type checking environment. It may be used to -// share identical type instances across type checked packages or calls to -// Instantiate. -type Environment struct { - // For now, Environment just hides a Checker. - // Eventually, we strive to remove the need for a checker. - check *Checker -} - -// NewEnvironment returns a new Environment, initialized with the given -// Checker, or nil. -func NewEnvironment(check *Checker) *Environment { - return &Environment{check} -} - // Instantiate instantiates the type typ with the given type arguments targs. // typ must be a *Named or a *Signature type, and its number of type parameters // must match the number of provided type arguments. The result is a new, @@ -46,22 +31,18 @@ func NewEnvironment(check *Checker) *Environment { // TODO(rfindley): change this function to also return an error if lengths of // tparams and targs do not match. func Instantiate(env *Environment, typ Type, targs []Type, validate bool) (Type, error) { - var check *Checker - if env != nil { - check = env.check - } - inst := check.instance(nopos, typ, targs) + inst := (*Checker)(nil).instance(nopos, typ, targs, env) var err error if validate { var tparams []*TypeParam switch t := typ.(type) { case *Named: - tparams = t.TParams().list() + tparams = t.TypeParams().list() case *Signature: - tparams = t.TParams().list() + tparams = t.TypeParams().list() } - if i, err := check.verify(nopos, tparams, targs); err != nil { + if i, err := (*Checker)(nil).verify(nopos, tparams, targs); err != nil { return inst, ArgumentError{i, err} } } @@ -90,7 +71,7 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, posLis }() } - inst := check.instance(pos, typ, targs) + inst := check.instance(pos, typ, targs, check.conf.Environment) assert(len(posList) <= len(targs)) check.later(func() { @@ -99,9 +80,9 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, posLis var tparams []*TypeParam switch t := typ.(type) { case *Named: - tparams = t.TParams().list() + tparams = t.TypeParams().list() case *Signature: - tparams = t.TParams().list() + tparams = t.TypeParams().list() } // Avoid duplicate errors; instantiate will have complained if tparams // and targs do not have the same length. @@ -122,35 +103,40 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, posLis // instance creates a type or function instance using the given original type // typ and arguments targs. For Named types the resulting instance will be // unexpanded. -func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type) Type { +func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type, env *Environment) Type { switch t := typ.(type) { case *Named: - h := typeHash(t, targs) - if check != nil { - // typ may already have been instantiated with identical type arguments. - // In that case, re-use the existing instance. - if named := check.typMap[h]; named != nil { + var h string + if env != nil { + h = env.TypeHash(t, targs) + // typ may already have been instantiated with identical type arguments. In + // that case, re-use the existing instance. + if named := env.typeForHash(h, nil); named != nil { return named } } tname := NewTypeName(pos, t.obj.pkg, t.obj.name, nil) - named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is loaded + named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is resolved named.targs = NewTypeList(targs) - named.instPos = &pos - if check != nil { - check.typMap[h] = named + named.resolver = func(env *Environment, n *Named) (*TypeParamList, Type, []*Func) { + return expandNamed(env, n, pos) + } + if env != nil { + // It's possible that we've lost a race to add named to the environment. + // In this case, use whichever instance is recorded in the environment. + named = env.typeForHash(h, named) } return named case *Signature: - tparams := t.TParams() + tparams := t.TypeParams() if !check.validateTArgLen(pos, tparams.Len(), len(targs)) { return Typ[Invalid] } if tparams.Len() == 0 { return typ // nothing to do (minor optimization) } - sig := check.subst(pos, typ, makeSubstMap(tparams.list(), targs), nil).(*Signature) + sig := check.subst(pos, typ, makeSubstMap(tparams.list(), targs), env).(*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). diff --git a/src/cmd/compile/internal/types2/instantiate_test.go b/src/cmd/compile/internal/types2/instantiate_test.go new file mode 100644 index 00000000000..69a26491cb5 --- /dev/null +++ b/src/cmd/compile/internal/types2/instantiate_test.go @@ -0,0 +1,62 @@ +// 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 types2_test + +import ( + . "cmd/compile/internal/types2" + "testing" +) + +func TestInstantiateEquality(t *testing.T) { + const src = genericPkg + "p; type T[P any] int" + pkg, err := pkgFor(".", src, nil) + if err != nil { + t.Fatal(err) + } + T := pkg.Scope().Lookup("T").Type().(*Named) + // Instantiating the same type twice should result in pointer-equivalent + // instances. + env := NewEnvironment() + res1, err := Instantiate(env, T, []Type{Typ[Int]}, false) + if err != nil { + t.Fatal(err) + } + res2, err := Instantiate(env, T, []Type{Typ[Int]}, false) + if err != nil { + t.Fatal(err) + } + if res1 != res2 { + t.Errorf("first instance (%s) not pointer-equivalent to second instance (%s)", res1, res2) + } +} +func TestInstantiateNonEquality(t *testing.T) { + const src = genericPkg + "p; type T[P any] int" + pkg1, err := pkgFor(".", src, nil) + if err != nil { + t.Fatal(err) + } + pkg2, err := pkgFor(".", src, nil) + if err != nil { + t.Fatal(err) + } + // We consider T1 and T2 to be distinct types, so their instances should not + // be deduplicated by the environment. + T1 := pkg1.Scope().Lookup("T").Type().(*Named) + T2 := pkg2.Scope().Lookup("T").Type().(*Named) + env := NewEnvironment() + res1, err := Instantiate(env, T1, []Type{Typ[Int]}, false) + if err != nil { + t.Fatal(err) + } + res2, err := Instantiate(env, 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) + } +} diff --git a/src/cmd/compile/internal/types2/interface.go b/src/cmd/compile/internal/types2/interface.go index e57158d2d50..340df51524b 100644 --- a/src/cmd/compile/internal/types2/interface.go +++ b/src/cmd/compile/internal/types2/interface.go @@ -11,6 +11,7 @@ import "cmd/compile/internal/syntax" // An Interface represents an interface type. type Interface struct { + check *Checker // for error reporting; nil once type set is computed obj *TypeName // corresponding declared object; or nil (for better error messages) methods []*Func // ordered list of explicitly declared methods embeddeds []Type // ordered list of explicitly embedded elements @@ -21,7 +22,7 @@ type Interface struct { } // typeSet returns the type set for interface t. -func (t *Interface) typeSet() *_TypeSet { return computeInterfaceTypeSet(nil, nopos, t) } +func (t *Interface) typeSet() *_TypeSet { return computeInterfaceTypeSet(t.check, nopos, t) } // emptyInterface represents the empty interface var emptyInterface = Interface{complete: true, tset: &topTypeSet} @@ -198,7 +199,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType } // All methods and embedded elements for this interface are collected; - // i.e., this interface is may be used in a type set computation. + // i.e., this interface may be used in a type set computation. ityp.complete = true if len(ityp.methods) == 0 && len(ityp.embeddeds) == 0 { @@ -214,7 +215,15 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType // 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. - check.later(func() { computeInterfaceTypeSet(check, iface.Pos(), ityp) }) + // + // 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 + }) } func flattenUnion(list []syntax.Expr, x syntax.Expr) []syntax.Expr { diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go index d0718e51e2d..0e7a2b70e2e 100644 --- a/src/cmd/compile/internal/types2/lookup.go +++ b/src/cmd/compile/internal/types2/lookup.go @@ -122,7 +122,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o seen[named] = true // look for a matching attached method - named.load() + named.resolve(nil) if i, m := lookupMethod(named.methods, pkg, name); m != nil { // potential match // caution: method may not have a proper signature yet @@ -321,10 +321,10 @@ 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 ftyp.TParams().Len() != mtyp.TParams().Len() { + if ftyp.TypeParams().Len() != mtyp.TypeParams().Len() { return m, f } - if !acceptMethodTypeParams && ftyp.TParams().Len() > 0 { + if !acceptMethodTypeParams && ftyp.TypeParams().Len() > 0 { panic("method with type parameters") } @@ -334,7 +334,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // TODO(gri) is this always correct? what about type bounds? // (Alternative is to rename/subst type parameters and compare.) u := newUnifier(true) - u.x.init(ftyp.TParams().list()) + u.x.init(ftyp.TypeParams().list()) if !u.unify(ftyp, mtyp) { return m, f } @@ -373,10 +373,10 @@ 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 ftyp.TParams().Len() != mtyp.TParams().Len() { + if ftyp.TypeParams().Len() != mtyp.TypeParams().Len() { return m, f } - if !acceptMethodTypeParams && ftyp.TParams().Len() > 0 { + if !acceptMethodTypeParams && ftyp.TypeParams().Len() > 0 { panic("method with type parameters") } @@ -387,17 +387,17 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // 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 && Vn.TParams().Len() > 0 { + if Vn != nil && Vn.TypeParams().Len() > 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().list()) != Vn.targs.Len() { + if len(ftyp.RecvTypeParams().list()) != Vn.targs.Len() { return } - ftyp = check.subst(nopos, ftyp, makeSubstMap(ftyp.RParams().list(), Vn.targs.list()), nil).(*Signature) + ftyp = check.subst(nopos, ftyp, makeSubstMap(ftyp.RecvTypeParams().list(), Vn.targs.list()), nil).(*Signature) } // If the methods have type parameters we don't care whether they @@ -406,7 +406,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // TODO(gri) is this always correct? what about type bounds? // (Alternative is to rename/subst type parameters and compare.) u := newUnifier(true) - if ftyp.TParams().Len() > 0 { + if ftyp.TypeParams().Len() > 0 { // We reach here only if we accept method type parameters. // In this case, unification must consider any receiver // and method type parameters as "free" type parameters. @@ -416,9 +416,9 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // unimplemented call so that we test this code if we // enable method type parameters. unimplemented() - u.x.init(append(ftyp.RParams().list(), ftyp.TParams().list()...)) + u.x.init(append(ftyp.RecvTypeParams().list(), ftyp.TypeParams().list()...)) } else { - u.x.init(ftyp.RParams().list()) + u.x.init(ftyp.RecvTypeParams().list()) } if !u.unify(ftyp, mtyp) { return m, f diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go index a76e69fcf17..c844012e39e 100644 --- a/src/cmd/compile/internal/types2/named.go +++ b/src/cmd/compile/internal/types2/named.go @@ -12,18 +12,18 @@ import ( // A Named represents a named (defined) type. type Named struct { check *Checker - info typeInfo // for cycle detection - 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 from (for cycle reporting) - underlying Type // possibly a *Named during setup; never a *Named once set up completely - instPos *syntax.Pos // position information for lazy instantiation, or nil - tparams *TParamList // type parameters, or nil - targs *TypeList // type arguments (after instantiation), or nil - methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily - - resolve func(*Named) ([]*TypeParam, Type, []*Func) - once sync.Once + info typeInfo // for cycle detection + 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 from (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 []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily + + // resolver may be provided to lazily resolve type parameters, underlying, and methods. + resolver func(*Environment, *Named) (tparams *TypeParamList, underlying Type, methods []*Func) + 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. @@ -36,49 +36,28 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { return (*Checker)(nil).newNamed(obj, nil, underlying, nil, methods) } -func (t *Named) load() *Named { - // If t is an instantiated type, it derives its methods and tparams from its - // base type. Since we expect type parameters and methods to be set after a - // call to load, we must load the base and copy here. - // - // underlying is set when t is expanded. - // - // By convention, a type instance is loaded iff its tparams are set. - if t.targs.Len() > 0 && t.tparams == nil { - t.orig.load() - t.tparams = t.orig.tparams - t.methods = t.orig.methods - } - if t.resolve == nil { +func (t *Named) resolve(env *Environment) *Named { + if t.resolver == nil { return t } t.once.Do(func() { - // TODO(mdempsky): Since we're passing t to resolve anyway + // 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 - // SetTParams, SetUnderlying, and AddMethod instead? Those - // methods would need to support reentrant calls though. It would + // 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 SetTParams). - - tparams, underlying, methods := t.resolve(t) - - switch underlying.(type) { - case nil, *Named: - panic("invalid underlying type") - } - - t.tparams = bindTParams(tparams) - t.underlying = underlying - t.methods = methods + // (like SetTypeParams). + t.tparams, t.underlying, t.methods = t.resolver(env, 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 *TParamList, methods []*Func) *Named { +func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tparams *TypeParamList, methods []*Func) *Named { typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, tparams: tparams, methods: methods} if typ.orig == nil { typ.orig = typ @@ -119,21 +98,21 @@ func (t *Named) Orig() *Named { return t.orig } // 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. +// 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) TParams() *TParamList { return t.load().tparams } +func (t *Named) TypeParams() *TypeParamList { return t.resolve(nil).tparams } -// SetTParams sets the type parameters of the named type t. -func (t *Named) SetTParams(tparams []*TypeParam) { t.load().tparams = bindTParams(tparams) } +// SetTypeParams sets the type parameters of the named type t. +func (t *Named) SetTypeParams(tparams []*TypeParam) { t.resolve(nil).tparams = bindTParams(tparams) } -// TArgs returns the type arguments used to instantiate the named type t. -func (t *Named) TArgs() *TypeList { return t.targs } +// 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 len(t.load().methods) } +func (t *Named) NumMethods() int { return len(t.resolve(nil).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.load().methods[i] } +func (t *Named) Method(i int) *Func { return t.resolve(nil).methods[i] } // SetUnderlying sets the underlying type and marks t as complete. func (t *Named) SetUnderlying(underlying Type) { @@ -143,18 +122,18 @@ func (t *Named) SetUnderlying(underlying Type) { if _, ok := underlying.(*Named); ok { panic("underlying type must not be *Named") } - t.load().underlying = underlying + t.resolve(nil).underlying = underlying } // AddMethod adds method m unless it is already in the method list. func (t *Named) AddMethod(m *Func) { - t.load() + t.resolve(nil) if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 { t.methods = append(t.methods, m) } } -func (t *Named) Underlying() Type { return t.load().expand(nil).underlying } +func (t *Named) Underlying() Type { return t.resolve(nil).underlying } func (t *Named) String() string { return TypeString(t, nil) } // ---------------------------------------------------------------------------- @@ -240,37 +219,36 @@ func (n *Named) setUnderlying(typ Type) { } } -// expand ensures that the underlying type of n is instantiated. +// expandNamed ensures that the underlying type of n is instantiated. // The underlying type will be Typ[Invalid] if there was an error. -func (n *Named) expand(typMap map[string]*Named) *Named { - if n.instPos != nil { - // n must be loaded before instantiation, in order to have accurate - // tparams. This is done implicitly by the call to n.TParams, but making it - // explicit is harmless: load is idempotent. - n.load() - var u Type - if n.check.validateTArgLen(*n.instPos, n.tparams.Len(), n.targs.Len()) { - if typMap == nil { - if n.check != nil { - typMap = n.check.typMap - } else { - // If we're instantiating lazily, we might be outside the scope of a - // type-checking pass. In that case we won't have a pre-existing - // typMap, but don't want to create a duplicate of the current instance - // in the process of expansion. - h := typeHash(n.orig, n.targs.list()) - typMap = map[string]*Named{h: n} - } +func expandNamed(env *Environment, n *Named, instPos syntax.Pos) (*TypeParamList, Type, []*Func) { + n.orig.resolve(env) + + var u Type + if n.check.validateTArgLen(instPos, n.orig.tparams.Len(), n.targs.Len()) { + // TODO(rfindley): handling an optional Checker and Environment here (and + // in subst) feels overly complicated. Can we simplify? + if env == nil { + if n.check != nil { + env = n.check.conf.Environment + } else { + // If we're instantiating lazily, we might be outside the scope of a + // type-checking pass. In that case we won't have a pre-existing + // environment, but don't want to create a duplicate of the current + // instance in the process of expansion. + env = NewEnvironment() } - u = n.check.subst(*n.instPos, n.orig.underlying, makeSubstMap(n.TParams().list(), n.targs.list()), typMap) - } else { - u = Typ[Invalid] + h := env.TypeHash(n.orig, n.targs.list()) + // add the instance to the environment to avoid infinite recursion. + // addInstance may return a different, existing instance, but we + // shouldn't return that instance from expand. + env.typeForHash(h, n) } - n.underlying = u - n.fromRHS = u - n.instPos = nil + u = n.check.subst(instPos, n.orig.underlying, makeSubstMap(n.orig.tparams.list(), n.targs.list()), env) + } else { + u = Typ[Invalid] } - return n + return n.orig.tparams, u, n.orig.methods } // safeUnderlying returns the underlying of typ without expanding instances, to @@ -279,7 +257,7 @@ func (n *Named) expand(typMap map[string]*Named) *Named { // TODO(rfindley): eliminate this function or give it a better name. func safeUnderlying(typ Type) Type { if t, _ := typ.(*Named); t != nil { - return t.load().underlying + return t.resolve(nil).underlying } return typ.Underlying() } diff --git a/src/cmd/compile/internal/types2/object.go b/src/cmd/compile/internal/types2/object.go index a3f5f913aa5..540cb3f44f8 100644 --- a/src/cmd/compile/internal/types2/object.go +++ b/src/cmd/compile/internal/types2/object.go @@ -278,9 +278,21 @@ func NewTypeName(pos syntax.Pos, pkg *Package, name string, typ Type) *TypeName // NewTypeNameLazy returns a new defined type like NewTypeName, but it // lazily calls resolve to finish constructing the Named object. -func NewTypeNameLazy(pos syntax.Pos, pkg *Package, name string, resolve func(named *Named) (tparams []*TypeParam, underlying Type, methods []*Func)) *TypeName { +func NewTypeNameLazy(pos syntax.Pos, pkg *Package, name string, load func(named *Named) (tparams []*TypeParam, underlying Type, methods []*Func)) *TypeName { obj := NewTypeName(pos, pkg, name, nil) - NewNamed(obj, nil, nil).resolve = resolve + + resolve := func(_ *Environment, t *Named) (*TypeParamList, Type, []*Func) { + tparams, underlying, methods := load(t) + + switch underlying.(type) { + case nil, *Named: + panic(fmt.Sprintf("invalid underlying type %T", t.underlying)) + } + + return bindTParams(tparams), underlying, methods + } + + NewNamed(obj, nil, nil).resolver = resolve return obj } @@ -475,8 +487,8 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) { if _, ok := typ.(*Basic); ok { return } - if named, _ := typ.(*Named); named != nil && named.TParams().Len() > 0 { - newTypeWriter(buf, qf).tParamList(named.TParams().list()) + if named, _ := typ.(*Named); named != nil && named.TypeParams().Len() > 0 { + newTypeWriter(buf, qf).tParamList(named.TypeParams().list()) } if tname.IsAlias() { buf.WriteString(" =") diff --git a/src/cmd/compile/internal/types2/predicates.go b/src/cmd/compile/internal/types2/predicates.go index 3ccafef990c..74ad3da72cb 100644 --- a/src/cmd/compile/internal/types2/predicates.go +++ b/src/cmd/compile/internal/types2/predicates.go @@ -21,7 +21,7 @@ func isNamed(typ Type) bool { 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.targs == nil && named.TParams() != nil + return named != nil && named.obj != nil && named.targs == nil && named.TypeParams() != nil } func is(typ Type, what BasicInfo) bool { @@ -220,11 +220,18 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool { // parameter names. if y, ok := y.(*Signature); ok { return x.variadic == y.variadic && - identicalTParams(x.TParams().list(), y.TParams().list(), cmpTags, p) && + identicalTParams(x.TypeParams().list(), y.TypeParams().list(), cmpTags, p) && identical(x.params, y.params, cmpTags, p) && identical(x.results, y.results, cmpTags, p) } + case *Union: + if y, _ := y.(*Union); y != nil { + xset := computeUnionTypeSet(nil, nopos, x) + yset := computeUnionTypeSet(nil, 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: @@ -302,11 +309,8 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool { // Two named types are identical if their type names originate // in the same type declaration. if y, ok := y.(*Named); ok { - x.expand(nil) - y.expand(nil) - - xargs := x.TArgs().list() - yargs := y.TArgs().list() + xargs := x.TypeArgs().list() + yargs := y.TypeArgs().list() if len(xargs) != len(yargs) { return false diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go index a7d0db624ce..e3186f5eed2 100644 --- a/src/cmd/compile/internal/types2/signature.go +++ b/src/cmd/compile/internal/types2/signature.go @@ -19,13 +19,13 @@ type Signature struct { // 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 *TParamList // receiver type parameters from left to right, or nil - tparams *TParamList // 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) + 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, 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, @@ -53,17 +53,17 @@ func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature { // 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() *TParamList { return s.tparams } +// TypeParams returns the type parameters of signature s, or nil. +func (s *Signature) TypeParams() *TypeParamList { return s.tparams } -// SetTParams sets the type parameters of signature s. -func (s *Signature) SetTParams(tparams []*TypeParam) { s.tparams = bindTParams(tparams) } +// SetTypeParams sets the type parameters of signature s. +func (s *Signature) SetTypeParams(tparams []*TypeParam) { s.tparams = bindTParams(tparams) } -// RParams returns the receiver type parameters of signature s, or nil. -func (s *Signature) RParams() *TParamList { return s.rparams } +// RecvTypeParams returns the receiver type parameters of signature s, or nil. +func (s *Signature) RecvTypeParams() *TypeParamList { return s.rparams } -// SetRParams sets the receiver type params of signature s. -func (s *Signature) SetRParams(rparams []*TypeParam) { s.rparams = bindTParams(rparams) } +// SetRecvTypeParams sets the receiver type params of signature s. +func (s *Signature) SetRecvTypeParams(rparams []*TypeParam) { s.rparams = bindTParams(rparams) } // Params returns the parameters of signature s, or nil. func (s *Signature) Params() *Tuple { return s.params } @@ -133,19 +133,19 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams [] // again when we type-check the signature. // TODO(gri) maybe the receiver should be marked as invalid instead? if recv, _ := check.genericType(rname, false).(*Named); recv != nil { - recvTParams = recv.TParams().list() + 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.RParams().Len() == len(recvTParams) { + if sig.RecvTypeParams().Len() == len(recvTParams) { // We have a list of *TypeNames but we need a list of Types. - list := make([]Type, sig.RParams().Len()) - for i, t := range sig.RParams().list() { + list := make([]Type, sig.RecvTypeParams().Len()) + for i, t := range sig.RecvTypeParams().list() { list[i] = t } smap := makeSubstMap(recvTParams, list) - for i, tpar := range sig.RParams().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 @@ -210,10 +210,10 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams [] var err string switch T := rtyp.(type) { case *Named: - T.expand(nil) + T.resolve(check.conf.Environment) // The receiver type may be an instantiated type referred to // by an alias (which cannot have receiver parameters for now). - if T.TArgs() != nil && sig.RParams() == nil { + if T.TypeArgs() != nil && sig.RecvTypeParams() == nil { check.errorf(recv.pos, "cannot define methods on instantiated type %s", recv.typ) break } diff --git a/src/cmd/compile/internal/types2/sizeof_test.go b/src/cmd/compile/internal/types2/sizeof_test.go index 5be369d8438..a7f1185fa89 100644 --- a/src/cmd/compile/internal/types2/sizeof_test.go +++ b/src/cmd/compile/internal/types2/sizeof_test.go @@ -28,10 +28,10 @@ func TestSizeof(t *testing.T) { {Tuple{}, 12, 24}, {Signature{}, 28, 56}, {Union{}, 16, 32}, - {Interface{}, 40, 80}, + {Interface{}, 44, 88}, {Map{}, 16, 32}, {Chan{}, 12, 24}, - {Named{}, 72, 136}, + {Named{}, 68, 128}, {TypeParam{}, 28, 48}, {term{}, 12, 24}, {top{}, 0, 0}, diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go index c67538d4f0f..87c1d7872bf 100644 --- a/src/cmd/compile/internal/types2/subst.go +++ b/src/cmd/compile/internal/types2/subst.go @@ -6,10 +6,7 @@ package types2 -import ( - "bytes" - "cmd/compile/internal/syntax" -) +import "cmd/compile/internal/syntax" type substMap map[*TypeParam]Type @@ -40,8 +37,8 @@ func (m substMap) lookup(tpar *TypeParam) Type { // incoming type. If a substitution took place, the result type is different // from the incoming type. // -// If the given typMap is non-nil, it is used in lieu of check.typMap. -func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, typMap map[string]*Named) Type { +// If the given environment is non-nil, it is used in lieu of check.env. +func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, env *Environment) Type { if smap.empty() { return typ } @@ -61,27 +58,27 @@ func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, typMap map[ if check != nil { subst.check = check - if typMap == nil { - typMap = check.typMap + if env == nil { + env = check.conf.Environment } } - if typMap == nil { + if env == nil { // If we don't have a *Checker and its global type map, // use a local version. Besides avoiding duplicate work, // the type map prevents infinite recursive substitution // for recursive types (example: type T[P any] *T[P]). - typMap = make(map[string]*Named) + env = NewEnvironment() } - subst.typMap = typMap + subst.env = env return subst.typ(typ) } type subster struct { - pos syntax.Pos - smap substMap - check *Checker // nil if called via Instantiate - typMap map[string]*Named + pos syntax.Pos + smap substMap + check *Checker // nil if called via Instantiate + env *Environment } func (subst *subster) typ(typ Type) Type { @@ -182,13 +179,19 @@ func (subst *subster) typ(typ Type) Type { } } - if t.TParams().Len() == 0 { + // 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 - assert(t.targs.Len() == t.TParams().Len()) + assert(t.targs.Len() == t.orig.TypeParams().Len()) // already instantiated dump(">>> %s already instantiated", t) @@ -201,7 +204,7 @@ func (subst *subster) typ(typ Type) Type { if new_targ != targ { dump(">>> substituted %d targ %s => %s", i, targ, new_targ) if newTArgs == nil { - newTArgs = make([]Type, t.TParams().Len()) + newTArgs = make([]Type, t.orig.TypeParams().Len()) copy(newTArgs, t.targs.list()) } newTArgs[i] = new_targ @@ -214,32 +217,29 @@ func (subst *subster) typ(typ Type) Type { } // before creating a new named type, check if we have this one already - h := typeHash(t, newTArgs) + h := subst.env.TypeHash(t.orig, newTArgs) dump(">>> new type hash: %s", h) - if named, found := subst.typMap[h]; found { + if named := subst.env.typeForHash(h, nil); named != nil { dump(">>> found %s", named) return named } - // Create a new named type and populate typMap 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. - tname := NewTypeName(subst.pos, t.obj.pkg, t.obj.name, nil) - t.load() - // It's ok to provide a nil *Checker because the newly created type - // doesn't need to be (lazily) expanded; it's expanded below. - named := (*Checker)(nil).newNamed(tname, t.orig, nil, t.tparams, t.methods) // t is loaded, so tparams and methods are available - named.targs = NewTypeList(newTArgs) - subst.typMap[h] = named - t.expand(subst.typMap) // must happen after typMap update to avoid infinite recursion - - // do the substitution - dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, newTArgs) - named.underlying = subst.typOrNil(t.underlying) - dump(">>> underlying: %v", named.underlying) + t.orig.resolve(subst.env) + // Create a new instance and populate the environment 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. + named := subst.check.instance(subst.pos, t.orig, newTArgs, subst.env).(*Named) + // TODO(rfindley): we probably don't need to resolve here. Investigate if + // this can be removed. + named.resolve(subst.env) assert(named.underlying != nil) - named.fromRHS = named.underlying // for consistency, though no cycle detection is necessary + + // 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. return named @@ -253,35 +253,6 @@ func (subst *subster) typ(typ Type) Type { return typ } -// typeHash returns a string representation of typ, which can be used as an exact -// type hash: types that are identical produce identical string representations. -// If typ is a *Named type and targs is not empty, typ is printed as if it were -// instantiated with targs. -func typeHash(typ Type, targs []Type) string { - assert(typ != nil) - var buf bytes.Buffer - - h := newTypeHasher(&buf) - if named, _ := typ.(*Named); named != nil && len(targs) > 0 { - // Don't use WriteType because we need to use the provided targs - // and not any targs that might already be with the *Named type. - h.typeName(named.obj) - h.typeList(targs) - } else { - assert(targs == nil) - h.typ(typ) - } - - if debug { - // there should be no instance markers in type hashes - for _, b := range buf.Bytes() { - assert(b != instanceMarker) - } - } - - 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. diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43527.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43527.go2 new file mode 100644 index 00000000000..e4bcee51fe5 --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43527.go2 @@ -0,0 +1,16 @@ +// 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 + +const L = 10 + +type ( + _ [L]struct{} + _ [A /* ERROR undeclared name A for array length */ ]struct{} + _ [B /* ERROR not an expression */ ]struct{} + _[A any] struct{} + + B int +) diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47887.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47887.go2 new file mode 100644 index 00000000000..4c4fc2fda8f --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47887.go2 @@ -0,0 +1,28 @@ +// 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 + +type Fooer[t any] interface { + foo(Barer[t]) +} +type Barer[t any] interface { + bar(Bazer[t]) +} +type Bazer[t any] interface { + Fooer[t] + baz(t) +} + +type Int int + +func (n Int) baz(int) {} +func (n Int) foo(b Barer[int]) { b.bar(n) } + +type F[t any] interface { f(G[t]) } +type G[t any] interface { g(H[t]) } +type H[t any] interface { F[t] } + +type T struct{} +func (n T) f(b G[T]) { b.g(n) } diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2 index 56e90942ab6..2c4b6610fec 100644 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2 +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2 @@ -5,4 +5,4 @@ package p // don't crash -func T /* ERROR missing */ [P /* ERROR named */ ] m /* ERROR m */ () /* ERROR \) */ { /* ERROR { */ } /* ERROR } */ +func T /* ERROR missing */ [P] /* ERROR missing */ m /* ERROR unexpected */ () /* ERROR \) */ { /* ERROR { */ } /* ERROR } */ diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48234.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48234.go2 new file mode 100644 index 00000000000..e069930c42d --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48234.go2 @@ -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 + +var _ = interface{ + m() + m /* ERROR "duplicate method" */ () +}(nil) diff --git a/src/cmd/compile/internal/types2/type.go b/src/cmd/compile/internal/types2/type.go index ca5ecdc4344..400d6f71287 100644 --- a/src/cmd/compile/internal/types2/type.go +++ b/src/cmd/compile/internal/types2/type.go @@ -115,7 +115,7 @@ func asInterface(t Type) *Interface { func asNamed(t Type) *Named { e, _ := t.(*Named) if e != nil { - e.expand(nil) + e.resolve(nil) } return e } diff --git a/src/cmd/compile/internal/types2/typelists.go b/src/cmd/compile/internal/types2/typelists.go index f313ea310e9..ababe85909d 100644 --- a/src/cmd/compile/internal/types2/typelists.go +++ b/src/cmd/compile/internal/types2/typelists.go @@ -6,20 +6,20 @@ package types2 import "bytes" -// TParamList holds a list of type parameters. -type TParamList struct{ tparams []*TypeParam } +// 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 *TParamList) Len() int { return len(l.list()) } +func (l *TypeParamList) Len() int { return len(l.list()) } // At returns the i'th type parameter in the list. -func (l *TParamList) At(i int) *TypeParam { return l.tparams[i] } +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 -// TParamList instead. -func (l *TParamList) list() []*TypeParam { +// TypeParamList instead. +func (l *TypeParamList) list() []*TypeParam { if l == nil { return nil } @@ -66,7 +66,7 @@ func (l *TypeList) String() string { // ---------------------------------------------------------------------------- // Implementation -func bindTParams(list []*TypeParam) *TParamList { +func bindTParams(list []*TypeParam) *TypeParamList { if len(list) == 0 { return nil } @@ -76,5 +76,5 @@ func bindTParams(list []*TypeParam) *TParamList { } typ.index = i } - return &TParamList{tparams: list} + return &TypeParamList{tparams: list} } diff --git a/src/cmd/compile/internal/types2/typeparam.go b/src/cmd/compile/internal/types2/typeparam.go index 445337fee88..505596f5719 100644 --- a/src/cmd/compile/internal/types2/typeparam.go +++ b/src/cmd/compile/internal/types2/typeparam.go @@ -29,18 +29,22 @@ type TypeParam struct { func (t *TypeParam) Obj() *TypeName { return t.obj } // NewTypeParam returns a new TypeParam. Type parameters may be set on a Named -// or Signature type by calling SetTParams. Setting a type parameter on more +// or Signature type by calling SetTypeParams. Setting a type parameter on more // than one type will result in a panic. // -// The bound argument can be nil, and set later via SetBound. -func (check *Checker) NewTypeParam(obj *TypeName, bound Type) *TypeParam { +// 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: bound} + typ := &TypeParam{check: check, id: id, obj: obj, index: -1, bound: constraint} if obj.typ == nil { obj.typ = typ } diff --git a/src/cmd/compile/internal/types2/typestring.go b/src/cmd/compile/internal/types2/typestring.go index 6083955306b..bdafcf883db 100644 --- a/src/cmd/compile/internal/types2/typestring.go +++ b/src/cmd/compile/internal/types2/typestring.go @@ -8,7 +8,7 @@ package types2 import ( "bytes" - "fmt" + "strconv" "unicode/utf8" ) @@ -63,32 +63,45 @@ func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) { newTypeWriter(buf, qf).signature(sig) } -// instanceMarker is the prefix for an instantiated type in unexpanded form. -const instanceMarker = '#' - type typeWriter struct { buf *bytes.Buffer seen map[Type]bool qf Qualifier - hash bool + env *Environment // if non-nil, we are type hashing } func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter { - return &typeWriter{buf, make(map[Type]bool), qf, false} + return &typeWriter{buf, make(map[Type]bool), qf, nil} } -func newTypeHasher(buf *bytes.Buffer) *typeWriter { - return &typeWriter{buf, make(map[Type]bool), nil, true} +func newTypeHasher(buf *bytes.Buffer, env *Environment) *typeWriter { + assert(env != nil) + return &typeWriter{buf, make(map[Type]bool), nil, env} +} + +func (w *typeWriter) byte(b byte) { + if w.env != 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) byte(b byte) { w.buf.WriteByte(b) } -func (w *typeWriter) string(s string) { w.buf.WriteString(s) } -func (w *typeWriter) writef(format string, args ...interface{}) { fmt.Fprintf(w.buf, format, args...) } func (w *typeWriter) error(msg string) { - if w.hash { + if w.env != nil { panic(msg) } - w.string("<" + msg + ">") + w.buf.WriteString("<" + msg + ">") } func (w *typeWriter) typ(typ Type) { @@ -115,7 +128,9 @@ func (w *typeWriter) typ(typ Type) { w.string(t.name) case *Array: - w.writef("[%d]", t.len) + w.byte('[') + w.string(strconv.FormatInt(t.len, 10)) + w.byte(']') w.typ(t.elem) case *Slice: @@ -126,7 +141,7 @@ func (w *typeWriter) typ(typ Type) { w.string("struct{") for i, f := range t.fields { if i > 0 { - w.string("; ") + w.byte(';') } // This doesn't do the right thing for embedded type // aliases where we should print the alias name, not @@ -137,7 +152,11 @@ func (w *typeWriter) typ(typ Type) { } w.typ(f.typ) if tag := t.Tag(i); tag != "" { - w.writef(" %q", tag) + w.byte(' ') + // TODO(gri) If tag contains blanks, replacing them with '#' + // in Environment.TypeHash may produce another tag + // accidentally. + w.string(strconv.Quote(tag)) } } w.byte('}') @@ -175,7 +194,7 @@ func (w *typeWriter) typ(typ Type) { first := true for _, m := range t.methods { if !first { - w.string("; ") + w.byte(';') } first = false w.string(m.name) @@ -183,7 +202,7 @@ func (w *typeWriter) typ(typ Type) { } for _, typ := range t.embeddeds { if !first { - w.string("; ") + w.byte(';') } first = false w.typ(typ) @@ -223,20 +242,14 @@ func (w *typeWriter) typ(typ Type) { } case *Named: - // Instance markers indicate unexpanded instantiated - // types. Write them to aid debugging, but don't write - // them when we need an instance hash: whether a type - // is fully expanded or not doesn't matter for identity. - if !w.hash && t.instPos != nil { - w.byte(instanceMarker) - } + w.typePrefix(t) w.typeName(t.obj) if t.targs != nil { // instantiated type w.typeList(t.targs.list()) - } else if !w.hash && t.TParams().Len() != 0 { // For type hashing, don't need to format the TParams + } else if w.env == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TParams // parameterized type - w.tParamList(t.TParams().list()) + w.tParamList(t.TypeParams().list()) } case *TypeParam: @@ -263,11 +276,20 @@ func (w *typeWriter) typ(typ Type) { } } +// If w.env is non-nil, typePrefix writes a unique prefix for the named type t +// based on the types already observed by w.env. If w.env is nil, it does +// nothing. +func (w *typeWriter) typePrefix(t *Named) { + if w.env != nil { + w.string(strconv.Itoa(w.env.idForType(t))) + } +} + func (w *typeWriter) typeList(list []Type) { w.byte('[') for i, typ := range list { if i > 0 { - w.string(", ") + w.byte(',') } w.typ(typ) } @@ -291,7 +313,7 @@ func (w *typeWriter) tParamList(list []*TypeParam) { w.byte(' ') w.typ(prev) } - w.string(", ") + w.byte(',') } prev = tpar.bound w.typ(tpar) @@ -308,31 +330,6 @@ func (w *typeWriter) typeName(obj *TypeName) { writePackage(w.buf, obj.pkg, w.qf) } w.string(obj.name) - - if w.hash { - // For local defined types, use the (original!) TypeName's scope - // numbers to disambiguate. - if typ, _ := obj.typ.(*Named); typ != nil { - // TODO(gri) Figure out why typ.orig != typ.orig.orig sometimes - // and whether the loop can iterate more than twice. - // (It seems somehow connected to instance types.) - for typ.orig != typ { - typ = typ.orig - } - w.writeScopeNumbers(typ.obj.parent) - } - } -} - -// writeScopeNumbers writes the number sequence for this scope to buf -// in the form ".i.j.k" where i, j, k, etc. stand for scope numbers. -// If a scope is nil or has no parent (such as a package scope), nothing -// is written. -func (w *typeWriter) writeScopeNumbers(s *Scope) { - if s != nil && s.number > 0 { - w.writeScopeNumbers(s.parent) - w.writef(".%d", s.number) - } } func (w *typeWriter) tuple(tup *Tuple, variadic bool) { @@ -340,10 +337,10 @@ func (w *typeWriter) tuple(tup *Tuple, variadic bool) { if tup != nil { for i, v := range tup.vars { if i > 0 { - w.string(", ") + w.byte(',') } // parameter names are ignored for type identity and thus type hashes - if !w.hash && v.name != "" { + if w.env == nil && v.name != "" { w.string(v.name) w.byte(' ') } @@ -371,8 +368,8 @@ func (w *typeWriter) tuple(tup *Tuple, variadic bool) { } func (w *typeWriter) signature(sig *Signature) { - if sig.TParams().Len() != 0 { - w.tParamList(sig.TParams().list()) + if sig.TypeParams().Len() != 0 { + w.tParamList(sig.TypeParams().list()) } w.tuple(sig.params, sig.variadic) @@ -384,7 +381,7 @@ func (w *typeWriter) signature(sig *Signature) { } w.byte(' ') - if n == 1 && (w.hash || sig.results.vars[0].name == "") { + if n == 1 && (w.env != nil || sig.results.vars[0].name == "") { // single unnamed result (if type hashing, name must be ignored) w.typ(sig.results.vars[0].typ) return diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go index f3db3bbba93..5aacb94a605 100644 --- a/src/cmd/compile/internal/types2/typexpr.go +++ b/src/cmd/compile/internal/types2/typexpr.go @@ -428,6 +428,14 @@ func (check *Checker) instantiatedType(x syntax.Expr, targsx []syntax.Expr, def // and returns the constant length >= 0, or a value < 0 // to indicate an error (and thus an unknown length). func (check *Checker) arrayLength(e syntax.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.(*syntax.Name); name != nil && check.lookup(name.Value) == nil { + check.errorf(name, "undeclared name %s for array length", name.Value) + return -1 + } + var x operand check.expr(&x, e) if x.mode != constant_ { @@ -436,6 +444,7 @@ func (check *Checker) arrayLength(e syntax.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) { @@ -447,6 +456,7 @@ func (check *Checker) arrayLength(e syntax.Expr) int64 { } } } + check.errorf(&x, "array length %s must be integer", &x) return -1 } diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go index a1e5b3679bf..bb69f0d27bc 100644 --- a/src/cmd/compile/internal/types2/unify.go +++ b/src/cmd/compile/internal/types2/unify.go @@ -428,9 +428,6 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool { case *Named: if y, ok := y.(*Named); ok { - x.expand(nil) - y.expand(nil) - xargs := x.targs.list() yargs := y.targs.list() diff --git a/src/cmd/compile/internal/types2/universe.go b/src/cmd/compile/internal/types2/universe.go index a615b4c8760..af3ab973259 100644 --- a/src/cmd/compile/internal/types2/universe.go +++ b/src/cmd/compile/internal/types2/universe.go @@ -88,7 +88,7 @@ func defPredeclaredTypes() { res := NewVar(nopos, nil, "", Typ[String]) sig := NewSignature(nil, nil, NewTuple(res), false) err := NewFunc(nopos, nil, "Error", sig) - ityp := &Interface{obj, []*Func{err}, nil, nil, true, nil} + ityp := &Interface{nil, obj, []*Func{err}, nil, nil, true, nil} computeInterfaceTypeSet(nil, nopos, ityp) // prevent races due to lazy computation of tset typ := NewNamed(obj, ityp, nil) sig.recv = NewVar(nopos, nil, "", typ) @@ -99,7 +99,7 @@ func defPredeclaredTypes() { { obj := NewTypeName(nopos, nil, "comparable", nil) obj.setColor(black) - ityp := &Interface{obj, nil, nil, nil, true, &_TypeSet{true, nil, allTermlist}} + ityp := &Interface{nil, obj, nil, nil, nil, true, &_TypeSet{true, nil, allTermlist}} NewNamed(obj, ityp, nil) def(obj) } |