Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/compile/internal/noder/helpers.go

Documentation: cmd/compile/internal/noder

     1  // Copyright 2021 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package noder
     6  
     7  import (
     8  	"go/constant"
     9  
    10  	"cmd/compile/internal/base"
    11  	"cmd/compile/internal/ir"
    12  	"cmd/compile/internal/typecheck"
    13  	"cmd/compile/internal/types"
    14  	"cmd/internal/src"
    15  )
    16  
    17  // Helpers for constructing typed IR nodes.
    18  //
    19  // TODO(mdempsky): Move into their own package so they can be easily
    20  // reused by iimport and frontend optimizations.
    21  
    22  type ImplicitNode interface {
    23  	ir.Node
    24  	SetImplicit(x bool)
    25  }
    26  
    27  // Implicit returns n after marking it as Implicit.
    28  func Implicit(n ImplicitNode) ImplicitNode {
    29  	n.SetImplicit(true)
    30  	return n
    31  }
    32  
    33  // typed returns n after setting its type to typ.
    34  func typed(typ *types.Type, n ir.Node) ir.Node {
    35  	n.SetType(typ)
    36  	n.SetTypecheck(1)
    37  	return n
    38  }
    39  
    40  // Values
    41  
    42  func Const(pos src.XPos, typ *types.Type, val constant.Value) ir.Node {
    43  	return typed(typ, ir.NewBasicLit(pos, val))
    44  }
    45  
    46  func Nil(pos src.XPos, typ *types.Type) ir.Node {
    47  	return typed(typ, ir.NewNilExpr(pos))
    48  }
    49  
    50  // Expressions
    51  
    52  func Addr(pos src.XPos, x ir.Node) *ir.AddrExpr {
    53  	n := typecheck.NodAddrAt(pos, x)
    54  	switch x.Op() {
    55  	case ir.OARRAYLIT, ir.OMAPLIT, ir.OSLICELIT, ir.OSTRUCTLIT:
    56  		n.SetOp(ir.OPTRLIT)
    57  	}
    58  	typed(types.NewPtr(x.Type()), n)
    59  	return n
    60  }
    61  
    62  func Assert(pos src.XPos, x ir.Node, typ *types.Type) ir.Node {
    63  	return typed(typ, ir.NewTypeAssertExpr(pos, x, nil))
    64  }
    65  
    66  func Binary(pos src.XPos, op ir.Op, typ *types.Type, x, y ir.Node) ir.Node {
    67  	switch op {
    68  	case ir.OANDAND, ir.OOROR:
    69  		return typed(x.Type(), ir.NewLogicalExpr(pos, op, x, y))
    70  	case ir.OADD:
    71  		n := ir.NewBinaryExpr(pos, op, x, y)
    72  		if x.Type().HasTParam() || y.Type().HasTParam() {
    73  			// Delay transformAdd() if either arg has a type param,
    74  			// since it needs to know the exact types to decide whether
    75  			// to transform OADD to OADDSTR.
    76  			n.SetType(typ)
    77  			n.SetTypecheck(3)
    78  			return n
    79  		}
    80  		typed(typ, n)
    81  		return transformAdd(n)
    82  	default:
    83  		return typed(x.Type(), ir.NewBinaryExpr(pos, op, x, y))
    84  	}
    85  }
    86  
    87  func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool) ir.Node {
    88  	n := ir.NewCallExpr(pos, ir.OCALL, fun, args)
    89  	n.IsDDD = dots
    90  	// n.Use will be changed to ir.CallUseStmt in g.stmt() if this call is
    91  	// just a statement (any return values are ignored).
    92  	n.Use = ir.CallUseExpr
    93  
    94  	if fun.Op() == ir.OTYPE {
    95  		// Actually a type conversion, not a function call.
    96  		if fun.Type().HasTParam() || args[0].Type().HasTParam() {
    97  			// For type params, don't typecheck until we actually know
    98  			// the type.
    99  			return typed(typ, n)
   100  		}
   101  		typed(typ, n)
   102  		return transformConvCall(n)
   103  	}
   104  
   105  	if fun, ok := fun.(*ir.Name); ok && fun.BuiltinOp != 0 {
   106  		// For Builtin ops, we currently stay with using the old
   107  		// typechecker to transform the call to a more specific expression
   108  		// and possibly use more specific ops. However, for a bunch of the
   109  		// ops, we delay doing the old typechecker if any of the args have
   110  		// type params, for a variety of reasons:
   111  		//
   112  		// OMAKE: hard to choose specific ops OMAKESLICE, etc. until arg type is known
   113  		// OREAL/OIMAG: can't determine type float32/float64 until arg type know
   114  		// OLEN/OCAP: old typechecker will complain if arg is not obviously a slice/array.
   115  		// OAPPEND: old typechecker will complain if arg is not obviously slice, etc.
   116  		//
   117  		// We will eventually break out the transforming functionality
   118  		// needed for builtin's, and call it here or during stenciling, as
   119  		// appropriate.
   120  		switch fun.BuiltinOp {
   121  		case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OLEN, ir.OCAP, ir.OAPPEND:
   122  			hasTParam := false
   123  			for _, arg := range args {
   124  				if arg.Type().HasTParam() {
   125  					hasTParam = true
   126  					break
   127  				}
   128  			}
   129  			if hasTParam {
   130  				return typed(typ, n)
   131  			}
   132  		}
   133  
   134  		typed(typ, n)
   135  		return transformBuiltin(n)
   136  	}
   137  
   138  	// Add information, now that we know that fun is actually being called.
   139  	switch fun := fun.(type) {
   140  	case *ir.ClosureExpr:
   141  		fun.Func.SetClosureCalled(true)
   142  	case *ir.SelectorExpr:
   143  		if fun.Op() == ir.OCALLPART {
   144  			op := ir.ODOTMETH
   145  			if fun.X.Type().IsInterface() {
   146  				op = ir.ODOTINTER
   147  			}
   148  			fun.SetOp(op)
   149  			// Set the type to include the receiver, since that's what
   150  			// later parts of the compiler expect
   151  			fun.SetType(fun.Selection.Type)
   152  		}
   153  	}
   154  
   155  	if fun.Type().HasTParam() {
   156  		// If the fun arg is or has a type param, don't do any extra
   157  		// transformations, since we may not have needed properties yet
   158  		// (e.g. number of return values, etc). The type param is probably
   159  		// described by a structural constraint that requires it to be a
   160  		// certain function type, etc., but we don't want to analyze that.
   161  		return typed(typ, n)
   162  	}
   163  
   164  	if fun.Op() == ir.OXDOT {
   165  		if !fun.(*ir.SelectorExpr).X.Type().HasTParam() {
   166  			base.FatalfAt(pos, "Expecting type param receiver in %v", fun)
   167  		}
   168  		// For methods called in a generic function, don't do any extra
   169  		// transformations. We will do those later when we create the
   170  		// instantiated function and have the correct receiver type.
   171  		typed(typ, n)
   172  		return n
   173  	}
   174  	if fun.Op() != ir.OFUNCINST {
   175  		// If no type params, do the normal call transformations. This
   176  		// will convert OCALL to OCALLFUNC.
   177  		typed(typ, n)
   178  		transformCall(n)
   179  		return n
   180  	}
   181  
   182  	// Leave the op as OCALL, which indicates the call still needs typechecking.
   183  	typed(typ, n)
   184  	return n
   185  }
   186  
   187  func Compare(pos src.XPos, typ *types.Type, op ir.Op, x, y ir.Node) ir.Node {
   188  	n := ir.NewBinaryExpr(pos, op, x, y)
   189  	if x.Type().HasTParam() || y.Type().HasTParam() {
   190  		// Delay transformCompare() if either arg has a type param, since
   191  		// it needs to know the exact types to decide on any needed conversions.
   192  		n.SetType(typ)
   193  		n.SetTypecheck(3)
   194  		return n
   195  	}
   196  	typed(typ, n)
   197  	transformCompare(n)
   198  	return n
   199  }
   200  
   201  func Deref(pos src.XPos, typ *types.Type, x ir.Node) *ir.StarExpr {
   202  	n := ir.NewStarExpr(pos, x)
   203  	typed(typ, n)
   204  	return n
   205  }
   206  
   207  func DotField(pos src.XPos, x ir.Node, index int) *ir.SelectorExpr {
   208  	op, typ := ir.ODOT, x.Type()
   209  	if typ.IsPtr() {
   210  		op, typ = ir.ODOTPTR, typ.Elem()
   211  	}
   212  	if !typ.IsStruct() {
   213  		base.FatalfAt(pos, "DotField of non-struct: %L", x)
   214  	}
   215  
   216  	// TODO(mdempsky): This is the backend's responsibility.
   217  	types.CalcSize(typ)
   218  
   219  	field := typ.Field(index)
   220  	return dot(pos, field.Type, op, x, field)
   221  }
   222  
   223  func DotMethod(pos src.XPos, x ir.Node, index int) *ir.SelectorExpr {
   224  	method := method(x.Type(), index)
   225  
   226  	// Method value.
   227  	typ := typecheck.NewMethodType(method.Type, nil)
   228  	return dot(pos, typ, ir.OCALLPART, x, method)
   229  }
   230  
   231  // MethodExpr returns a OMETHEXPR node with the indicated index into the methods
   232  // of typ. The receiver type is set from recv, which is different from typ if the
   233  // method was accessed via embedded fields. Similarly, the X value of the
   234  // ir.SelectorExpr is recv, the original OTYPE node before passing through the
   235  // embedded fields.
   236  func MethodExpr(pos src.XPos, recv ir.Node, embed *types.Type, index int) *ir.SelectorExpr {
   237  	method := method(embed, index)
   238  	typ := typecheck.NewMethodType(method.Type, recv.Type())
   239  	// The method expression T.m requires a wrapper when T
   240  	// is different from m's declared receiver type. We
   241  	// normally generate these wrappers while writing out
   242  	// runtime type descriptors, which is always done for
   243  	// types declared at package scope. However, we need
   244  	// to make sure to generate wrappers for anonymous
   245  	// receiver types too.
   246  	if recv.Sym() == nil {
   247  		typecheck.NeedRuntimeType(recv.Type())
   248  	}
   249  	return dot(pos, typ, ir.OMETHEXPR, recv, method)
   250  }
   251  
   252  func dot(pos src.XPos, typ *types.Type, op ir.Op, x ir.Node, selection *types.Field) *ir.SelectorExpr {
   253  	n := ir.NewSelectorExpr(pos, op, x, selection.Sym)
   254  	n.Selection = selection
   255  	typed(typ, n)
   256  	return n
   257  }
   258  
   259  // TODO(mdempsky): Move to package types.
   260  func method(typ *types.Type, index int) *types.Field {
   261  	if typ.IsInterface() {
   262  		return typ.AllMethods().Index(index)
   263  	}
   264  	return types.ReceiverBaseType(typ).Methods().Index(index)
   265  }
   266  
   267  func Index(pos src.XPos, typ *types.Type, x, index ir.Node) ir.Node {
   268  	n := ir.NewIndexExpr(pos, x, index)
   269  	if x.Type().HasTParam() {
   270  		// transformIndex needs to know exact type
   271  		n.SetType(typ)
   272  		n.SetTypecheck(3)
   273  		return n
   274  	}
   275  	typed(typ, n)
   276  	// transformIndex will modify n.Type() for OINDEXMAP.
   277  	transformIndex(n)
   278  	return n
   279  }
   280  
   281  func Slice(pos src.XPos, typ *types.Type, x, low, high, max ir.Node) ir.Node {
   282  	op := ir.OSLICE
   283  	if max != nil {
   284  		op = ir.OSLICE3
   285  	}
   286  	n := ir.NewSliceExpr(pos, op, x, low, high, max)
   287  	if x.Type().HasTParam() {
   288  		// transformSlice needs to know if x.Type() is a string or an array or a slice.
   289  		n.SetType(typ)
   290  		n.SetTypecheck(3)
   291  		return n
   292  	}
   293  	typed(typ, n)
   294  	transformSlice(n)
   295  	return n
   296  }
   297  
   298  func Unary(pos src.XPos, typ *types.Type, op ir.Op, x ir.Node) ir.Node {
   299  	switch op {
   300  	case ir.OADDR:
   301  		return Addr(pos, x)
   302  	case ir.ODEREF:
   303  		return Deref(pos, typ, x)
   304  	}
   305  
   306  	if op == ir.ORECV {
   307  		if typ.IsFuncArgStruct() && typ.NumFields() == 2 {
   308  			// Remove the second boolean type (if provided by type2),
   309  			// since that works better with the rest of the compiler
   310  			// (which will add it back in later).
   311  			assert(typ.Field(1).Type.Kind() == types.TBOOL)
   312  			typ = typ.Field(0).Type
   313  		}
   314  	}
   315  	return typed(typ, ir.NewUnaryExpr(pos, op, x))
   316  }
   317  
   318  // Statements
   319  
   320  var one = constant.MakeInt64(1)
   321  
   322  func IncDec(pos src.XPos, op ir.Op, x ir.Node) *ir.AssignOpStmt {
   323  	assert(x.Type() != nil)
   324  	return ir.NewAssignOpStmt(pos, op, x, typecheck.DefaultLit(ir.NewBasicLit(pos, one), x.Type()))
   325  }
   326  

View as plain text