diff options
| author | Matthew Dempsky <mdempsky@google.com> | 2022-03-06 23:47:27 -0800 |
|---|---|---|
| committer | Matthew Dempsky <mdempsky@google.com> | 2022-03-07 13:47:51 +0000 |
| commit | 0e2f1abf5b764a4a3928a2f4f050144063c46a93 (patch) | |
| tree | 4f4b62491d656e00b050bc76049b9237ca16c6f5 /src/cmd/compile/internal/noder | |
| parent | 8893175c3b5267f1eb70c518b5de6f03037c4d03 (diff) | |
| download | go-0e2f1abf5b764a4a3928a2f4f050144063c46a93.tar.xz | |
cmd/compile: represent derived types with ir.DynamicType in unified IR
This CL switches unified IR to using ir.DynamicType for derived
types. This has an immediate effect of fixing compilation of generic
code that when fully stenciled results in statically invalid type
assertions. This does require updating typecheck to expect
ODYNAMICTYPE in type switches, but this is straightforward to
implement.
For now, we still statically resolve the runtime type (or itab)
pointer. However, a subsequent CL will allow reading these pointers
from the runtime dictionary.
Change-Id: I1666678fcc588bc9cb8b97871bd02b9059848e6d
Reviewed-on: https://go-review.googlesource.com/c/go/+/390336
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Diffstat (limited to 'src/cmd/compile/internal/noder')
| -rw-r--r-- | src/cmd/compile/internal/noder/reader.go | 89 | ||||
| -rw-r--r-- | src/cmd/compile/internal/noder/writer.go | 45 |
2 files changed, 114 insertions, 20 deletions
diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 3207e3f85b..5191dbe177 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1407,25 +1407,22 @@ func (r *reader) switchStmt(label *types.Sym) ir.Node { init := r.stmt() var tag ir.Node + var ident *ir.Ident + var iface *types.Type if r.Bool() { pos := r.pos() - var ident *ir.Ident if r.Bool() { pos := r.pos() sym := typecheck.Lookup(r.String()) ident = ir.NewIdent(pos, sym) } x := r.expr() + iface = x.Type() tag = ir.NewTypeSwitchGuard(pos, ident, x) } else { tag = r.expr() } - tswitch, ok := tag.(*ir.TypeSwitchGuard) - if ok && tswitch.Tag == nil { - tswitch = nil - } - clauses := make([]*ir.CaseClause, r.Len()) for i := range clauses { if i > 0 { @@ -1434,18 +1431,30 @@ func (r *reader) switchStmt(label *types.Sym) ir.Node { r.openScope() pos := r.pos() - cases := r.exprList() + var cases []ir.Node + if iface != nil { + cases = make([]ir.Node, r.Len()) + if len(cases) == 0 { + cases = nil // TODO(mdempsky): Unclear if this matters. + } + for i := range cases { + cases[i] = r.exprType(iface, true) + } + } else { + cases = r.exprList() + } clause := ir.NewCaseStmt(pos, cases, nil) - if tswitch != nil { + + if ident != nil { pos := r.pos() typ := r.typ() - name := ir.NewNameAt(pos, tswitch.Tag.Sym()) + name := ir.NewNameAt(pos, ident.Sym()) setType(name, typ) r.addLocal(name, ir.PAUTO) clause.Var = name - name.Defn = tswitch + name.Defn = tag } clause.Body = r.stmts() @@ -1529,10 +1538,7 @@ func (r *reader) expr() (res ir.Node) { return typecheck.Callee(r.obj()) case exprType: - // TODO(mdempsky): ir.TypeNode should probably return a typecheck'd node. - n := ir.TypeNode(r.typ()) - n.SetTypecheck(1) - return n + return r.exprType(nil, false) case exprConst: pos := r.pos() @@ -1552,6 +1558,15 @@ func (r *reader) expr() (res ir.Node) { x := r.expr() pos := r.pos() _, sym := r.selector() + + // Method expression with derived receiver type. + if x.Op() == ir.ODYNAMICTYPE { + // TODO(mdempsky): Handle with runtime dictionary lookup. + n := ir.TypeNode(x.Type()) + n.SetTypecheck(1) + x = n + } + n := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, x, sym)).(*ir.SelectorExpr) if n.Op() == ir.OMETHVALUE { wrapper := methodValueWrapper{ @@ -1588,8 +1603,12 @@ func (r *reader) expr() (res ir.Node) { case exprAssert: x := r.expr() pos := r.pos() - typ := r.expr().(ir.Ntype) - return typecheck.Expr(ir.NewTypeAssertExpr(pos, x, typ)) + typ := r.exprType(x.Type(), false) + + if typ, ok := typ.(*ir.DynamicType); ok && typ.Op() == ir.ODYNAMICTYPE { + return typed(typ.Type(), ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, x, typ.X)) + } + return typecheck.Expr(ir.NewTypeAssertExpr(pos, x, typ.(ir.Ntype))) case exprUnaryOp: op := r.op() @@ -1734,6 +1753,44 @@ func (r *reader) exprs() []ir.Node { return nodes } +func (r *reader) exprType(iface *types.Type, nilOK bool) ir.Node { + if iface != nil { + base.Assertf(iface.IsInterface(), "%L must be an interface type", iface) + } + + r.Sync(pkgbits.SyncExprType) + + if nilOK && r.Bool() { + return typecheck.Expr(types.BuiltinPkg.Lookup("nil").Def.(*ir.NilExpr)) + } + + pos := r.pos() + info := r.typInfo() + typ := r.p.typIdx(info, r.dict, true) + + if info.derived { + // TODO(mdempsky): Handle with runtime dictionary lookup. + + var lsym *obj.LSym + + // For assertions from non-empty interfaces to non-interfaces, + // we need the ITab instead. + if iface != nil && !iface.IsEmptyInterface() && !typ.IsInterface() { + lsym = reflectdata.ITabLsym(typ, iface) + } else { + lsym = reflectdata.TypeLinksym(typ) + } + + ptr := typecheck.Expr(typecheck.NodAddr(ir.NewLinksymExpr(pos, lsym, types.Types[types.TUINT8]))) + return typed(typ, ir.NewDynamicType(pos, ptr)) + } + + // TODO(mdempsky): ir.TypeNode should probably return a typecheck'd node. + n := ir.TypeNode(typ) + n.SetTypecheck(1) + return n +} + func (r *reader) op() ir.Op { r.Sync(pkgbits.SyncOp) return ir.Op(r.Len()) diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 59bce0730d..821fae59e0 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1073,7 +1073,12 @@ func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { w.pos(stmt) w.stmt(stmt.Init) + var iface types2.Type if guard, ok := stmt.Tag.(*syntax.TypeSwitchGuard); w.Bool(ok) { + tv, ok := w.p.info.Types[guard.X] + assert(ok && tv.IsValue()) + iface = tv.Type + w.pos(guard) if tag := guard.Lhs; w.Bool(tag != nil) { w.pos(tag) @@ -1092,7 +1097,16 @@ func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { w.openScope(clause.Pos()) w.pos(clause) - w.exprList(clause.Cases) + + if iface != nil { + cases := unpackListExpr(clause.Cases) + w.Len(len(cases)) + for _, cas := range cases { + w.exprType(iface, cas, true) + } + } else { + w.exprList(clause.Cases) + } if obj, ok := w.p.info.Implicits[clause]; ok { // TODO(mdempsky): These pos details are quirkish, but also @@ -1152,13 +1166,13 @@ func (w *writer) expr(expr syntax.Expr) { if tv.IsType() { w.Code(exprType) - w.typ(tv.Type) + w.exprType(nil, expr, false) return } if tv.Value != nil { w.Code(exprConst) - w.pos(expr.Pos()) + w.pos(expr) w.typ(tv.Type) w.Value(tv.Value) @@ -1232,10 +1246,13 @@ func (w *writer) expr(expr syntax.Expr) { } case *syntax.AssertExpr: + tv, ok := w.p.info.Types[expr.X] + assert(ok && tv.IsValue()) + w.Code(exprAssert) w.expr(expr.X) w.pos(expr) - w.expr(expr.Type) + w.exprType(tv.Type, expr.Type, false) case *syntax.Operation: if expr.Y == nil { @@ -1370,6 +1387,26 @@ func (w *writer) exprs(exprs []syntax.Expr) { } } +func (w *writer) exprType(iface types2.Type, typ syntax.Expr, nilOK bool) { + if iface != nil { + _, ok := iface.Underlying().(*types2.Interface) + base.Assertf(ok, "%v must be an interface type", iface) + } + + tv, ok := w.p.info.Types[typ] + assert(ok) + + w.Sync(pkgbits.SyncExprType) + + if nilOK && w.Bool(tv.IsNil()) { + return + } + + assert(tv.IsType()) + w.pos(typ) + w.typ(tv.Type) +} + func (w *writer) op(op ir.Op) { // TODO(mdempsky): Remove in favor of explicit codes? Would make // export data more stable against internal refactorings, but low |
