diff options
Diffstat (limited to 'src/cmd/compile/internal/typecheck')
-rw-r--r-- | src/cmd/compile/internal/typecheck/crawler.go | 45 | ||||
-rw-r--r-- | src/cmd/compile/internal/typecheck/iexport.go | 68 | ||||
-rw-r--r-- | src/cmd/compile/internal/typecheck/iimport.go | 67 | ||||
-rw-r--r-- | src/cmd/compile/internal/typecheck/stmt.go | 9 | ||||
-rw-r--r-- | src/cmd/compile/internal/typecheck/subr.go | 67 | ||||
-rw-r--r-- | src/cmd/compile/internal/typecheck/typecheck.go | 1 |
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: |