mirror of
https://github.com/golang/go
synced 2024-09-30 00:24:29 -06:00
cmd/compile: keep typecheck results in syntax tree
Saves on both space and cost of map operations. Saves about 3% in compile time. name old time/op new time/op delta Template 251ms ± 2% 244ms ± 1% -2.78% (p=0.000 n=8+8) Unicode 149ms ± 5% 135ms ± 2% -9.03% (p=0.000 n=10+10) GoTypes 1.38s ± 1% 1.35s ± 1% -2.29% (p=0.000 n=10+10) Compiler 115ms ± 2% 112ms ± 2% -2.50% (p=0.001 n=10+9) SSA 11.9s ± 0% 11.4s ± 0% -4.04% (p=0.000 n=9+10) Flate 153ms ± 1% 148ms ± 1% -3.32% (p=0.000 n=10+9) GoParser 284ms ± 2% 280ms ± 1% -1.70% (p=0.002 n=10+10) Tar 209ms ± 2% 205ms ± 2% -1.98% (p=0.004 n=9+10) XML 287ms ± 2% 281ms ± 1% -2.06% (p=0.000 n=10+10) LinkCompiler 508ms ± 2% 501ms ± 2% -1.31% (p=0.024 n=9+9) ExternalLinkCompiler 2.66s ± 3% 2.63s ± 4% ~ (p=0.280 n=10+10) LinkWithoutDebugCompiler 338ms ± 3% 330ms ± 3% -2.21% (p=0.009 n=10+10) StdCmd 21.5s ± 1% 20.8s ± 1% -3.27% (p=0.000 n=9+9) [Geo mean] 615ms 597ms -2.91% name old user-time/op new user-time/op delta Template 344ms ± 2% 324ms ± 3% -6.01% (p=0.000 n=9+9) Unicode 215ms ±11% 192ms ± 2% -10.84% (p=0.000 n=10+9) GoTypes 1.99s ± 2% 1.93s ± 2% -2.73% (p=0.000 n=10+10) Compiler 142ms ± 4% 140ms ± 3% -1.89% (p=0.031 n=9+9) SSA 17.4s ± 1% 17.0s ± 5% ~ (p=0.113 n=9+10) Flate 200ms ± 4% 196ms ± 6% ~ (p=0.190 n=10+10) GoParser 388ms ± 3% 378ms ± 4% -2.59% (p=0.004 n=9+10) Tar 278ms ± 8% 277ms ± 2% ~ (p=0.315 n=10+10) XML 387ms ± 2% 381ms ± 2% -1.63% (p=0.005 n=8+8) LinkCompiler 784ms ± 4% 778ms ± 2% ~ (p=0.436 n=10+10) ExternalLinkCompiler 2.45s ± 1% 2.42s ± 1% -1.11% (p=0.001 n=10+9) LinkWithoutDebugCompiler 374ms ± 3% 366ms ± 2% -2.15% (p=0.010 n=10+9) [Geo mean] 600ms 583ms -2.91% Change-Id: I9552a70d6a2ad500e9acd8815762b761be3c2ff9 Reviewed-on: https://go-review.googlesource.com/c/go/+/432897 Reviewed-by: Keith Randall <khr@google.com> Reviewed-by: Robert Griesemer <gri@google.com>
This commit is contained in:
parent
e3ac2152f2
commit
59bc93535b
@ -9,6 +9,7 @@ import (
|
||||
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/syntax"
|
||||
"cmd/compile/internal/typecheck"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/compile/internal/types2"
|
||||
@ -222,7 +223,7 @@ func IncDec(pos src.XPos, op ir.Op, x ir.Node) *ir.AssignOpStmt {
|
||||
return ir.NewAssignOpStmt(pos, op, x, bl)
|
||||
}
|
||||
|
||||
func idealType(tv types2.TypeAndValue) types2.Type {
|
||||
func idealType(tv syntax.TypeAndValue) types2.Type {
|
||||
// The gc backend expects all expressions to have a concrete type, and
|
||||
// types2 mostly satisfies this expectation already. But there are a few
|
||||
// cases where the Go spec doesn't require converting to concrete type,
|
||||
|
@ -59,13 +59,13 @@ func checkFiles(noders []*noder) (posMap, *types2.Package, *types2.Info) {
|
||||
Sizes: &gcSizes{},
|
||||
}
|
||||
info := &types2.Info{
|
||||
Types: make(map[syntax.Expr]types2.TypeAndValue),
|
||||
Defs: make(map[*syntax.Name]types2.Object),
|
||||
Uses: make(map[*syntax.Name]types2.Object),
|
||||
Selections: make(map[*syntax.SelectorExpr]*types2.Selection),
|
||||
Implicits: make(map[syntax.Node]types2.Object),
|
||||
Scopes: make(map[syntax.Node]*types2.Scope),
|
||||
Instances: make(map[*syntax.Name]types2.Instance),
|
||||
StoreTypesInSyntax: true,
|
||||
Defs: make(map[*syntax.Name]types2.Object),
|
||||
Uses: make(map[*syntax.Name]types2.Object),
|
||||
Selections: make(map[*syntax.SelectorExpr]*types2.Selection),
|
||||
Implicits: make(map[syntax.Node]types2.Object),
|
||||
Scopes: make(map[syntax.Node]*types2.Scope),
|
||||
Instances: make(map[*syntax.Name]types2.Instance),
|
||||
// expand as needed
|
||||
}
|
||||
|
||||
@ -390,17 +390,17 @@ func (g *irgen) delayTransform() bool {
|
||||
return g.topFuncIsGeneric
|
||||
}
|
||||
|
||||
func (g *irgen) typeAndValue(x syntax.Expr) types2.TypeAndValue {
|
||||
tv, ok := g.info.Types[x]
|
||||
if !ok {
|
||||
func (g *irgen) typeAndValue(x syntax.Expr) syntax.TypeAndValue {
|
||||
tv := x.GetTypeInfo()
|
||||
if tv.Type == nil {
|
||||
base.FatalfAt(g.pos(x), "missing type for %v (%T)", x, x)
|
||||
}
|
||||
return tv
|
||||
}
|
||||
|
||||
func (g *irgen) type2(x syntax.Expr) types2.Type {
|
||||
tv, ok := g.info.Types[x]
|
||||
if !ok {
|
||||
func (g *irgen) type2(x syntax.Expr) syntax.Type {
|
||||
tv := x.GetTypeInfo()
|
||||
if tv.Type == nil {
|
||||
base.FatalfAt(g.pos(x), "missing type for %v (%T)", x, x)
|
||||
}
|
||||
return tv.Type
|
||||
|
@ -120,16 +120,16 @@ func (pw *pkgWriter) unexpected(what string, p poser) {
|
||||
pw.fatalf(p, "unexpected %s: %v (%T)", what, p, p)
|
||||
}
|
||||
|
||||
func (pw *pkgWriter) typeAndValue(x syntax.Expr) types2.TypeAndValue {
|
||||
tv, ok := pw.info.Types[x]
|
||||
if !ok {
|
||||
func (pw *pkgWriter) typeAndValue(x syntax.Expr) syntax.TypeAndValue {
|
||||
tv := x.GetTypeInfo()
|
||||
if tv.Type == nil {
|
||||
pw.fatalf(x, "missing Types entry: %v", syntax.String(x))
|
||||
}
|
||||
return tv
|
||||
}
|
||||
func (pw *pkgWriter) maybeTypeAndValue(x syntax.Expr) (types2.TypeAndValue, bool) {
|
||||
tv, ok := pw.info.Types[x]
|
||||
return tv, ok
|
||||
func (pw *pkgWriter) maybeTypeAndValue(x syntax.Expr) (syntax.TypeAndValue, bool) {
|
||||
tv := x.GetTypeInfo()
|
||||
return tv, tv.Type != nil
|
||||
}
|
||||
|
||||
// typeOf returns the Type of the given value expression.
|
||||
|
@ -132,6 +132,7 @@ func NewName(pos Pos, value string) *Name {
|
||||
type (
|
||||
Expr interface {
|
||||
Node
|
||||
typeInfo
|
||||
aExpr()
|
||||
}
|
||||
|
||||
@ -308,7 +309,10 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
type expr struct{ node }
|
||||
type expr struct {
|
||||
node
|
||||
typeAndValue // After typechecking, contains the results of typechecking this expression.
|
||||
}
|
||||
|
||||
func (*expr) aExpr() {}
|
||||
|
||||
|
73
src/cmd/compile/internal/syntax/type.go
Normal file
73
src/cmd/compile/internal/syntax/type.go
Normal file
@ -0,0 +1,73 @@
|
||||
// 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 syntax
|
||||
|
||||
import "go/constant"
|
||||
|
||||
// A Type represents a type of Go.
|
||||
// All types implement the Type interface.
|
||||
// (This type originally lived in types2. We moved it here
|
||||
// so we could depend on it from other packages without
|
||||
// introducing a circularity.)
|
||||
type Type interface {
|
||||
// Underlying returns the underlying type of a type.
|
||||
Underlying() Type
|
||||
|
||||
// String returns a string representation of a type.
|
||||
String() string
|
||||
}
|
||||
|
||||
// Expressions in the syntax package provide storage for
|
||||
// the typechecker to record its results. This interface
|
||||
// is the mechanism the typechecker uses to record results,
|
||||
// and clients use to retrieve those results.
|
||||
type typeInfo interface {
|
||||
SetTypeInfo(TypeAndValue)
|
||||
GetTypeInfo() TypeAndValue
|
||||
}
|
||||
|
||||
// A TypeAndValue records the type information, constant
|
||||
// value if known, and various other flags associated with
|
||||
// an expression.
|
||||
// This type is similar to types2.TypeAndValue, but exposes
|
||||
// none of types2's internals.
|
||||
type TypeAndValue struct {
|
||||
Type Type
|
||||
Value constant.Value
|
||||
exprFlags
|
||||
}
|
||||
|
||||
type exprFlags uint8
|
||||
|
||||
func (f exprFlags) IsVoid() bool { return f&1 != 0 }
|
||||
func (f exprFlags) IsType() bool { return f&2 != 0 }
|
||||
func (f exprFlags) IsBuiltin() bool { return f&4 != 0 }
|
||||
func (f exprFlags) IsValue() bool { return f&8 != 0 }
|
||||
func (f exprFlags) IsNil() bool { return f&16 != 0 }
|
||||
func (f exprFlags) Addressable() bool { return f&32 != 0 }
|
||||
func (f exprFlags) Assignable() bool { return f&64 != 0 }
|
||||
func (f exprFlags) HasOk() bool { return f&128 != 0 }
|
||||
|
||||
func (f *exprFlags) SetIsVoid() { *f |= 1 }
|
||||
func (f *exprFlags) SetIsType() { *f |= 2 }
|
||||
func (f *exprFlags) SetIsBuiltin() { *f |= 4 }
|
||||
func (f *exprFlags) SetIsValue() { *f |= 8 }
|
||||
func (f *exprFlags) SetIsNil() { *f |= 16 }
|
||||
func (f *exprFlags) SetAddressable() { *f |= 32 }
|
||||
func (f *exprFlags) SetAssignable() { *f |= 64 }
|
||||
func (f *exprFlags) SetHasOk() { *f |= 128 }
|
||||
|
||||
// a typeAndValue contains the results of typechecking an expression.
|
||||
// It is embedded in expression nodes.
|
||||
type typeAndValue struct {
|
||||
tv TypeAndValue
|
||||
}
|
||||
|
||||
func (x *typeAndValue) SetTypeInfo(tv TypeAndValue) {
|
||||
x.tv = tv
|
||||
}
|
||||
func (x *typeAndValue) GetTypeInfo() TypeAndValue {
|
||||
return x.tv
|
||||
}
|
@ -197,6 +197,12 @@ type Info struct {
|
||||
// qualified identifiers are collected in the Uses map.
|
||||
Types map[syntax.Expr]TypeAndValue
|
||||
|
||||
// If StoreTypesInSyntax is set, type information identical to
|
||||
// that which would be put in the Types map, will be set in
|
||||
// syntax.Expr.TypeAndValue (independently of whether Types
|
||||
// is nil or not).
|
||||
StoreTypesInSyntax bool
|
||||
|
||||
// Instances maps identifiers denoting generic types or functions to their
|
||||
// type arguments and instantiated type.
|
||||
//
|
||||
@ -276,12 +282,24 @@ type Info struct {
|
||||
InitOrder []*Initializer
|
||||
}
|
||||
|
||||
func (info *Info) recordTypes() bool {
|
||||
return info.Types != nil || info.StoreTypesInSyntax
|
||||
}
|
||||
|
||||
// TypeOf returns the type of expression e, or nil if not found.
|
||||
// Precondition: the Types, Uses and Defs maps are populated.
|
||||
// Precondition 1: the Types map is populated or StoreTypesInSynax is set.
|
||||
// Precondition 2: Uses and Defs maps are populated.
|
||||
func (info *Info) TypeOf(e syntax.Expr) Type {
|
||||
if t, ok := info.Types[e]; ok {
|
||||
return t.Type
|
||||
if info.Types != nil {
|
||||
if t, ok := info.Types[e]; ok {
|
||||
return t.Type
|
||||
}
|
||||
} else if info.StoreTypesInSyntax {
|
||||
if tv := e.GetTypeInfo(); tv.Type != nil {
|
||||
return tv.Type
|
||||
}
|
||||
}
|
||||
|
||||
if id, _ := e.(*syntax.Name); id != nil {
|
||||
if obj := info.ObjectOf(id); obj != nil {
|
||||
return obj.Type()
|
||||
|
@ -115,7 +115,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
return
|
||||
}
|
||||
if t := coreString(x.typ); t != nil && isString(t) {
|
||||
if check.Types != nil {
|
||||
if check.recordTypes() {
|
||||
sig := makeSig(S, S, x.typ)
|
||||
sig.variadic = true
|
||||
check.recordBuiltinType(call.Fun, sig)
|
||||
@ -147,7 +147,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
|
||||
x.mode = value
|
||||
x.typ = S
|
||||
if check.Types != nil {
|
||||
if check.recordTypes() {
|
||||
check.recordBuiltinType(call.Fun, sig)
|
||||
}
|
||||
|
||||
@ -223,7 +223,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
}
|
||||
|
||||
// record the signature before changing x.typ
|
||||
if check.Types != nil && mode != constant_ {
|
||||
if check.recordTypes() && mode != constant_ {
|
||||
check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ))
|
||||
}
|
||||
|
||||
@ -248,7 +248,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
return
|
||||
}
|
||||
x.mode = novalue
|
||||
if check.Types != nil {
|
||||
if check.recordTypes() {
|
||||
check.recordBuiltinType(call.Fun, makeSig(nil, x.typ))
|
||||
}
|
||||
|
||||
@ -340,7 +340,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
x.mode = value
|
||||
}
|
||||
|
||||
if check.Types != nil && x.mode != constant_ {
|
||||
if check.recordTypes() && x.mode != constant_ {
|
||||
check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ, x.typ))
|
||||
}
|
||||
|
||||
@ -371,7 +371,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
return
|
||||
}
|
||||
|
||||
if check.Types != nil {
|
||||
if check.recordTypes() {
|
||||
check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ, y.typ))
|
||||
}
|
||||
x.mode = value
|
||||
@ -410,7 +410,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
}
|
||||
|
||||
x.mode = novalue
|
||||
if check.Types != nil {
|
||||
if check.recordTypes() {
|
||||
check.recordBuiltinType(call.Fun, makeSig(nil, map_, key))
|
||||
}
|
||||
|
||||
@ -476,7 +476,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
x.mode = value
|
||||
}
|
||||
|
||||
if check.Types != nil && x.mode != constant_ {
|
||||
if check.recordTypes() && x.mode != constant_ {
|
||||
check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ))
|
||||
}
|
||||
|
||||
@ -525,7 +525,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
}
|
||||
x.mode = value
|
||||
x.typ = T
|
||||
if check.Types != nil {
|
||||
if check.recordTypes() {
|
||||
check.recordBuiltinType(call.Fun, makeSig(x.typ, types...))
|
||||
}
|
||||
|
||||
@ -539,7 +539,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
|
||||
x.mode = value
|
||||
x.typ = &Pointer{base: T}
|
||||
if check.Types != nil {
|
||||
if check.recordTypes() {
|
||||
check.recordBuiltinType(call.Fun, makeSig(x.typ, T))
|
||||
}
|
||||
|
||||
@ -564,7 +564,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
}
|
||||
|
||||
x.mode = novalue
|
||||
if check.Types != nil {
|
||||
if check.recordTypes() {
|
||||
check.recordBuiltinType(call.Fun, makeSig(nil, &emptyInterface))
|
||||
}
|
||||
|
||||
@ -588,7 +588,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
}
|
||||
|
||||
x.mode = novalue
|
||||
if check.Types != nil {
|
||||
if check.recordTypes() {
|
||||
check.recordBuiltinType(call.Fun, makeSig(nil, params...))
|
||||
}
|
||||
|
||||
@ -596,7 +596,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
// recover() interface{}
|
||||
x.mode = value
|
||||
x.typ = &emptyInterface
|
||||
if check.Types != nil {
|
||||
if check.recordTypes() {
|
||||
check.recordBuiltinType(call.Fun, makeSig(x.typ))
|
||||
}
|
||||
|
||||
@ -620,7 +620,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
|
||||
x.mode = value
|
||||
x.typ = Typ[UnsafePointer]
|
||||
if check.Types != nil {
|
||||
if check.recordTypes() {
|
||||
check.recordBuiltinType(call.Fun, makeSig(x.typ, x.typ, y.typ))
|
||||
}
|
||||
|
||||
@ -633,7 +633,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
|
||||
if hasVarSize(x.typ, nil) {
|
||||
x.mode = value
|
||||
if check.Types != nil {
|
||||
if check.recordTypes() {
|
||||
check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], x.typ))
|
||||
}
|
||||
} else {
|
||||
@ -697,7 +697,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
// arranging struct fields if it wanted to.
|
||||
if hasVarSize(base, nil) {
|
||||
x.mode = value
|
||||
if check.Types != nil {
|
||||
if check.recordTypes() {
|
||||
check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], obj.Type()))
|
||||
}
|
||||
} else {
|
||||
@ -716,7 +716,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
|
||||
if hasVarSize(x.typ, nil) {
|
||||
x.mode = value
|
||||
if check.Types != nil {
|
||||
if check.recordTypes() {
|
||||
check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], x.typ))
|
||||
}
|
||||
} else {
|
||||
@ -747,7 +747,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
|
||||
x.mode = value
|
||||
x.typ = NewSlice(ptr.base)
|
||||
if check.Types != nil {
|
||||
if check.recordTypes() {
|
||||
check.recordBuiltinType(call.Fun, makeSig(x.typ, ptr, y.typ))
|
||||
}
|
||||
|
||||
@ -766,7 +766,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
|
||||
x.mode = value
|
||||
x.typ = NewPointer(slice.elem)
|
||||
if check.Types != nil {
|
||||
if check.recordTypes() {
|
||||
check.recordBuiltinType(call.Fun, makeSig(x.typ, slice))
|
||||
}
|
||||
|
||||
@ -790,7 +790,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
|
||||
x.mode = value
|
||||
x.typ = Typ[String]
|
||||
if check.Types != nil {
|
||||
if check.recordTypes() {
|
||||
check.recordBuiltinType(call.Fun, makeSig(x.typ, NewPointer(universeByte), y.typ))
|
||||
}
|
||||
|
||||
@ -808,7 +808,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
|
||||
x.mode = value
|
||||
x.typ = NewPointer(universeByte)
|
||||
if check.Types != nil {
|
||||
if check.recordTypes() {
|
||||
check.recordBuiltinType(call.Fun, makeSig(x.typ, Typ[String]))
|
||||
}
|
||||
|
||||
|
@ -425,7 +425,7 @@ func (check *Checker) record(x *operand) {
|
||||
}
|
||||
|
||||
func (check *Checker) recordUntyped() {
|
||||
if !debug && check.Types == nil {
|
||||
if !debug && !check.recordTypes() {
|
||||
return // nothing to do
|
||||
}
|
||||
|
||||
@ -453,6 +453,35 @@ func (check *Checker) recordTypeAndValue(x syntax.Expr, mode operandMode, typ Ty
|
||||
if m := check.Types; m != nil {
|
||||
m[x] = TypeAndValue{mode, typ, val}
|
||||
}
|
||||
if check.StoreTypesInSyntax {
|
||||
tv := TypeAndValue{mode, typ, val}
|
||||
stv := syntax.TypeAndValue{Type: typ, Value: val}
|
||||
if tv.IsVoid() {
|
||||
stv.SetIsVoid()
|
||||
}
|
||||
if tv.IsType() {
|
||||
stv.SetIsType()
|
||||
}
|
||||
if tv.IsBuiltin() {
|
||||
stv.SetIsBuiltin()
|
||||
}
|
||||
if tv.IsValue() {
|
||||
stv.SetIsValue()
|
||||
}
|
||||
if tv.IsNil() {
|
||||
stv.SetIsNil()
|
||||
}
|
||||
if tv.Addressable() {
|
||||
stv.SetAddressable()
|
||||
}
|
||||
if tv.Assignable() {
|
||||
stv.SetAssignable()
|
||||
}
|
||||
if tv.HasOk() {
|
||||
stv.SetHasOk()
|
||||
}
|
||||
x.SetTypeInfo(stv)
|
||||
}
|
||||
}
|
||||
|
||||
func (check *Checker) recordBuiltinType(f syntax.Expr, sig *Signature) {
|
||||
@ -489,7 +518,25 @@ func (check *Checker) recordCommaOkTypes(x syntax.Expr, a [2]Type) {
|
||||
NewVar(pos, check.pkg, "", a[1]),
|
||||
)
|
||||
m[x] = tv
|
||||
// if x is a parenthesized expression (p.X), update p.X
|
||||
p, _ := x.(*syntax.ParenExpr)
|
||||
if p == nil {
|
||||
break
|
||||
}
|
||||
x = p.X
|
||||
}
|
||||
}
|
||||
if check.StoreTypesInSyntax {
|
||||
// Note: this loop is duplicated because the type of tv is different.
|
||||
// Above it is types2.TypeAndValue, here it is syntax.TypeAndValue.
|
||||
for {
|
||||
tv := x.GetTypeInfo()
|
||||
assert(tv.Type != nil) // should have been recorded already
|
||||
pos := x.Pos()
|
||||
tv.Type = NewTuple(
|
||||
NewVar(pos, check.pkg, "", a[0]),
|
||||
NewVar(pos, check.pkg, "", a[1]),
|
||||
)
|
||||
x.SetTypeInfo(tv)
|
||||
p, _ := x.(*syntax.ParenExpr)
|
||||
if p == nil {
|
||||
break
|
||||
|
@ -4,15 +4,11 @@
|
||||
|
||||
package types2
|
||||
|
||||
import "cmd/compile/internal/syntax"
|
||||
|
||||
// A Type represents a type of Go.
|
||||
// All types implement the Type interface.
|
||||
type Type interface {
|
||||
// Underlying returns the underlying type of a type.
|
||||
Underlying() Type
|
||||
|
||||
// String returns a string representation of a type.
|
||||
String() string
|
||||
}
|
||||
type Type = syntax.Type
|
||||
|
||||
// under returns the true expanded underlying type.
|
||||
// If it doesn't exist, the result is Typ[Invalid].
|
||||
|
Loading…
Reference in New Issue
Block a user