summaryrefslogtreecommitdiffstats
path: root/src/cmd/compile/internal/noder/expr.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:16:40 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:16:40 +0000
commit47ab3d4a42e9ab51c465c4322d2ec233f6324e6b (patch)
treea61a0ffd83f4a3def4b36e5c8e99630c559aa723 /src/cmd/compile/internal/noder/expr.go
parentInitial commit. (diff)
downloadgolang-1.18-upstream.tar.xz
golang-1.18-upstream.zip
Adding upstream version 1.18.10.upstream/1.18.10upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/compile/internal/noder/expr.go')
-rw-r--r--src/cmd/compile/internal/noder/expr.go493
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
+ }
+}