Newer
Older
if sel, ok := w.p.info.Selections[selector]; ok && sel.Kind() == types2.MethodVal {
w.Bool(true) // method call
typ := w.recvExpr(selector, sel)
w.methodExpr(selector, typ, sel)
return
}
}
w.Bool(false) // not a method call (i.e., normal function call)
if obj, inst := lookupObj(w.p, fun); w.Bool(obj != nil && inst.TypeArgs.Len() != 0) {
obj := obj.(*types2.Func)
w.pos(fun)
w.funcInst(obj, inst.TypeArgs)
return
}
w.expr(fun)
Matthew Dempsky
committed
sigType := types2.CoreType(tv.Type).(*types2.Signature)
paramTypes := sigType.Params()
w.Code(exprCall)
writeFunExpr()
Matthew Dempsky
committed
Matthew Dempsky
committed
paramType := func(i int) types2.Type {
if sigType.Variadic() && !expr.HasDots && i >= paramTypes.Len()-1 {
return paramTypes.At(paramTypes.Len() - 1).Type().(*types2.Slice).Elem()
}
return paramTypes.At(i).Type()
Matthew Dempsky
committed
w.multiExpr(expr, paramType, expr.ArgList)
w.Bool(expr.HasDots)
if rtype != nil {
w.rtype(rtype)
}
func sliceElem(typ types2.Type) types2.Type {
return types2.CoreType(typ).(*types2.Slice).Elem()
}
Matthew Dempsky
committed
func (w *writer) optExpr(expr syntax.Expr) {
if w.Bool(expr != nil) {
w.expr(expr)
}
}
// recvExpr writes out expr.X, but handles any implicit addressing,
// dereferencing, and field selections appropriate for the method
// selection.
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
func (w *writer) recvExpr(expr *syntax.SelectorExpr, sel *types2.Selection) types2.Type {
index := sel.Index()
implicits := index[:len(index)-1]
w.Code(exprRecv)
w.expr(expr.X)
w.pos(expr)
w.Len(len(implicits))
typ := w.p.typeOf(expr.X)
for _, ix := range implicits {
typ = deref2(typ).Underlying().(*types2.Struct).Field(ix).Type()
w.Len(ix)
}
recv := sel.Obj().(*types2.Func).Type().(*types2.Signature).Recv().Type()
if w.Bool(isPtrTo(typ, recv)) { // needs deref
typ = recv
} else if w.Bool(isPtrTo(recv, typ)) { // needs addr
typ = recv
}
return typ
}
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
// funcInst writes a reference to an instantiated function.
func (w *writer) funcInst(obj *types2.Func, targs *types2.TypeList) {
info := w.p.objInstIdx(obj, targs, w.dict)
// Type arguments list contains derived types; we can emit a static
// call to the shaped function, but need to dynamically compute the
// runtime dictionary pointer.
if w.Bool(info.anyDerived()) {
w.Len(w.dict.subdictIdx(info))
return
}
// Type arguments list is statically known; we can emit a static
// call with a statically reference to the respective runtime
// dictionary.
w.objInfo(info)
}
// methodExpr writes out a reference to the method selected by
// expr. sel should be the corresponding types2.Selection, and recv
// the type produced after any implicit addressing, dereferencing, and
// field selection. (Note: recv might differ from sel.Obj()'s receiver
// parameter in the case of interface types, and is needed for
// handling type parameter methods.)
func (w *writer) methodExpr(expr *syntax.SelectorExpr, recv types2.Type, sel *types2.Selection) {
fun := sel.Obj().(*types2.Func)
sig := fun.Type().(*types2.Signature)
w.typ(recv)
w.typ(sig)
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
w.pos(expr)
w.selector(fun)
// Method on a type parameter. These require an indirect call
// through the current function's runtime dictionary.
if typeParam, ok := recv.(*types2.TypeParam); w.Bool(ok) {
typeParamIdx := w.dict.typeParamIndex(typeParam)
methodInfo := w.p.selectorIdx(fun)
w.Len(w.dict.typeParamMethodExprIdx(typeParamIdx, methodInfo))
return
}
if isInterface(recv) != isInterface(sig.Recv().Type()) {
w.p.fatalf(expr, "isInterface inconsistency: %v and %v", recv, sig.Recv().Type())
}
if !isInterface(recv) {
if named, ok := deref2(recv).(*types2.Named); ok {
obj, targs := splitNamed(named)
info := w.p.objInstIdx(obj, targs, w.dict)
// Method on a derived receiver type. These can be handled by a
// static call to the shaped method, but require dynamically
// looking up the appropriate dictionary argument in the current
// function's runtime dictionary.
if w.p.hasImplicitTypeParams(obj) || info.anyDerived() {
w.Bool(true) // dynamic subdictionary
w.Len(w.dict.subdictIdx(info))
return
}
// Method on a fully known receiver type. These can be handled
// by a static call to the shaped method, and with a static
// reference to the receiver type's dictionary.
if targs.Len() != 0 {
w.Bool(false) // no dynamic subdictionary
w.Bool(true) // static dictionary
w.objInfo(info)
return
}
}
}
w.Bool(false) // no dynamic subdictionary
w.Bool(false) // no static dictionary
}
Matthew Dempsky
committed
// multiExpr writes a sequence of expressions, where the i'th value is
// implicitly converted to dstType(i). It also handles when exprs is a
// single, multi-valued expression (e.g., the multi-valued argument in
// an f(g()) call, or the RHS operand in a comma-ok assignment).
func (w *writer) multiExpr(pos poser, dstType func(int) types2.Type, exprs []syntax.Expr) {
w.Sync(pkgbits.SyncMultiExpr)
if len(exprs) == 1 {
expr := exprs[0]
if tuple, ok := w.p.typeOf(expr).(*types2.Tuple); ok {
assert(tuple.Len() > 1)
Matthew Dempsky
committed
w.Bool(true) // N:1 assignment
w.pos(pos)
w.expr(expr)
w.Len(tuple.Len())
for i := 0; i < tuple.Len(); i++ {
src := tuple.At(i).Type()
// TODO(mdempsky): Investigate not writing src here. I think
// the reader should be able to infer it from expr anyway.
w.typ(src)
if dst := dstType(i); w.Bool(dst != nil && !types2.Identical(src, dst)) {
if src == nil || dst == nil {
w.p.fatalf(pos, "src is %v, dst is %v", src, dst)
}
if !types2.AssignableTo(src, dst) {
w.p.fatalf(pos, "%v is not assignable to %v", src, dst)
}
w.typ(dst)
w.convRTTI(src, dst)
Matthew Dempsky
committed
}
}
Matthew Dempsky
committed
return
}
}
Matthew Dempsky
committed
w.Bool(false) // N:N assignment
w.Len(len(exprs))
Matthew Dempsky
committed
for i, expr := range exprs {
w.implicitConvExpr(dstType(i), expr)
Matthew Dempsky
committed
}
}
// implicitConvExpr is like expr, but if dst is non-nil and different
// from expr's type, then an implicit conversion operation is inserted
// at expr's position.
func (w *writer) implicitConvExpr(dst types2.Type, expr syntax.Expr) {
w.convertExpr(dst, expr, true)
}
func (w *writer) convertExpr(dst types2.Type, expr syntax.Expr, implicit bool) {
Matthew Dempsky
committed
src := w.p.typeOf(expr)
// Omit implicit no-op conversions.
identical := dst == nil || types2.Identical(src, dst)
if implicit && identical {
w.expr(expr)
return
Matthew Dempsky
committed
}
if implicit && !types2.AssignableTo(src, dst) {
w.p.fatalf(expr, "%v is not assignable to %v", src, dst)
}
w.Code(exprConvert)
w.Bool(implicit)
w.typ(dst)
w.pos(expr)
w.convRTTI(src, dst)
w.Bool(isTypeParam(dst))
Matthew Dempsky
committed
w.Bool(identical)
Matthew Dempsky
committed
w.expr(expr)
}
func (w *writer) compLit(lit *syntax.CompositeLit) {
typ := w.p.typeOf(lit)
w.Sync(pkgbits.SyncCompLit)
w.typ(typ)
if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok {
typ = ptr.Elem()
}
Matthew Dempsky
committed
var keyType, elemType types2.Type
var structType *types2.Struct
switch typ0 := typ; typ := types2.CoreType(typ).(type) {
Matthew Dempsky
committed
default:
w.p.fatalf(lit, "unexpected composite literal type: %v", typ)
case *types2.Array:
elemType = typ.Elem()
case *types2.Map:
w.rtype(typ0)
Matthew Dempsky
committed
keyType, elemType = typ.Key(), typ.Elem()
case *types2.Slice:
elemType = typ.Elem()
case *types2.Struct:
structType = typ
}
w.Len(len(lit.ElemList))
for i, elem := range lit.ElemList {
Matthew Dempsky
committed
elemType := elemType
if structType != nil {
if kv, ok := elem.(*syntax.KeyValueExpr); ok {
// use position of expr.Key rather than of elem (which has position of ':')
w.pos(kv.Key)
Matthew Dempsky
committed
i = fieldIndex(w.p.info, structType, kv.Key.(*syntax.Name))
elem = kv.Value
} else {
w.pos(elem)
}
Matthew Dempsky
committed
elemType = structType.Field(i).Type()
w.Len(i)
if kv, ok := elem.(*syntax.KeyValueExpr); w.Bool(ok) {
// use position of expr.Key rather than of elem (which has position of ':')
w.pos(kv.Key)
w.implicitConvExpr(keyType, kv.Key)
elem = kv.Value
}
}
w.pos(elem)
w.implicitConvExpr(elemType, elem)
}
}
func (w *writer) funcLit(expr *syntax.FuncLit) {
sig := w.p.typeOf(expr).(*types2.Signature)
body, closureVars := w.p.bodyIdx(sig, expr.Body, w.dict)
w.Sync(pkgbits.SyncFuncLit)
w.pos(expr)
w.signature(sig)
w.Len(len(closureVars))
for _, cv := range closureVars {
w.pos(cv.pos)
w.useLocal(cv.pos, cv.var_)
w.Reloc(pkgbits.RelocBody, body)
type posVar struct {
pos syntax.Pos
var_ *types2.Var
func (p posVar) String() string {
return p.pos.String() + ":" + p.var_.String()
}
func (w *writer) exprList(expr syntax.Expr) {
w.Sync(pkgbits.SyncExprList)
w.exprs(syntax.UnpackListExpr(expr))
}
func (w *writer) exprs(exprs []syntax.Expr) {
w.Sync(pkgbits.SyncExprs)
w.Len(len(exprs))
for _, expr := range exprs {
w.expr(expr)
}
}
// rtype writes information so that the reader can construct an
// expression of type *runtime._type representing typ.
func (w *writer) rtype(typ types2.Type) {
typ = types2.Default(typ)
info := w.p.typIdx(typ, w.dict)
w.rtypeInfo(info)
}
func (w *writer) rtypeInfo(info typeInfo) {
w.Sync(pkgbits.SyncRType)
if w.Bool(info.derived) {
w.Len(w.dict.rtypeIdx(info))
} else {
w.typInfo(info)
}
}
// varDictIndex writes out information for populating DictIndex for
// the ir.Name that will represent obj.
func (w *writer) varDictIndex(obj *types2.Var) {
info := w.p.typIdx(obj.Type(), w.dict)
if w.Bool(info.derived) {
w.Len(w.dict.rtypeIdx(info))
}
}
func isUntyped(typ types2.Type) bool {
basic, ok := typ.(*types2.Basic)
return ok && basic.Info()&types2.IsUntyped != 0
}
func isTuple(typ types2.Type) bool {
_, ok := typ.(*types2.Tuple)
return ok
}
func (w *writer) itab(typ, iface types2.Type) {
typ = types2.Default(typ)
iface = types2.Default(iface)
typInfo := w.p.typIdx(typ, w.dict)
ifaceInfo := w.p.typIdx(iface, w.dict)
w.rtypeInfo(typInfo)
w.rtypeInfo(ifaceInfo)
if w.Bool(typInfo.derived || ifaceInfo.derived) {
w.Len(w.dict.itabIdx(typInfo, ifaceInfo))
}
// convRTTI writes information so that the reader can construct
// expressions for converting from src to dst.
func (w *writer) convRTTI(src, dst types2.Type) {
w.Sync(pkgbits.SyncConvRTTI)
w.itab(src, dst)
}
func (w *writer) exprType(iface types2.Type, typ syntax.Expr) {
base.Assertf(iface == nil || isInterface(iface), "%v must be nil or an interface type", iface)
tv := w.p.typeAndValue(typ)
assert(tv.IsType())
w.Sync(pkgbits.SyncExprType)
w.pos(typ)
if w.Bool(iface != nil && !iface.Underlying().(*types2.Interface).Empty()) {
w.itab(tv.Type, iface)
} else {
w.rtype(tv.Type)
info := w.p.typIdx(tv.Type, w.dict)
w.Bool(info.derived)
}
}
// isInterface reports whether typ is known to be an interface type.
// If typ is a type parameter, then isInterface reports an internal
// compiler error instead.
func isInterface(typ types2.Type) bool {
if _, ok := typ.(*types2.TypeParam); ok {
// typ is a type parameter and may be instantiated as either a
// concrete or interface type, so the writer can't depend on
// knowing this.
base.Fatalf("%v is a type parameter", typ)
}
_, ok := typ.Underlying().(*types2.Interface)
return ok
}
// op writes an Op into the bitstream.
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
// priority at the moment.
assert(op != 0)
w.Sync(pkgbits.SyncOp)
w.Len(int(op))
}
// @@@ Package initialization
// Caution: This code is still clumsy, because toolstash -cmp is
// particularly sensitive to it.
type typeDeclGen struct {
*syntax.TypeDecl
gen int
// Implicit type parameters in scope at this type declaration.
implicits []*types2.TypeName
type fileImports struct {
importedEmbed, importedUnsafe bool
}
// declCollector is a visitor type that collects compiler-needed
// information about declarations that types2 doesn't track.
//
// Notably, it maps declared types and functions back to their
// declaration statement, keeps track of implicit type parameters, and
// assigns unique type "generation" numbers to local defined types.
type declCollector struct {
pw *pkgWriter
typegen *int
file *fileImports
withinFunc bool
implicits []*types2.TypeName
}
func (c *declCollector) withTParams(obj types2.Object) *declCollector {
tparams := objTypeParams(obj)
Robert Griesemer
committed
n := tparams.Len()
if n == 0 {
return c
}
copy := *c
copy.implicits = copy.implicits[:len(copy.implicits):len(copy.implicits)]
Robert Griesemer
committed
for i := 0; i < n; i++ {
Robert Griesemer
committed
copy.implicits = append(copy.implicits, tparams.At(i).Obj())
Robert Griesemer
committed
}
return ©
func (c *declCollector) Visit(n syntax.Node) syntax.Visitor {
pw := c.pw
switch n := n.(type) {
case *syntax.File:
pw.checkPragmas(n.Pragma, ir.GoBuildPragma, false)
case *syntax.ImportDecl:
pw.checkPragmas(n.Pragma, 0, false)
switch pkgNameOf(pw.info, n).Imported().Path() {
case "embed":
c.file.importedEmbed = true
case "unsafe":
c.file.importedUnsafe = true
}
case *syntax.ConstDecl:
pw.checkPragmas(n.Pragma, 0, false)
case *syntax.FuncDecl:
pw.checkPragmas(n.Pragma, funcPragmas, false)
obj := pw.info.Defs[n.Name].(*types2.Func)
pw.funDecls[obj] = n
return c.withTParams(obj)
case *syntax.TypeDecl:
obj := pw.info.Defs[n.Name].(*types2.TypeName)
d := typeDeclGen{TypeDecl: n, implicits: c.implicits}
if n.Alias {
pw.checkPragmas(n.Pragma, 0, false)
} else {
pw.checkPragmas(n.Pragma, 0, false)
// Assign a unique ID to function-scoped defined types.
if c.withinFunc {
*c.typegen++
d.gen = *c.typegen
}
}
pw.typDecls[obj] = d
// TODO(mdempsky): Omit? Not strictly necessary; only matters for
// type declarations within function literals within parameterized
// type declarations, but types2 the function literals will be
// constant folded away.
return c.withTParams(obj)
case *syntax.VarDecl:
pw.checkPragmas(n.Pragma, 0, true)
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
if p, ok := n.Pragma.(*pragmas); ok && len(p.Embeds) > 0 {
if err := checkEmbed(n, c.file.importedEmbed, c.withinFunc); err != nil {
pw.errorf(p.Embeds[0].Pos, "%s", err)
}
}
case *syntax.BlockStmt:
if !c.withinFunc {
copy := *c
copy.withinFunc = true
return ©
}
}
return c
}
func (pw *pkgWriter) collectDecls(noders []*noder) {
var typegen int
for _, p := range noders {
var file fileImports
syntax.Walk(p.file, &declCollector{
pw: pw,
typegen: &typegen,
file: &file,
})
pw.cgoPragmas = append(pw.cgoPragmas, p.pragcgobuf...)
for _, l := range p.linknames {
if !file.importedUnsafe {
pw.errorf(l.pos, "//go:linkname only allowed in Go files that import \"unsafe\"")
continue
}
switch obj := pw.curpkg.Scope().Lookup(l.local).(type) {
case *types2.Func, *types2.Var:
if _, ok := pw.linknames[obj]; !ok {
pw.linknames[obj] = l.remote
} else {
pw.errorf(l.pos, "duplicate //go:linkname for %s", l.local)
}
default:
if types.AllowsGoVersion(1, 18) {
pw.errorf(l.pos, "//go:linkname must refer to declared function or variable")
}
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
}
}
}
}
func (pw *pkgWriter) checkPragmas(p syntax.Pragma, allowed ir.PragmaFlag, embedOK bool) {
if p == nil {
return
}
pragma := p.(*pragmas)
for _, pos := range pragma.Pos {
if pos.Flag&^allowed != 0 {
pw.errorf(pos.Pos, "misplaced compiler directive")
}
}
if !embedOK {
for _, e := range pragma.Embeds {
pw.errorf(e.Pos, "misplaced go:embed directive")
}
}
}
func (w *writer) pkgInit(noders []*noder) {
w.Len(len(w.p.cgoPragmas))
for _, cgoPragma := range w.p.cgoPragmas {
w.Strings(cgoPragma)
w.pkgInitOrder()
w.Sync(pkgbits.SyncDecls)
for _, p := range noders {
for _, decl := range p.file.DeclList {
w.pkgDecl(decl)
}
}
w.Code(declEnd)
w.Sync(pkgbits.SyncEOF)
}
func (w *writer) pkgInitOrder() {
// TODO(mdempsky): Write as a function body instead?
w.Len(len(w.p.info.InitOrder))
for _, init := range w.p.info.InitOrder {
w.Len(len(init.Lhs))
for _, v := range init.Lhs {
w.obj(v, nil)
}
w.expr(init.Rhs)
}
}
func (w *writer) pkgDecl(decl syntax.Decl) {
switch decl := decl.(type) {
default:
w.p.unexpected("declaration", decl)
case *syntax.ImportDecl:
case *syntax.ConstDecl:
w.Code(declOther)
w.pkgObjs(decl.NameList...)
case *syntax.FuncDecl:
if decl.Name.Value == "_" {
break // skip blank functions
}
obj := w.p.info.Defs[decl.Name].(*types2.Func)
sig := obj.Type().(*types2.Signature)
if sig.RecvTypeParams() != nil || sig.TypeParams() != nil {
break // skip generic functions
}
if recv := sig.Recv(); recv != nil {
w.Code(declMethod)
w.typ(recvBase(recv))
w.selector(obj)
break
}
w.Code(declFunc)
w.pkgObjs(decl.Name)
case *syntax.TypeDecl:
if len(decl.TParamList) != 0 {
break // skip generic type decls
}
if decl.Name.Value == "_" {
break // skip blank type decls
}
name := w.p.info.Defs[decl.Name].(*types2.TypeName)
// Skip type declarations for interfaces that are only usable as
// type parameter bounds.
Robert Griesemer
committed
if iface, ok := name.Type().Underlying().(*types2.Interface); ok && !iface.IsMethodSet() {
break
}
w.Code(declOther)
w.pkgObjs(decl.Name)
case *syntax.VarDecl:
w.Code(declVar)
w.pkgObjs(decl.NameList...)
Matthew Dempsky
committed
var embeds []pragmaEmbed
if p, ok := decl.Pragma.(*pragmas); ok {
embeds = p.Embeds
}
w.Len(len(embeds))
for _, embed := range embeds {
w.pos(embed.Pos)
w.Strings(embed.Patterns)
}
}
}
func (w *writer) pkgObjs(names ...*syntax.Name) {
w.Sync(pkgbits.SyncDeclNames)
w.Len(len(names))
for _, name := range names {
obj, ok := w.p.info.Defs[name]
assert(ok)
w.Sync(pkgbits.SyncDeclName)
w.obj(obj, nil)
}
}
// @@@ Helpers
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
// staticBool analyzes a boolean expression and reports whether it's
// always true (positive result), always false (negative result), or
// unknown (zero).
//
// It also simplifies the expression while preserving semantics, if
// possible.
func (pw *pkgWriter) staticBool(ep *syntax.Expr) int {
if val := pw.typeAndValue(*ep).Value; val != nil {
if constant.BoolVal(val) {
return +1
} else {
return -1
}
}
if e, ok := (*ep).(*syntax.Operation); ok {
switch e.Op {
case syntax.Not:
return pw.staticBool(&e.X)
case syntax.AndAnd:
x := pw.staticBool(&e.X)
if x < 0 {
*ep = e.X
return x
}
y := pw.staticBool(&e.Y)
if x > 0 || y < 0 {
if pw.typeAndValue(e.X).Value != nil {
*ep = e.Y
}
return y
}
case syntax.OrOr:
x := pw.staticBool(&e.X)
if x > 0 {
*ep = e.X
return x
}
y := pw.staticBool(&e.Y)
if x < 0 || y > 0 {
if pw.typeAndValue(e.X).Value != nil {
*ep = e.Y
}
return y
}
}
}
return 0
}
// hasImplicitTypeParams reports whether obj is a defined type with
// implicit type parameters (e.g., declared within a generic function
// or method).
func (pw *pkgWriter) hasImplicitTypeParams(obj *types2.TypeName) bool {
if obj.Pkg() == pw.curpkg {
decl, ok := pw.typDecls[obj]
assert(ok)
if len(decl.implicits) != 0 {
return true
}
}
return false
}
// isDefinedType reports whether obj is a defined type.
func isDefinedType(obj types2.Object) bool {
if obj, ok := obj.(*types2.TypeName); ok {
return !obj.IsAlias()
}
return false
}
// isGlobal reports whether obj was declared at package scope.
//
// Caveat: blank objects are not declared.
func isGlobal(obj types2.Object) bool {
return obj.Parent() == obj.Pkg().Scope()
}
// lookupObj returns the object that expr refers to, if any. If expr
Robert Griesemer
committed
// is an explicit instantiation of a generic object, then the instance
// object is returned as well.
func lookupObj(p *pkgWriter, expr syntax.Expr) (obj types2.Object, inst types2.Instance) {
if index, ok := expr.(*syntax.IndexExpr); ok {
args := syntax.UnpackListExpr(index.Index)
Robert Griesemer
committed
if len(args) == 1 {
tv := p.typeAndValue(args[0])
Robert Griesemer
committed
if tv.IsValue() {
return // normal index expression
}
}
expr = index.X
}
// Strip package qualifier, if present.
if sel, ok := expr.(*syntax.SelectorExpr); ok {
if !isPkgQual(p.info, sel) {
return // normal selector expression
}
expr = sel.Sel
}
if name, ok := expr.(*syntax.Name); ok {
obj = p.info.Uses[name]
inst = p.info.Instances[name]
}
return
}
// isPkgQual reports whether the given selector expression is a
// package-qualified identifier.
func isPkgQual(info *types2.Info, sel *syntax.SelectorExpr) bool {
if name, ok := sel.X.(*syntax.Name); ok {
_, isPkgName := info.Uses[name].(*types2.PkgName)
return isPkgName
}
return false
}
// isNil reports whether expr is a (possibly parenthesized) reference
// to the predeclared nil value.
func isNil(p *pkgWriter, expr syntax.Expr) bool {
tv := p.typeAndValue(expr)
return tv.IsNil()
}
// isBuiltin reports whether expr is a (possibly parenthesized)
// referenced to the specified built-in function.
func (pw *pkgWriter) isBuiltin(expr syntax.Expr, builtin string) bool {
if name, ok := syntax.Unparen(expr).(*syntax.Name); ok && name.Value == builtin {
return pw.typeAndValue(name).IsBuiltin()
}
return false
}
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
// recvBase returns the base type for the given receiver parameter.
func recvBase(recv *types2.Var) *types2.Named {
typ := recv.Type()
if ptr, ok := typ.(*types2.Pointer); ok {
typ = ptr.Elem()
}
return typ.(*types2.Named)
}
// namesAsExpr returns a list of names as a syntax.Expr.
func namesAsExpr(names []*syntax.Name) syntax.Expr {
if len(names) == 1 {
return names[0]
}
exprs := make([]syntax.Expr, len(names))
for i, name := range names {
exprs[i] = name
}
return &syntax.ListExpr{ElemList: exprs}
}
// fieldIndex returns the index of the struct field named by key.
func fieldIndex(info *types2.Info, str *types2.Struct, key *syntax.Name) int {
field := info.Uses[key].(*types2.Var)
for i := 0; i < str.NumFields(); i++ {
if str.Field(i) == field {
return i
}
}
panic(fmt.Sprintf("%s: %v is not a field of %v", key.Pos(), field, str))
}
// objTypeParams returns the type parameters on the given object.
func objTypeParams(obj types2.Object) *types2.TypeParamList {
switch obj := obj.(type) {
case *types2.Func:
sig := obj.Type().(*types2.Signature)
if sig.Recv() != nil {
return sig.RecvTypeParams()
}
return sig.TypeParams()
case *types2.TypeName:
if !obj.IsAlias() {
return obj.Type().(*types2.Named).TypeParams()
}
}
return nil
}
// splitNamed decomposes a use of a defined type into its original
// type definition and the type arguments used to instantiate it.
func splitNamed(typ *types2.Named) (*types2.TypeName, *types2.TypeList) {
base.Assertf(typ.TypeParams().Len() == typ.TypeArgs().Len(), "use of uninstantiated type: %v", typ)
orig := typ.Origin()
base.Assertf(orig.TypeArgs() == nil, "origin %v of %v has type arguments", orig, typ)
base.Assertf(typ.Obj() == orig.Obj(), "%v has object %v, but %v has object %v", typ, typ.Obj(), orig, orig.Obj())
return typ.Obj(), typ.TypeArgs()
}
func asPragmaFlag(p syntax.Pragma) ir.PragmaFlag {
if p == nil {
return 0
}
return p.(*pragmas).Flag
}
func asWasmImport(p syntax.Pragma) *WasmImport {
if p == nil {
return nil
}
return p.(*pragmas).WasmImport
}
// isPtrTo reports whether from is the type *to.
func isPtrTo(from, to types2.Type) bool {
ptr, ok := from.(*types2.Pointer)
return ok && types2.Identical(ptr.Elem(), to)
}
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
// hasFallthrough reports whether stmts ends in a fallthrough
// statement.
func hasFallthrough(stmts []syntax.Stmt) bool {
last, ok := lastNonEmptyStmt(stmts).(*syntax.BranchStmt)
return ok && last.Tok == syntax.Fallthrough
}
// lastNonEmptyStmt returns the last non-empty statement in list, if
// any.
func lastNonEmptyStmt(stmts []syntax.Stmt) syntax.Stmt {
for i := len(stmts) - 1; i >= 0; i-- {
stmt := stmts[i]
if _, ok := stmt.(*syntax.EmptyStmt); !ok {
return stmt
}
}
return nil
}
// terminates reports whether stmt terminates normal control flow
// (i.e., does not merely advance to the following statement).
func (pw *pkgWriter) terminates(stmt syntax.Stmt) bool {
switch stmt := stmt.(type) {
case *syntax.BranchStmt:
if stmt.Tok == syntax.Goto {
return true
}
case *syntax.ReturnStmt:
return true
case *syntax.ExprStmt:
if call, ok := syntax.Unparen(stmt.X).(*syntax.CallExpr); ok {
if pw.isBuiltin(call.Fun, "panic") {
return true
}
}
// The handling of BlockStmt here is approximate, but it serves to
// allow dead-code elimination for:
//
// if true {
// return x
// }
// unreachable
case *syntax.IfStmt:
cond := pw.staticBool(&stmt.Cond)
return (cond < 0 || pw.terminates(stmt.Then)) && (cond > 0 || pw.terminates(stmt.Else))
case *syntax.BlockStmt:
return pw.terminates(lastNonEmptyStmt(stmt.List))
}
return false
}