aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/typecheck
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/typecheck')
-rw-r--r--src/cmd/compile/internal/typecheck/crawler.go45
-rw-r--r--src/cmd/compile/internal/typecheck/iexport.go68
-rw-r--r--src/cmd/compile/internal/typecheck/iimport.go67
-rw-r--r--src/cmd/compile/internal/typecheck/stmt.go9
-rw-r--r--src/cmd/compile/internal/typecheck/subr.go67
-rw-r--r--src/cmd/compile/internal/typecheck/typecheck.go1
6 files changed, 177 insertions, 80 deletions
diff --git a/src/cmd/compile/internal/typecheck/crawler.go b/src/cmd/compile/internal/typecheck/crawler.go
index 9e523c3d14..3f212aa805 100644
--- a/src/cmd/compile/internal/typecheck/crawler.go
+++ b/src/cmd/compile/internal/typecheck/crawler.go
@@ -44,12 +44,13 @@ func (p *crawler) markObject(n *ir.Name) {
p.markType(n.Type())
}
-// markType recursively visits types reachable from t to identify
-// functions whose inline bodies may be needed.
+// markType recursively visits types reachable from t to identify functions whose
+// inline bodies may be needed. For instantiated generic types, it visits the base
+// generic type, which has the relevant methods.
func (p *crawler) markType(t *types.Type) {
- if t.IsInstantiatedGeneric() {
- // Re-instantiated types don't add anything new, so don't follow them.
- return
+ if t.OrigSym() != nil {
+ // Convert to the base generic type.
+ t = t.OrigSym().Def.Type()
}
if p.marked[t] {
return
@@ -92,6 +93,9 @@ func (p *crawler) markType(t *types.Type) {
p.markType(t.Elem())
case types.TSTRUCT:
+ if t.IsFuncArgStruct() {
+ break
+ }
for _, f := range t.FieldSlice() {
if types.IsExported(f.Sym.Name) || f.Embedded != 0 {
p.markType(f.Type)
@@ -129,9 +133,9 @@ func (p *crawler) markEmbed(t *types.Type) {
t = t.Elem()
}
- if t.IsInstantiatedGeneric() {
- // Re-instantiated types don't add anything new, so don't follow them.
- return
+ if t.OrigSym() != nil {
+ // Convert to the base generic type.
+ t = t.OrigSym().Def.Type()
}
if p.embedded[t] {
@@ -185,6 +189,15 @@ func (p *crawler) markInlBody(n *ir.Name) {
var doFlood func(n ir.Node)
doFlood = func(n ir.Node) {
+ t := n.Type()
+ if t != nil && (t.HasTParam() || t.IsFullyInstantiated()) {
+ // Ensure that we call markType() on any base generic type
+ // that is written to the export file (even if not explicitly
+ // marked for export), so we will call markInlBody on its
+ // methods, and the methods will be available for
+ // instantiation if needed.
+ p.markType(t)
+ }
switch n.Op() {
case ir.OMETHEXPR, ir.ODOTMETH:
p.markInlBody(ir.MethodExprName(n))
@@ -198,9 +211,6 @@ func (p *crawler) markInlBody(n *ir.Name) {
case ir.PEXTERN:
Export(n)
}
- p.checkGenericType(n.Type())
- case ir.OTYPE:
- p.checkGenericType(n.Type())
case ir.OMETHVALUE:
// Okay, because we don't yet inline indirect
// calls to method values.
@@ -216,16 +226,3 @@ func (p *crawler) markInlBody(n *ir.Name) {
// because after inlining they might be callable.
ir.VisitList(fn.Inl.Body, doFlood)
}
-
-// checkGenerictype ensures that we call markType() on any base generic type that
-// is written to the export file (even if not explicitly marked
-// for export), so its methods will be available for inlining if needed.
-func (p *crawler) checkGenericType(t *types.Type) {
- if t != nil && t.HasTParam() {
- if t.OrigSym() != nil {
- // Convert to the base generic type.
- t = t.OrigSym().Def.Type()
- }
- p.markType(t)
- }
-}
diff --git a/src/cmd/compile/internal/typecheck/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go
index f001017a86..489306e1e6 100644
--- a/src/cmd/compile/internal/typecheck/iexport.go
+++ b/src/cmd/compile/internal/typecheck/iexport.go
@@ -63,8 +63,9 @@
// }
//
// type Func struct {
-// Tag byte // 'F'
+// Tag byte // 'F' or 'G'
// Pos Pos
+// TypeParams []typeOff // only present if Tag == 'G'
// Signature Signature
// }
//
@@ -75,8 +76,9 @@
// }
//
// type Type struct {
-// Tag byte // 'T'
+// Tag byte // 'T' or 'U'
// Pos Pos
+// TypeParams []typeOff // only present if Tag == 'U'
// Underlying typeOff
//
// Methods []struct{ // omitted if Underlying is an interface type
@@ -93,6 +95,12 @@
// Type typeOff
// }
//
+// // "Automatic" declaration of each typeparam
+// type TypeParam struct {
+// Tag byte // 'P'
+// Pos Pos
+// Bound typeOff
+// }
//
// typeOff means a uvarint that either indicates a predeclared type,
// or an offset into the Data section. If the uvarint is less than
@@ -104,7 +112,7 @@
// (*exportWriter).value for details.
//
//
-// There are nine kinds of type descriptors, distinguished by an itag:
+// There are twelve kinds of type descriptors, distinguished by an itag:
//
// type DefinedType struct {
// Tag itag // definedType
@@ -172,8 +180,30 @@
// }
// }
//
+// // Reference to a type param declaration
+// type TypeParamType struct {
+// Tag itag // typeParamType
+// Name stringOff
+// PkgPath stringOff
+// }
+//
+// // Instantiation of a generic type (like List[T2] or List[int])
+// type InstanceType struct {
+// Tag itag // instanceType
+// Pos pos
+// TypeArgs []typeOff
+// BaseType typeOff
+// }
+//
+// type UnionType struct {
+// Tag itag // interfaceType
+// Terms []struct {
+// tilde bool
+// Type typeOff
+// }
+// }
+//
//
-// TODO(danscales): fill in doc for 'type TypeParamType' and 'type InstType'
//
// type Signature struct {
// Params []Param
@@ -255,7 +285,7 @@ const (
structType
interfaceType
typeParamType
- instType
+ instanceType // Instantiation of a generic type
unionType
)
@@ -893,7 +923,7 @@ func (w *exportWriter) doTyp(t *types.Type) {
if strings.Index(s.Name, "[") < 0 {
base.Fatalf("incorrect name for instantiated type")
}
- w.startType(instType)
+ w.startType(instanceType)
w.pos(t.Pos())
// Export the type arguments for the instantiated type. The
// instantiated type could be in a method header (e.g. "func (v
@@ -1456,10 +1486,23 @@ func (w *exportWriter) node(n ir.Node) {
}
}
-// Caution: stmt will emit more than one node for statement nodes n that have a non-empty
-// n.Ninit and where n cannot have a natural init section (such as in "if", "for", etc.).
+func isNonEmptyAssign(n ir.Node) bool {
+ switch n.Op() {
+ case ir.OAS:
+ if n.(*ir.AssignStmt).Y != nil {
+ return true
+ }
+ case ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
+ return true
+ }
+ return false
+}
+
+// Caution: stmt will emit more than one node for statement nodes n that have a
+// non-empty n.Ninit and where n is not a non-empty assignment or a node with a natural init
+// section (such as in "if", "for", etc.).
func (w *exportWriter) stmt(n ir.Node) {
- if len(n.Init()) > 0 && !ir.StmtWithInit(n.Op()) {
+ if len(n.Init()) > 0 && !ir.StmtWithInit(n.Op()) && !isNonEmptyAssign(n) && n.Op() != ir.ORANGE {
// can't use stmtList here since we don't want the final OEND
for _, n := range n.Init() {
w.stmt(n)
@@ -1495,8 +1538,10 @@ func (w *exportWriter) stmt(n ir.Node) {
if n.Y != nil {
w.op(ir.OAS)
w.pos(n.Pos())
+ w.stmtList(n.Init())
w.expr(n.X)
w.expr(n.Y)
+ w.bool(n.Def)
}
case ir.OASOP:
@@ -1517,8 +1562,10 @@ func (w *exportWriter) stmt(n ir.Node) {
w.op(ir.OAS2)
}
w.pos(n.Pos())
+ w.stmtList(n.Init())
w.exprList(n.Lhs)
w.exprList(n.Rhs)
+ w.bool(n.Def)
case ir.ORETURN:
n := n.(*ir.ReturnStmt)
@@ -1556,6 +1603,7 @@ func (w *exportWriter) stmt(n ir.Node) {
n := n.(*ir.RangeStmt)
w.op(ir.ORANGE)
w.pos(n.Pos())
+ w.stmtList(n.Init())
w.exprsOrNil(n.Key, n.Value)
w.expr(n.X)
w.stmtList(n.Body)
@@ -2065,8 +2113,10 @@ func (w *exportWriter) expr(n ir.Node) {
n := n.(*ir.AssignListStmt)
w.op(ir.OSELRECV2)
w.pos(n.Pos())
+ w.stmtList(n.Init())
w.exprList(n.Lhs)
w.exprList(n.Rhs)
+ w.bool(n.Def)
default:
base.Fatalf("cannot export %v (%d) node\n"+
diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go
index 8bc098c2bd..ec4057a8d0 100644
--- a/src/cmd/compile/internal/typecheck/iimport.go
+++ b/src/cmd/compile/internal/typecheck/iimport.go
@@ -316,16 +316,12 @@ func (r *importReader) doDecl(sym *types.Sym) *ir.Name {
return n
case 'T', 'U':
- var rparams []*types.Type
- if tag == 'U' {
- rparams = r.typeList()
- }
-
// Types can be recursive. We need to setup a stub
// declaration before recursing.
n := importtype(pos, sym)
t := n.Type()
if tag == 'U' {
+ rparams := r.typeList()
t.SetRParams(rparams)
}
@@ -825,7 +821,7 @@ func (r *importReader) typ1() *types.Type {
}
return n.Type()
- case instType:
+ case instanceType:
if r.p.exportVersion < iexportVersionGenerics {
base.Fatalf("unexpected instantiation type")
}
@@ -1170,10 +1166,26 @@ func (r *importReader) stmtList() []ir.Node {
if n.Op() == ir.OBLOCK {
n := n.(*ir.BlockStmt)
list = append(list, n.List...)
- } else {
- list = append(list, n)
+ continue
}
-
+ if len(list) > 0 {
+ // check for an optional label that can only immediately
+ // precede a for/range/select/switch statement.
+ if last := list[len(list)-1]; last.Op() == ir.OLABEL {
+ label := last.(*ir.LabelStmt).Label
+ switch n.Op() {
+ case ir.OFOR:
+ n.(*ir.ForStmt).Label = label
+ case ir.ORANGE:
+ n.(*ir.RangeStmt).Label = label
+ case ir.OSELECT:
+ n.(*ir.SelectStmt).Label = label
+ case ir.OSWITCH:
+ n.(*ir.SwitchStmt).Label = label
+ }
+ }
+ }
+ list = append(list, n)
}
return list
}
@@ -1503,7 +1515,7 @@ func (r *importReader) node() ir.Node {
if go117ExportTypes {
n.SetOp(op)
}
- *n.PtrInit() = init
+ n.SetInit(init)
n.IsDDD = r.bool()
if go117ExportTypes {
n.SetType(r.exoticType())
@@ -1607,7 +1619,12 @@ func (r *importReader) node() ir.Node {
// unreachable - never exported
case ir.OAS:
- return ir.NewAssignStmt(r.pos(), r.expr(), r.expr())
+ pos := r.pos()
+ init := r.stmtList()
+ n := ir.NewAssignStmt(pos, r.expr(), r.expr())
+ n.SetInit(init)
+ n.Def = r.bool()
+ return n
case ir.OASOP:
n := ir.NewAssignOpStmt(r.pos(), r.op(), r.expr(), nil)
@@ -1624,7 +1641,12 @@ func (r *importReader) node() ir.Node {
// unreachable - mapped to case OAS2 by exporter
goto error
}
- return ir.NewAssignListStmt(r.pos(), op, r.exprList(), r.exprList())
+ pos := r.pos()
+ init := r.stmtList()
+ n := ir.NewAssignListStmt(pos, op, r.exprList(), r.exprList())
+ n.SetInit(init)
+ n.Def = r.bool()
+ return n
case ir.ORETURN:
return ir.NewReturnStmt(r.pos(), r.exprList())
@@ -1638,26 +1660,28 @@ func (r *importReader) node() ir.Node {
case ir.OIF:
pos, init := r.pos(), r.stmtList()
n := ir.NewIfStmt(pos, r.expr(), r.stmtList(), r.stmtList())
- *n.PtrInit() = init
+ n.SetInit(init)
return n
case ir.OFOR:
pos, init := r.pos(), r.stmtList()
cond, post := r.exprsOrNil()
n := ir.NewForStmt(pos, nil, cond, post, r.stmtList())
- *n.PtrInit() = init
+ n.SetInit(init)
return n
case ir.ORANGE:
- pos := r.pos()
+ pos, init := r.pos(), r.stmtList()
k, v := r.exprsOrNil()
- return ir.NewRangeStmt(pos, k, v, r.expr(), r.stmtList())
+ n := ir.NewRangeStmt(pos, k, v, r.expr(), r.stmtList())
+ n.SetInit(init)
+ return n
case ir.OSELECT:
pos := r.pos()
init := r.stmtList()
n := ir.NewSelectStmt(pos, r.commList())
- *n.PtrInit() = init
+ n.SetInit(init)
return n
case ir.OSWITCH:
@@ -1665,7 +1689,7 @@ func (r *importReader) node() ir.Node {
init := r.stmtList()
x, _ := r.exprsOrNil()
n := ir.NewSwitchStmt(pos, x, r.caseList(x))
- *n.PtrInit() = init
+ n.SetInit(init)
return n
// case OCASE:
@@ -1709,7 +1733,12 @@ func (r *importReader) node() ir.Node {
return n
case ir.OSELRECV2:
- return ir.NewAssignListStmt(r.pos(), ir.OSELRECV2, r.exprList(), r.exprList())
+ pos := r.pos()
+ init := r.stmtList()
+ n := ir.NewAssignListStmt(pos, ir.OSELRECV2, r.exprList(), r.exprList())
+ n.SetInit(init)
+ n.Def = r.bool()
+ return n
default:
base.Fatalf("cannot import %v (%d) node\n"+
diff --git a/src/cmd/compile/internal/typecheck/stmt.go b/src/cmd/compile/internal/typecheck/stmt.go
index c322d490e5..9a02c1752c 100644
--- a/src/cmd/compile/internal/typecheck/stmt.go
+++ b/src/cmd/compile/internal/typecheck/stmt.go
@@ -395,10 +395,11 @@ func tcSelect(sel *ir.SelectStmt) {
n := Stmt(ncase.Comm)
ncase.Comm = n
oselrecv2 := func(dst, recv ir.Node, def bool) {
- n := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv})
- n.Def = def
- n.SetTypecheck(1)
- ncase.Comm = n
+ selrecv := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv})
+ selrecv.Def = def
+ selrecv.SetTypecheck(1)
+ selrecv.SetInit(n.Init())
+ ncase.Comm = selrecv
}
switch n.Op() {
default:
diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go
index 34f20879f1..d4af4e172e 100644
--- a/src/cmd/compile/internal/typecheck/subr.go
+++ b/src/cmd/compile/internal/typecheck/subr.go
@@ -352,7 +352,10 @@ func Assignop(src, dst *types.Type) (ir.Op, string) {
if types.Identical(src, dst) {
return ir.OCONVNOP, ""
}
+ return Assignop1(src, dst)
+}
+func Assignop1(src, dst *types.Type) (ir.Op, string) {
// 2. src and dst have identical underlying types and
// a. either src or dst is not a named type, or
// b. both are empty interface types, or
@@ -1019,10 +1022,11 @@ type Tsubster struct {
SubstForwFunc func(*types.Type) *types.Type
}
-// Typ computes the type obtained by substituting any type parameter in t with the
-// corresponding type argument in subst. If t contains no type parameters, the
-// result is t; otherwise the result is a new type. It deals with recursive types
-// by using TFORW types and finding partially or fully created types via sym.Def.
+// Typ computes the type obtained by substituting any type parameter or shape in t
+// that appears in subst.Tparams with the corresponding type argument in subst.Targs.
+// If t contains no type parameters, the result is t; otherwise the result is a new
+// type. It deals with recursive types by using TFORW types and finding partially or
+// fully created types via sym.Def.
func (ts *Tsubster) Typ(t *types.Type) *types.Type {
// Defer the CheckSize calls until we have fully-defined
// (possibly-recursive) top-level type.
@@ -1033,14 +1037,14 @@ func (ts *Tsubster) Typ(t *types.Type) *types.Type {
}
func (ts *Tsubster) typ1(t *types.Type) *types.Type {
- if !t.HasTParam() && t.Kind() != types.TFUNC {
+ if !t.HasTParam() && !t.HasShape() && t.Kind() != types.TFUNC {
// Note: function types need to be copied regardless, as the
// types of closures may contain declarations that need
// to be copied. See #45738.
return t
}
- if t.IsTypeParam() {
+ if t.IsTypeParam() || t.IsShape() {
for i, tp := range ts.Tparams {
if tp == t {
return ts.Targs[i]
@@ -1072,14 +1076,14 @@ func (ts *Tsubster) typ1(t *types.Type) *types.Type {
var targsChanged bool
var forw *types.Type
- if t.Sym() != nil && t.HasTParam() {
+ if t.Sym() != nil && (t.HasTParam() || t.HasShape()) {
// Need to test for t.HasTParam() again because of special TFUNC case above.
// Translate the type params for this type according to
// the tparam/targs mapping from subst.
neededTargs = make([]*types.Type, len(t.RParams()))
for i, rparam := range t.RParams() {
neededTargs[i] = ts.typ1(rparam)
- if !types.Identical(neededTargs[i], rparam) {
+ if !types.IdenticalStrict(neededTargs[i], rparam) {
targsChanged = true
}
}
@@ -1286,7 +1290,7 @@ func (ts *Tsubster) typ1(t *types.Type) *types.Type {
// fields, set force to true.
func (ts *Tsubster) tstruct(t *types.Type, force bool) *types.Type {
if t.NumFields() == 0 {
- if t.HasTParam() {
+ if t.HasTParam() || t.HasShape() {
// For an empty struct, we need to return a new type,
// since it may now be fully instantiated (HasTParam
// becomes false).
@@ -1312,6 +1316,7 @@ func (ts *Tsubster) tstruct(t *types.Type, force bool) *types.Type {
// the type param, not the instantiated type).
newfields[i] = types.NewField(f.Pos, f.Sym, t2)
newfields[i].Embedded = f.Embedded
+ newfields[i].Note = f.Note
if f.IsDDD() {
newfields[i].SetIsDDD(true)
}
@@ -1387,19 +1392,20 @@ func genericTypeName(sym *types.Sym) string {
return sym.Name[0:strings.Index(sym.Name, "[")]
}
-// Shapify takes a concrete type and returns a GCshape type that can
+// Shapify takes a concrete type and a type param index, and returns a GCshape type that can
// be used in place of the input type and still generate identical code.
// No methods are added - all methods calls directly on a shape should
// be done by converting to an interface using the dictionary.
//
-// TODO: this could take the generic function and base its decisions
-// on how that generic function uses this type argument. For instance,
-// if it doesn't use it as a function argument/return value, then
-// we don't need to distinguish int64 and float64 (because they only
-// differ in how they get passed as arguments). For now, we only
-// unify two different types if they are identical in every possible way.
-func Shapify(t *types.Type) *types.Type {
- assert(!t.HasShape())
+// For now, we only consider two types to have the same shape, if they have exactly
+// the same underlying type or they are both pointer types.
+//
+// Shape types are also distinguished by the index of the type in a type param/arg
+// list. We need to do this so we can distinguish and substitute properly for two
+// type params in the same function that have the same shape for a particular
+// instantiation.
+func Shapify(t *types.Type, index int) *types.Type {
+ assert(!t.IsShape())
// Map all types with the same underlying type to the same shape.
u := t.Underlying()
@@ -1409,22 +1415,35 @@ func Shapify(t *types.Type) *types.Type {
u = types.Types[types.TUINT8].PtrTo()
}
- if s := shaped[u]; s != nil {
+ if shapeMap == nil {
+ shapeMap = map[int]map[*types.Type]*types.Type{}
+ }
+ submap := shapeMap[index]
+ if submap == nil {
+ submap = map[*types.Type]*types.Type{}
+ shapeMap[index] = submap
+ }
+ if s := submap[u]; s != nil {
return s
}
- sym := shapePkg.Lookup(u.LinkString())
+ nm := fmt.Sprintf("%s_%d", u.LinkString(), index)
+ sym := types.ShapePkg.Lookup(nm)
+ if sym.Def != nil {
+ // Use any existing type with the same name
+ submap[u] = sym.Def.Type()
+ return submap[u]
+ }
name := ir.NewDeclNameAt(u.Pos(), ir.OTYPE, sym)
s := types.NewNamed(name)
+ sym.Def = name
s.SetUnderlying(u)
s.SetIsShape(true)
s.SetHasShape(true)
name.SetType(s)
name.SetTypecheck(1)
- shaped[u] = s
+ submap[u] = s
return s
}
-var shaped = map[*types.Type]*types.Type{}
-
-var shapePkg = types.NewPkg(".shape", ".shape")
+var shapeMap map[int]map[*types.Type]*types.Type
diff --git a/src/cmd/compile/internal/typecheck/typecheck.go b/src/cmd/compile/internal/typecheck/typecheck.go
index 404af5b1b2..42970f6a5e 100644
--- a/src/cmd/compile/internal/typecheck/typecheck.go
+++ b/src/cmd/compile/internal/typecheck/typecheck.go
@@ -879,6 +879,7 @@ func typecheck1(n ir.Node, top int) ir.Node {
case ir.OTAILCALL:
n := n.(*ir.TailCallStmt)
+ n.Call = typecheck(n.Call, ctxStmt|ctxExpr).(*ir.CallExpr)
return n
case ir.OCHECKNIL: