diff options
Diffstat (limited to 'src/cmd/compile/internal/noder/expr.go')
-rw-r--r-- | src/cmd/compile/internal/noder/expr.go | 493 |
1 files changed, 493 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/noder/expr.go b/src/cmd/compile/internal/noder/expr.go new file mode 100644 index 0000000..4b5ae70 --- /dev/null +++ b/src/cmd/compile/internal/noder/expr.go @@ -0,0 +1,493 @@ +// 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 noder + +import ( + "fmt" + + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/syntax" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/compile/internal/types2" + "cmd/internal/src" +) + +func (g *irgen) expr(expr syntax.Expr) ir.Node { + expr = unparen(expr) // skip parens; unneeded after parse+typecheck + + if expr == nil { + return nil + } + + if expr, ok := expr.(*syntax.Name); ok && expr.Value == "_" { + return ir.BlankNode + } + + tv, ok := g.info.Types[expr] + if !ok { + base.FatalfAt(g.pos(expr), "missing type for %v (%T)", expr, expr) + } + switch { + case tv.IsBuiltin(): + // Qualified builtins, such as unsafe.Add and unsafe.Slice. + if expr, ok := expr.(*syntax.SelectorExpr); ok { + if name, ok := expr.X.(*syntax.Name); ok { + if _, ok := g.info.Uses[name].(*types2.PkgName); ok { + return g.use(expr.Sel) + } + } + } + return g.use(expr.(*syntax.Name)) + case tv.IsType(): + return ir.TypeNode(g.typ(tv.Type)) + case tv.IsValue(), tv.IsVoid(): + // ok + default: + base.FatalfAt(g.pos(expr), "unrecognized type-checker result") + } + + base.Assert(g.exprStmtOK) + + // 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, + // and so types2 leaves them untyped. So we need to fix those up here. + typ := tv.Type + if basic, ok := typ.(*types2.Basic); ok && basic.Info()&types2.IsUntyped != 0 { + switch basic.Kind() { + case types2.UntypedNil: + // ok; can appear in type switch case clauses + // TODO(mdempsky): Handle as part of type switches instead? + case types2.UntypedBool: + typ = types2.Typ[types2.Bool] // expression in "if" or "for" condition + case types2.UntypedString: + typ = types2.Typ[types2.String] // argument to "append" or "copy" calls + default: + base.FatalfAt(g.pos(expr), "unexpected untyped type: %v", basic) + } + } + + // Constant expression. + if tv.Value != nil { + typ := g.typ(typ) + value := FixValue(typ, tv.Value) + return OrigConst(g.pos(expr), typ, value, constExprOp(expr), syntax.String(expr)) + } + + n := g.expr0(typ, expr) + if n.Typecheck() != 1 && n.Typecheck() != 3 { + base.FatalfAt(g.pos(expr), "missed typecheck: %+v", n) + } + if n.Op() != ir.OFUNCINST && !g.match(n.Type(), typ, tv.HasOk()) { + base.FatalfAt(g.pos(expr), "expected %L to have type %v", n, typ) + } + return n +} + +func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node { + pos := g.pos(expr) + assert(pos.IsKnown()) + + // Set base.Pos for transformation code that still uses base.Pos, rather than + // the pos of the node being converted. + base.Pos = pos + + switch expr := expr.(type) { + case *syntax.Name: + if _, isNil := g.info.Uses[expr].(*types2.Nil); isNil { + return Nil(pos, g.typ(typ)) + } + return g.use(expr) + + case *syntax.CompositeLit: + return g.compLit(typ, expr) + + case *syntax.FuncLit: + return g.funcLit(typ, expr) + + case *syntax.AssertExpr: + return Assert(pos, g.expr(expr.X), g.typeExpr(expr.Type)) + + case *syntax.CallExpr: + fun := g.expr(expr.Fun) + return g.callExpr(pos, g.typ(typ), fun, g.exprs(expr.ArgList), expr.HasDots) + + case *syntax.IndexExpr: + args := unpackListExpr(expr.Index) + if len(args) == 1 { + tv, ok := g.info.Types[args[0]] + assert(ok) + if tv.IsValue() { + // This is just a normal index expression + n := Index(pos, g.typ(typ), g.expr(expr.X), g.expr(args[0])) + if !g.delayTransform() { + // transformIndex will modify n.Type() for OINDEXMAP. + transformIndex(n) + } + return n + } + } + + // expr.Index is a list of type args, so we ignore it, since types2 has + // already provided this info with the Info.Instances map. + return g.expr(expr.X) + + case *syntax.SelectorExpr: + // Qualified identifier. + if name, ok := expr.X.(*syntax.Name); ok { + if _, ok := g.info.Uses[name].(*types2.PkgName); ok { + return g.use(expr.Sel) + } + } + return g.selectorExpr(pos, typ, expr) + + case *syntax.SliceExpr: + n := Slice(pos, g.typ(typ), g.expr(expr.X), g.expr(expr.Index[0]), g.expr(expr.Index[1]), g.expr(expr.Index[2])) + if !g.delayTransform() { + transformSlice(n) + } + return n + + case *syntax.Operation: + if expr.Y == nil { + n := Unary(pos, g.typ(typ), g.op(expr.Op, unOps[:]), g.expr(expr.X)) + if n.Op() == ir.OADDR && !g.delayTransform() { + transformAddr(n.(*ir.AddrExpr)) + } + return n + } + switch op := g.op(expr.Op, binOps[:]); op { + case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE: + n := Compare(pos, g.typ(typ), op, g.expr(expr.X), g.expr(expr.Y)) + if !g.delayTransform() { + transformCompare(n) + } + return n + case ir.OANDAND, ir.OOROR: + x := g.expr(expr.X) + y := g.expr(expr.Y) + return typed(x.Type(), ir.NewLogicalExpr(pos, op, x, y)) + default: + n := Binary(pos, op, g.typ(typ), g.expr(expr.X), g.expr(expr.Y)) + if op == ir.OADD && !g.delayTransform() { + return transformAdd(n) + } + return n + } + + default: + g.unhandled("expression", expr) + panic("unreachable") + } +} + +// substType does a normal type substition, but tparams is in the form of a field +// list, and targs is in terms of a slice of type nodes. substType records any newly +// instantiated types into g.instTypeList. +func (g *irgen) substType(typ *types.Type, tparams *types.Type, targs []ir.Node) *types.Type { + fields := tparams.FieldSlice() + tparams1 := make([]*types.Type, len(fields)) + for i, f := range fields { + tparams1[i] = f.Type + } + targs1 := make([]*types.Type, len(targs)) + for i, n := range targs { + targs1[i] = n.Type() + } + ts := typecheck.Tsubster{ + Tparams: tparams1, + Targs: targs1, + } + newt := ts.Typ(typ) + return newt +} + +// callExpr creates a call expression (which might be a type conversion, built-in +// call, or a regular call) and does standard transforms, unless we are in a generic +// function. +func (g *irgen) callExpr(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool) ir.Node { + n := ir.NewCallExpr(pos, ir.OCALL, fun, args) + n.IsDDD = dots + typed(typ, n) + + if fun.Op() == ir.OTYPE { + // Actually a type conversion, not a function call. + if !g.delayTransform() { + return transformConvCall(n) + } + return n + } + + if fun, ok := fun.(*ir.Name); ok && fun.BuiltinOp != 0 { + if !g.delayTransform() { + return transformBuiltin(n) + } + return n + } + + // Add information, now that we know that fun is actually being called. + switch fun := fun.(type) { + case *ir.SelectorExpr: + if fun.Op() == ir.OMETHVALUE { + op := ir.ODOTMETH + if fun.X.Type().IsInterface() { + op = ir.ODOTINTER + } + fun.SetOp(op) + // Set the type to include the receiver, since that's what + // later parts of the compiler expect + fun.SetType(fun.Selection.Type) + } + } + + // A function instantiation (even if fully concrete) shouldn't be + // transformed yet, because we need to add the dictionary during the + // transformation. + if fun.Op() != ir.OFUNCINST && !g.delayTransform() { + transformCall(n) + } + return n +} + +// selectorExpr resolves the choice of ODOT, ODOTPTR, OMETHVALUE (eventually +// ODOTMETH & ODOTINTER), and OMETHEXPR and deals with embedded fields here rather +// than in typecheck.go. +func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.SelectorExpr) ir.Node { + x := g.expr(expr.X) + if x.Type().HasTParam() { + // Leave a method call on a type param as an OXDOT, since it can + // only be fully transformed once it has an instantiated type. + n := ir.NewSelectorExpr(pos, ir.OXDOT, x, typecheck.Lookup(expr.Sel.Value)) + typed(g.typ(typ), n) + return n + } + + selinfo := g.info.Selections[expr] + // Everything up to the last selection is an implicit embedded field access, + // and the last selection is determined by selinfo.Kind(). + index := selinfo.Index() + embeds, last := index[:len(index)-1], index[len(index)-1] + + origx := x + for _, ix := range embeds { + x = Implicit(DotField(pos, x, ix)) + } + + kind := selinfo.Kind() + if kind == types2.FieldVal { + return DotField(pos, x, last) + } + + var n ir.Node + method2 := selinfo.Obj().(*types2.Func) + + if kind == types2.MethodExpr { + // OMETHEXPR is unusual in using directly the node and type of the + // original OTYPE node (origx) before passing through embedded + // fields, even though the method is selected from the type + // (x.Type()) reached after following the embedded fields. We will + // actually drop any ODOT nodes we created due to the embedded + // fields. + n = MethodExpr(pos, origx, x.Type(), last) + } else { + // Add implicit addr/deref for method values, if needed. + if x.Type().IsInterface() { + n = DotMethod(pos, x, last) + } else { + recvType2 := method2.Type().(*types2.Signature).Recv().Type() + _, wantPtr := recvType2.(*types2.Pointer) + havePtr := x.Type().IsPtr() + + if havePtr != wantPtr { + if havePtr { + x = Implicit(Deref(pos, x.Type().Elem(), x)) + } else { + x = Implicit(Addr(pos, x)) + } + } + recvType2Base := recvType2 + if wantPtr { + recvType2Base = types2.AsPointer(recvType2).Elem() + } + if recvType2Base.(*types2.Named).TypeParams().Len() > 0 { + // recvType2 is the original generic type that is + // instantiated for this method call. + // selinfo.Recv() is the instantiated type + recvType2 = recvType2Base + recvTypeSym := g.pkg(method2.Pkg()).Lookup(recvType2.(*types2.Named).Obj().Name()) + recvType := recvTypeSym.Def.(*ir.Name).Type() + // method is the generic method associated with + // the base generic type. The instantiated type may not + // have method bodies filled in, if it was imported. + method := recvType.Methods().Index(last).Nname.(*ir.Name) + n = ir.NewSelectorExpr(pos, ir.OMETHVALUE, x, typecheck.Lookup(expr.Sel.Value)) + n.(*ir.SelectorExpr).Selection = types.NewField(pos, method.Sym(), method.Type()) + n.(*ir.SelectorExpr).Selection.Nname = method + typed(method.Type(), n) + + xt := deref(x.Type()) + targs := make([]ir.Node, len(xt.RParams())) + for i := range targs { + targs[i] = ir.TypeNode(xt.RParams()[i]) + } + + // Create function instantiation with the type + // args for the receiver type for the method call. + n = ir.NewInstExpr(pos, ir.OFUNCINST, n, targs) + typed(g.typ(typ), n) + return n + } + + if !g.match(x.Type(), recvType2, false) { + base.FatalfAt(pos, "expected %L to have type %v", x, recvType2) + } else { + n = DotMethod(pos, x, last) + } + } + } + if have, want := n.Sym(), g.selector(method2); have != want { + base.FatalfAt(pos, "bad Sym: have %v, want %v", have, want) + } + return n +} + +func (g *irgen) exprList(expr syntax.Expr) []ir.Node { + return g.exprs(unpackListExpr(expr)) +} + +func unpackListExpr(expr syntax.Expr) []syntax.Expr { + switch expr := expr.(type) { + case nil: + return nil + case *syntax.ListExpr: + return expr.ElemList + default: + return []syntax.Expr{expr} + } +} + +func (g *irgen) exprs(exprs []syntax.Expr) []ir.Node { + nodes := make([]ir.Node, len(exprs)) + for i, expr := range exprs { + nodes[i] = g.expr(expr) + } + return nodes +} + +func (g *irgen) compLit(typ types2.Type, lit *syntax.CompositeLit) ir.Node { + if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok { + n := ir.NewAddrExpr(g.pos(lit), g.compLit(ptr.Elem(), lit)) + n.SetOp(ir.OPTRLIT) + return typed(g.typ(typ), n) + } + + _, isStruct := types2.CoreType(typ).(*types2.Struct) + + exprs := make([]ir.Node, len(lit.ElemList)) + for i, elem := range lit.ElemList { + switch elem := elem.(type) { + case *syntax.KeyValueExpr: + var key ir.Node + if isStruct { + key = ir.NewIdent(g.pos(elem.Key), g.name(elem.Key.(*syntax.Name))) + } else { + key = g.expr(elem.Key) + } + value := wrapname(g.pos(elem.Value), g.expr(elem.Value)) + if value.Op() == ir.OPAREN { + // Make sure any PAREN node added by wrapper has a type + typed(value.(*ir.ParenExpr).X.Type(), value) + } + exprs[i] = ir.NewKeyExpr(g.pos(elem), key, value) + default: + exprs[i] = wrapname(g.pos(elem), g.expr(elem)) + if exprs[i].Op() == ir.OPAREN { + // Make sure any PAREN node added by wrapper has a type + typed(exprs[i].(*ir.ParenExpr).X.Type(), exprs[i]) + } + } + } + + n := ir.NewCompLitExpr(g.pos(lit), ir.OCOMPLIT, nil, exprs) + typed(g.typ(typ), n) + var r ir.Node = n + if !g.delayTransform() { + r = transformCompLit(n) + } + return r +} + +func (g *irgen) funcLit(typ2 types2.Type, expr *syntax.FuncLit) ir.Node { + fn := ir.NewClosureFunc(g.pos(expr), ir.CurFunc != nil) + ir.NameClosure(fn.OClosure, ir.CurFunc) + + typ := g.typ(typ2) + typed(typ, fn.Nname) + typed(typ, fn.OClosure) + fn.SetTypecheck(1) + + g.funcBody(fn, nil, expr.Type, expr.Body) + + ir.FinishCaptureNames(fn.Pos(), ir.CurFunc, fn) + + // TODO(mdempsky): ir.CaptureName should probably handle + // copying these fields from the canonical variable. + for _, cv := range fn.ClosureVars { + cv.SetType(cv.Canonical().Type()) + cv.SetTypecheck(1) + cv.SetWalkdef(1) + } + + if g.topFuncIsGeneric { + // Don't add any closure inside a generic function/method to the + // g.target.Decls list, even though it may not be generic itself. + // See issue #47514. + return ir.UseClosure(fn.OClosure, nil) + } else { + return ir.UseClosure(fn.OClosure, g.target) + } +} + +func (g *irgen) typeExpr(typ syntax.Expr) *types.Type { + n := g.expr(typ) + if n.Op() != ir.OTYPE { + base.FatalfAt(g.pos(typ), "expected type: %L", n) + } + return n.Type() +} + +// constExprOp returns an ir.Op that represents the outermost +// operation of the given constant expression. It's intended for use +// with ir.RawOrigExpr. +func constExprOp(expr syntax.Expr) ir.Op { + switch expr := expr.(type) { + default: + panic(fmt.Sprintf("%s: unexpected expression: %T", expr.Pos(), expr)) + + case *syntax.BasicLit: + return ir.OLITERAL + case *syntax.Name, *syntax.SelectorExpr: + return ir.ONAME + case *syntax.CallExpr: + return ir.OCALL + case *syntax.Operation: + if expr.Y == nil { + return unOps[expr.Op] + } + return binOps[expr.Op] + } +} + +func unparen(expr syntax.Expr) syntax.Expr { + for { + paren, ok := expr.(*syntax.ParenExpr) + if !ok { + return expr + } + expr = paren.X + } +} |