diff --git a/src/cmd/compile/internal/typecheck/expr.go b/src/cmd/compile/internal/typecheck/expr.go new file mode 100644 index 0000000000..f940a2e73d --- /dev/null +++ b/src/cmd/compile/internal/typecheck/expr.go @@ -0,0 +1,1001 @@ +// Copyright 2009 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 typecheck + +import ( + "fmt" + "go/constant" + "go/token" + "strings" + + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/types" +) + +// tcAddr typechecks an OADDR node. +func tcAddr(n *ir.AddrExpr) ir.Node { + n.X = Expr(n.X) + if n.X.Type() == nil { + n.SetType(nil) + return n + } + + switch n.X.Op() { + case ir.OARRAYLIT, ir.OMAPLIT, ir.OSLICELIT, ir.OSTRUCTLIT: + n.SetOp(ir.OPTRLIT) + + default: + checklvalue(n.X, "take the address of") + r := ir.OuterValue(n.X) + if r.Op() == ir.ONAME { + r := r.(*ir.Name) + if ir.Orig(r) != r { + base.Fatalf("found non-orig name node %v", r) // TODO(mdempsky): What does this mean? + } + r.Name().SetAddrtaken(true) + if r.Name().IsClosureVar() && !CaptureVarsComplete { + // Mark the original variable as Addrtaken so that capturevars + // knows not to pass it by value. + // But if the capturevars phase is complete, don't touch it, + // in case l.Name's containing function has not yet been compiled. + r.Name().Defn.Name().SetAddrtaken(true) + } + } + n.X = DefaultLit(n.X, nil) + if n.X.Type() == nil { + n.SetType(nil) + return n + } + } + + n.SetType(types.NewPtr(n.X.Type())) + return n +} + +// tcArith typechecks a binary arithmetic expression. +func tcArith(n ir.Node) ir.Node { + var l, r ir.Node + var setLR func() + switch n := n.(type) { + case *ir.AssignOpStmt: + l, r = n.X, n.Y + setLR = func() { n.X = l; n.Y = r } + case *ir.BinaryExpr: + l, r = n.X, n.Y + setLR = func() { n.X = l; n.Y = r } + case *ir.LogicalExpr: + l, r = n.X, n.Y + setLR = func() { n.X = l; n.Y = r } + } + l = Expr(l) + r = Expr(r) + setLR() + if l.Type() == nil || r.Type() == nil { + n.SetType(nil) + return n + } + op := n.Op() + if n.Op() == ir.OASOP { + n := n.(*ir.AssignOpStmt) + checkassign(n, l) + if n.IncDec && !okforarith[l.Type().Kind()] { + base.Errorf("invalid operation: %v (non-numeric type %v)", n, l.Type()) + n.SetType(nil) + return n + } + // TODO(marvin): Fix Node.EType type union. + op = n.AsOp + } + if op == ir.OLSH || op == ir.ORSH { + r = DefaultLit(r, types.Types[types.TUINT]) + setLR() + t := r.Type() + if !t.IsInteger() { + base.Errorf("invalid operation: %v (shift count type %v, must be integer)", n, r.Type()) + n.SetType(nil) + return n + } + if t.IsSigned() && !types.AllowsGoVersion(curpkg(), 1, 13) { + base.ErrorfVers("go1.13", "invalid operation: %v (signed shift count type %v)", n, r.Type()) + n.SetType(nil) + return n + } + t = l.Type() + if t != nil && t.Kind() != types.TIDEAL && !t.IsInteger() { + base.Errorf("invalid operation: %v (shift of type %v)", n, t) + n.SetType(nil) + return n + } + + // no defaultlit for left + // the outer context gives the type + n.SetType(l.Type()) + if (l.Type() == types.UntypedFloat || l.Type() == types.UntypedComplex) && r.Op() == ir.OLITERAL { + n.SetType(types.UntypedInt) + } + return n + } + + // For "x == x && len(s)", it's better to report that "len(s)" (type int) + // can't be used with "&&" than to report that "x == x" (type untyped bool) + // can't be converted to int (see issue #41500). + if n.Op() == ir.OANDAND || n.Op() == ir.OOROR { + n := n.(*ir.LogicalExpr) + if !n.X.Type().IsBoolean() { + base.Errorf("invalid operation: %v (operator %v not defined on %s)", n, n.Op(), typekind(n.X.Type())) + n.SetType(nil) + return n + } + if !n.Y.Type().IsBoolean() { + base.Errorf("invalid operation: %v (operator %v not defined on %s)", n, n.Op(), typekind(n.Y.Type())) + n.SetType(nil) + return n + } + } + + // ideal mixed with non-ideal + l, r = defaultlit2(l, r, false) + setLR() + + if l.Type() == nil || r.Type() == nil { + n.SetType(nil) + return n + } + t := l.Type() + if t.Kind() == types.TIDEAL { + t = r.Type() + } + et := t.Kind() + if et == types.TIDEAL { + et = types.TINT + } + aop := ir.OXXX + if iscmp[n.Op()] && t.Kind() != types.TIDEAL && !types.Identical(l.Type(), r.Type()) { + // comparison is okay as long as one side is + // assignable to the other. convert so they have + // the same type. + // + // the only conversion that isn't a no-op is concrete == interface. + // in that case, check comparability of the concrete type. + // The conversion allocates, so only do it if the concrete type is huge. + converted := false + if r.Type().Kind() != types.TBLANK { + aop, _ = assignop(l.Type(), r.Type()) + if aop != ir.OXXX { + if r.Type().IsInterface() && !l.Type().IsInterface() && !types.IsComparable(l.Type()) { + base.Errorf("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(l.Type())) + n.SetType(nil) + return n + } + + types.CalcSize(l.Type()) + if r.Type().IsInterface() == l.Type().IsInterface() || l.Type().Width >= 1<<16 { + l = ir.NewConvExpr(base.Pos, aop, r.Type(), l) + l.SetTypecheck(1) + setLR() + } + + t = r.Type() + converted = true + } + } + + if !converted && l.Type().Kind() != types.TBLANK { + aop, _ = assignop(r.Type(), l.Type()) + if aop != ir.OXXX { + if l.Type().IsInterface() && !r.Type().IsInterface() && !types.IsComparable(r.Type()) { + base.Errorf("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(r.Type())) + n.SetType(nil) + return n + } + + types.CalcSize(r.Type()) + if r.Type().IsInterface() == l.Type().IsInterface() || r.Type().Width >= 1<<16 { + r = ir.NewConvExpr(base.Pos, aop, l.Type(), r) + r.SetTypecheck(1) + setLR() + } + + t = l.Type() + } + } + + et = t.Kind() + } + + if t.Kind() != types.TIDEAL && !types.Identical(l.Type(), r.Type()) { + l, r = defaultlit2(l, r, true) + if l.Type() == nil || r.Type() == nil { + n.SetType(nil) + return n + } + if l.Type().IsInterface() == r.Type().IsInterface() || aop == 0 { + base.Errorf("invalid operation: %v (mismatched types %v and %v)", n, l.Type(), r.Type()) + n.SetType(nil) + return n + } + } + + if t.Kind() == types.TIDEAL { + t = mixUntyped(l.Type(), r.Type()) + } + if dt := defaultType(t); !okfor[op][dt.Kind()] { + base.Errorf("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(t)) + n.SetType(nil) + return n + } + + // okfor allows any array == array, map == map, func == func. + // restrict to slice/map/func == nil and nil == slice/map/func. + if l.Type().IsArray() && !types.IsComparable(l.Type()) { + base.Errorf("invalid operation: %v (%v cannot be compared)", n, l.Type()) + n.SetType(nil) + return n + } + + if l.Type().IsSlice() && !ir.IsNil(l) && !ir.IsNil(r) { + base.Errorf("invalid operation: %v (slice can only be compared to nil)", n) + n.SetType(nil) + return n + } + + if l.Type().IsMap() && !ir.IsNil(l) && !ir.IsNil(r) { + base.Errorf("invalid operation: %v (map can only be compared to nil)", n) + n.SetType(nil) + return n + } + + if l.Type().Kind() == types.TFUNC && !ir.IsNil(l) && !ir.IsNil(r) { + base.Errorf("invalid operation: %v (func can only be compared to nil)", n) + n.SetType(nil) + return n + } + + if l.Type().IsStruct() { + if f := types.IncomparableField(l.Type()); f != nil { + base.Errorf("invalid operation: %v (struct containing %v cannot be compared)", n, f.Type) + n.SetType(nil) + return n + } + } + + if iscmp[n.Op()] { + t = types.UntypedBool + n.SetType(t) + if con := EvalConst(n); con.Op() == ir.OLITERAL { + return con + } + l, r = defaultlit2(l, r, true) + setLR() + return n + } + + if et == types.TSTRING && n.Op() == ir.OADD { + // create or update OADDSTR node with list of strings in x + y + z + (w + v) + ... + n := n.(*ir.BinaryExpr) + var add *ir.AddStringExpr + if l.Op() == ir.OADDSTR { + add = l.(*ir.AddStringExpr) + add.SetPos(n.Pos()) + } else { + add = ir.NewAddStringExpr(n.Pos(), []ir.Node{l}) + } + if r.Op() == ir.OADDSTR { + r := r.(*ir.AddStringExpr) + add.List.Append(r.List.Take()...) + } else { + add.List.Append(r) + } + add.SetType(t) + return add + } + + if (op == ir.ODIV || op == ir.OMOD) && ir.IsConst(r, constant.Int) { + if constant.Sign(r.Val()) == 0 { + base.Errorf("division by zero") + n.SetType(nil) + return n + } + } + + n.SetType(t) + return n +} + +// The result of tcCompLit MUST be assigned back to n, e.g. +// n.Left = tcCompLit(n.Left) +func tcCompLit(n *ir.CompLitExpr) (res ir.Node) { + if base.EnableTrace && base.Flag.LowerT { + defer tracePrint("typecheckcomplit", n)(&res) + } + + lno := base.Pos + defer func() { + base.Pos = lno + }() + + if n.Ntype == nil { + base.ErrorfAt(n.Pos(), "missing type in composite literal") + n.SetType(nil) + return n + } + + // Save original node (including n.Right) + n.SetOrig(ir.Copy(n)) + + ir.SetPos(n.Ntype) + + // Need to handle [...]T arrays specially. + if array, ok := n.Ntype.(*ir.ArrayType); ok && array.Elem != nil && array.Len == nil { + array.Elem = typecheck(array.Elem, ctxType) + elemType := array.Elem.Type() + if elemType == nil { + n.SetType(nil) + return n + } + length := typecheckarraylit(elemType, -1, n.List, "array literal") + n.SetOp(ir.OARRAYLIT) + n.SetType(types.NewArray(elemType, length)) + n.Ntype = nil + return n + } + + n.Ntype = ir.Node(typecheck(n.Ntype, ctxType)).(ir.Ntype) + t := n.Ntype.Type() + if t == nil { + n.SetType(nil) + return n + } + n.SetType(t) + + switch t.Kind() { + default: + base.Errorf("invalid composite literal type %v", t) + n.SetType(nil) + + case types.TARRAY: + typecheckarraylit(t.Elem(), t.NumElem(), n.List, "array literal") + n.SetOp(ir.OARRAYLIT) + n.Ntype = nil + + case types.TSLICE: + length := typecheckarraylit(t.Elem(), -1, n.List, "slice literal") + n.SetOp(ir.OSLICELIT) + n.Ntype = nil + n.Len = length + + case types.TMAP: + var cs constSet + for i3, l := range n.List { + ir.SetPos(l) + if l.Op() != ir.OKEY { + n.List[i3] = Expr(l) + base.Errorf("missing key in map literal") + continue + } + l := l.(*ir.KeyExpr) + + r := l.Key + r = pushtype(r, t.Key()) + r = Expr(r) + l.Key = AssignConv(r, t.Key(), "map key") + cs.add(base.Pos, l.Key, "key", "map literal") + + r = l.Value + r = pushtype(r, t.Elem()) + r = Expr(r) + l.Value = AssignConv(r, t.Elem(), "map value") + } + + n.SetOp(ir.OMAPLIT) + n.Ntype = nil + + case types.TSTRUCT: + // Need valid field offsets for Xoffset below. + types.CalcSize(t) + + errored := false + if len(n.List) != 0 && nokeys(n.List) { + // simple list of variables + ls := n.List + for i, n1 := range ls { + ir.SetPos(n1) + n1 = Expr(n1) + ls[i] = n1 + if i >= t.NumFields() { + if !errored { + base.Errorf("too many values in %v", n) + errored = true + } + continue + } + + f := t.Field(i) + s := f.Sym + if s != nil && !types.IsExported(s.Name) && s.Pkg != types.LocalPkg { + base.Errorf("implicit assignment of unexported field '%s' in %v literal", s.Name, t) + } + // No pushtype allowed here. Must name fields for that. + n1 = AssignConv(n1, f.Type, "field value") + sk := ir.NewStructKeyExpr(base.Pos, f.Sym, n1) + sk.Offset = f.Offset + ls[i] = sk + } + if len(ls) < t.NumFields() { + base.Errorf("too few values in %v", n) + } + } else { + hash := make(map[string]bool) + + // keyed list + ls := n.List + for i, l := range ls { + ir.SetPos(l) + + if l.Op() == ir.OKEY { + kv := l.(*ir.KeyExpr) + key := kv.Key + + // Sym might have resolved to name in other top-level + // package, because of import dot. Redirect to correct sym + // before we do the lookup. + s := key.Sym() + if id, ok := key.(*ir.Ident); ok && DotImportRefs[id] != nil { + s = Lookup(s.Name) + } + + // An OXDOT uses the Sym field to hold + // the field to the right of the dot, + // so s will be non-nil, but an OXDOT + // is never a valid struct literal key. + if s == nil || s.Pkg != types.LocalPkg || key.Op() == ir.OXDOT || s.IsBlank() { + base.Errorf("invalid field name %v in struct initializer", key) + continue + } + + l = ir.NewStructKeyExpr(l.Pos(), s, kv.Value) + ls[i] = l + } + + if l.Op() != ir.OSTRUCTKEY { + if !errored { + base.Errorf("mixture of field:value and value initializers") + errored = true + } + ls[i] = Expr(ls[i]) + continue + } + l := l.(*ir.StructKeyExpr) + + f := lookdot1(nil, l.Field, t, t.Fields(), 0) + if f == nil { + if ci := lookdot1(nil, l.Field, t, t.Fields(), 2); ci != nil { // Case-insensitive lookup. + if visible(ci.Sym) { + base.Errorf("unknown field '%v' in struct literal of type %v (but does have %v)", l.Field, t, ci.Sym) + } else if nonexported(l.Field) && l.Field.Name == ci.Sym.Name { // Ensure exactness before the suggestion. + base.Errorf("cannot refer to unexported field '%v' in struct literal of type %v", l.Field, t) + } else { + base.Errorf("unknown field '%v' in struct literal of type %v", l.Field, t) + } + continue + } + var f *types.Field + p, _ := dotpath(l.Field, t, &f, true) + if p == nil || f.IsMethod() { + base.Errorf("unknown field '%v' in struct literal of type %v", l.Field, t) + continue + } + // dotpath returns the parent embedded types in reverse order. + var ep []string + for ei := len(p) - 1; ei >= 0; ei-- { + ep = append(ep, p[ei].field.Sym.Name) + } + ep = append(ep, l.Field.Name) + base.Errorf("cannot use promoted field %v in struct literal of type %v", strings.Join(ep, "."), t) + continue + } + fielddup(f.Sym.Name, hash) + l.Offset = f.Offset + + // No pushtype allowed here. Tried and rejected. + l.Value = Expr(l.Value) + l.Value = AssignConv(l.Value, f.Type, "field value") + } + } + + n.SetOp(ir.OSTRUCTLIT) + n.Ntype = nil + } + + return n +} + +// tcConv typechecks an OCONV node. +func tcConv(n *ir.ConvExpr) ir.Node { + types.CheckSize(n.Type()) // ensure width is calculated for backend + n.X = Expr(n.X) + n.X = convlit1(n.X, n.Type(), true, nil) + t := n.X.Type() + if t == nil || n.Type() == nil { + n.SetType(nil) + return n + } + op, why := convertop(n.X.Op() == ir.OLITERAL, t, n.Type()) + if op == ir.OXXX { + if !n.Diag() && !n.Type().Broke() && !n.X.Diag() { + base.Errorf("cannot convert %L to type %v%s", n.X, n.Type(), why) + n.SetDiag(true) + } + n.SetOp(ir.OCONV) + n.SetType(nil) + return n + } + + n.SetOp(op) + switch n.Op() { + case ir.OCONVNOP: + if t.Kind() == n.Type().Kind() { + switch t.Kind() { + case types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128: + // Floating point casts imply rounding and + // so the conversion must be kept. + n.SetOp(ir.OCONV) + } + } + + // do not convert to []byte literal. See CL 125796. + // generated code and compiler memory footprint is better without it. + case ir.OSTR2BYTES: + // ok + + case ir.OSTR2RUNES: + if n.X.Op() == ir.OLITERAL { + return stringtoruneslit(n) + } + } + return n +} + +// tcDot typechecks an OXDOT or ODOT node. +func tcDot(n *ir.SelectorExpr, top int) ir.Node { + if n.Op() == ir.OXDOT { + n = AddImplicitDots(n) + n.SetOp(ir.ODOT) + if n.X == nil { + n.SetType(nil) + return n + } + } + + n.X = typecheck(n.X, ctxExpr|ctxType) + + n.X = DefaultLit(n.X, nil) + + t := n.X.Type() + if t == nil { + base.UpdateErrorDot(ir.Line(n), fmt.Sprint(n.X), fmt.Sprint(n)) + n.SetType(nil) + return n + } + + s := n.Sel + + if n.X.Op() == ir.OTYPE { + return typecheckMethodExpr(n) + } + + if t.IsPtr() && !t.Elem().IsInterface() { + t = t.Elem() + if t == nil { + n.SetType(nil) + return n + } + n.SetOp(ir.ODOTPTR) + types.CheckSize(t) + } + + if n.Sel.IsBlank() { + base.Errorf("cannot refer to blank field or method") + n.SetType(nil) + return n + } + + if lookdot(n, t, 0) == nil { + // Legitimate field or method lookup failed, try to explain the error + switch { + case t.IsEmptyInterface(): + base.Errorf("%v undefined (type %v is interface with no methods)", n, n.X.Type()) + + case t.IsPtr() && t.Elem().IsInterface(): + // Pointer to interface is almost always a mistake. + base.Errorf("%v undefined (type %v is pointer to interface, not interface)", n, n.X.Type()) + + case lookdot(n, t, 1) != nil: + // Field or method matches by name, but it is not exported. + base.Errorf("%v undefined (cannot refer to unexported field or method %v)", n, n.Sel) + + default: + if mt := lookdot(n, t, 2); mt != nil && visible(mt.Sym) { // Case-insensitive lookup. + base.Errorf("%v undefined (type %v has no field or method %v, but does have %v)", n, n.X.Type(), n.Sel, mt.Sym) + } else { + base.Errorf("%v undefined (type %v has no field or method %v)", n, n.X.Type(), n.Sel) + } + } + n.SetType(nil) + return n + } + + if (n.Op() == ir.ODOTINTER || n.Op() == ir.ODOTMETH) && top&ctxCallee == 0 { + return tcCallPart(n, s) + } + return n +} + +// tcDotType typechecks an ODOTTYPE node. +func tcDotType(n *ir.TypeAssertExpr) ir.Node { + n.X = Expr(n.X) + n.X = DefaultLit(n.X, nil) + l := n.X + t := l.Type() + if t == nil { + n.SetType(nil) + return n + } + if !t.IsInterface() { + base.Errorf("invalid type assertion: %v (non-interface type %v on left)", n, t) + n.SetType(nil) + return n + } + + if n.Ntype != nil { + n.Ntype = typecheck(n.Ntype, ctxType) + n.SetType(n.Ntype.Type()) + n.Ntype = nil + if n.Type() == nil { + return n + } + } + + if n.Type() != nil && !n.Type().IsInterface() { + var missing, have *types.Field + var ptr int + if !implements(n.Type(), t, &missing, &have, &ptr) { + if have != nil && have.Sym == missing.Sym { + base.Errorf("impossible type assertion:\n\t%v does not implement %v (wrong type for %v method)\n"+ + "\t\thave %v%S\n\t\twant %v%S", n.Type(), t, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type) + } else if ptr != 0 { + base.Errorf("impossible type assertion:\n\t%v does not implement %v (%v method has pointer receiver)", n.Type(), t, missing.Sym) + } else if have != nil { + base.Errorf("impossible type assertion:\n\t%v does not implement %v (missing %v method)\n"+ + "\t\thave %v%S\n\t\twant %v%S", n.Type(), t, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type) + } else { + base.Errorf("impossible type assertion:\n\t%v does not implement %v (missing %v method)", n.Type(), t, missing.Sym) + } + n.SetType(nil) + return n + } + } + return n +} + +// tcITab typechecks an OITAB node. +func tcITab(n *ir.UnaryExpr) ir.Node { + n.X = Expr(n.X) + t := n.X.Type() + if t == nil { + n.SetType(nil) + return n + } + if !t.IsInterface() { + base.Fatalf("OITAB of %v", t) + } + n.SetType(types.NewPtr(types.Types[types.TUINTPTR])) + return n +} + +// tcIndex typechecks an OINDEX node. +func tcIndex(n *ir.IndexExpr) ir.Node { + n.X = Expr(n.X) + n.X = DefaultLit(n.X, nil) + n.X = implicitstar(n.X) + l := n.X + n.Index = Expr(n.Index) + r := n.Index + t := l.Type() + if t == nil || r.Type() == nil { + n.SetType(nil) + return n + } + switch t.Kind() { + default: + base.Errorf("invalid operation: %v (type %v does not support indexing)", n, t) + n.SetType(nil) + return n + + case types.TSTRING, types.TARRAY, types.TSLICE: + n.Index = indexlit(n.Index) + if t.IsString() { + n.SetType(types.ByteType) + } else { + n.SetType(t.Elem()) + } + why := "string" + if t.IsArray() { + why = "array" + } else if t.IsSlice() { + why = "slice" + } + + if n.Index.Type() != nil && !n.Index.Type().IsInteger() { + base.Errorf("non-integer %s index %v", why, n.Index) + return n + } + + if !n.Bounded() && ir.IsConst(n.Index, constant.Int) { + x := n.Index.Val() + if constant.Sign(x) < 0 { + base.Errorf("invalid %s index %v (index must be non-negative)", why, n.Index) + } else if t.IsArray() && constant.Compare(x, token.GEQ, constant.MakeInt64(t.NumElem())) { + base.Errorf("invalid array index %v (out of bounds for %d-element array)", n.Index, t.NumElem()) + } else if ir.IsConst(n.X, constant.String) && constant.Compare(x, token.GEQ, constant.MakeInt64(int64(len(ir.StringVal(n.X))))) { + base.Errorf("invalid string index %v (out of bounds for %d-byte string)", n.Index, len(ir.StringVal(n.X))) + } else if ir.ConstOverflow(x, types.Types[types.TINT]) { + base.Errorf("invalid %s index %v (index too large)", why, n.Index) + } + } + + case types.TMAP: + n.Index = AssignConv(n.Index, t.Key(), "map index") + n.SetType(t.Elem()) + n.SetOp(ir.OINDEXMAP) + n.Assigned = false + } + return n +} + +// tcLenCap typechecks an OLEN or OCAP node. +func tcLenCap(n *ir.UnaryExpr) ir.Node { + n.X = Expr(n.X) + n.X = DefaultLit(n.X, nil) + n.X = implicitstar(n.X) + l := n.X + t := l.Type() + if t == nil { + n.SetType(nil) + return n + } + + var ok bool + if n.Op() == ir.OLEN { + ok = okforlen[t.Kind()] + } else { + ok = okforcap[t.Kind()] + } + if !ok { + base.Errorf("invalid argument %L for %v", l, n.Op()) + n.SetType(nil) + return n + } + + n.SetType(types.Types[types.TINT]) + return n +} + +// tcRecv typechecks an ORECV node. +func tcRecv(n *ir.UnaryExpr) ir.Node { + n.X = Expr(n.X) + n.X = DefaultLit(n.X, nil) + l := n.X + t := l.Type() + if t == nil { + n.SetType(nil) + return n + } + if !t.IsChan() { + base.Errorf("invalid operation: %v (receive from non-chan type %v)", n, t) + n.SetType(nil) + return n + } + + if !t.ChanDir().CanRecv() { + base.Errorf("invalid operation: %v (receive from send-only type %v)", n, t) + n.SetType(nil) + return n + } + + n.SetType(t.Elem()) + return n +} + +// tcSPtr typechecks an OSPTR node. +func tcSPtr(n *ir.UnaryExpr) ir.Node { + n.X = Expr(n.X) + t := n.X.Type() + if t == nil { + n.SetType(nil) + return n + } + if !t.IsSlice() && !t.IsString() { + base.Fatalf("OSPTR of %v", t) + } + if t.IsString() { + n.SetType(types.NewPtr(types.Types[types.TUINT8])) + } else { + n.SetType(types.NewPtr(t.Elem())) + } + return n +} + +// tcSlice typechecks an OSLICE or OSLICE3 node. +func tcSlice(n *ir.SliceExpr) ir.Node { + n.X = Expr(n.X) + low, high, max := n.SliceBounds() + hasmax := n.Op().IsSlice3() + low = Expr(low) + high = Expr(high) + max = Expr(max) + n.X = DefaultLit(n.X, nil) + low = indexlit(low) + high = indexlit(high) + max = indexlit(max) + n.SetSliceBounds(low, high, max) + l := n.X + if l.Type() == nil { + n.SetType(nil) + return n + } + if l.Type().IsArray() { + if !ir.IsAssignable(n.X) { + base.Errorf("invalid operation %v (slice of unaddressable value)", n) + n.SetType(nil) + return n + } + + addr := NodAddr(n.X) + addr.SetImplicit(true) + n.X = Expr(addr) + l = n.X + } + t := l.Type() + var tp *types.Type + if t.IsString() { + if hasmax { + base.Errorf("invalid operation %v (3-index slice of string)", n) + n.SetType(nil) + return n + } + n.SetType(t) + n.SetOp(ir.OSLICESTR) + } else if t.IsPtr() && t.Elem().IsArray() { + tp = t.Elem() + n.SetType(types.NewSlice(tp.Elem())) + types.CalcSize(n.Type()) + if hasmax { + n.SetOp(ir.OSLICE3ARR) + } else { + n.SetOp(ir.OSLICEARR) + } + } else if t.IsSlice() { + n.SetType(t) + } else { + base.Errorf("cannot slice %v (type %v)", l, t) + n.SetType(nil) + return n + } + + if low != nil && !checksliceindex(l, low, tp) { + n.SetType(nil) + return n + } + if high != nil && !checksliceindex(l, high, tp) { + n.SetType(nil) + return n + } + if max != nil && !checksliceindex(l, max, tp) { + n.SetType(nil) + return n + } + if !checksliceconst(low, high) || !checksliceconst(low, max) || !checksliceconst(high, max) { + n.SetType(nil) + return n + } + return n +} + +// tcSliceHeader typechecks an OSLICEHEADER node. +func tcSliceHeader(n *ir.SliceHeaderExpr) ir.Node { + // Errors here are Fatalf instead of Errorf because only the compiler + // can construct an OSLICEHEADER node. + // Components used in OSLICEHEADER that are supplied by parsed source code + // have already been typechecked in e.g. OMAKESLICE earlier. + t := n.Type() + if t == nil { + base.Fatalf("no type specified for OSLICEHEADER") + } + + if !t.IsSlice() { + base.Fatalf("invalid type %v for OSLICEHEADER", n.Type()) + } + + if n.Ptr == nil || n.Ptr.Type() == nil || !n.Ptr.Type().IsUnsafePtr() { + base.Fatalf("need unsafe.Pointer for OSLICEHEADER") + } + + if x := len(n.LenCap); x != 2 { + base.Fatalf("expected 2 params (len, cap) for OSLICEHEADER, got %d", x) + } + + n.Ptr = Expr(n.Ptr) + l := Expr(n.LenCap[0]) + c := Expr(n.LenCap[1]) + l = DefaultLit(l, types.Types[types.TINT]) + c = DefaultLit(c, types.Types[types.TINT]) + + if ir.IsConst(l, constant.Int) && ir.Int64Val(l) < 0 { + base.Fatalf("len for OSLICEHEADER must be non-negative") + } + + if ir.IsConst(c, constant.Int) && ir.Int64Val(c) < 0 { + base.Fatalf("cap for OSLICEHEADER must be non-negative") + } + + if ir.IsConst(l, constant.Int) && ir.IsConst(c, constant.Int) && constant.Compare(l.Val(), token.GTR, c.Val()) { + base.Fatalf("len larger than cap for OSLICEHEADER") + } + + n.LenCap[0] = l + n.LenCap[1] = c + return n +} + +// tcStar typechecks an ODEREF node, which may be an expression or a type. +func tcStar(n *ir.StarExpr, top int) ir.Node { + n.X = typecheck(n.X, ctxExpr|ctxType) + l := n.X + t := l.Type() + if t == nil { + n.SetType(nil) + return n + } + if l.Op() == ir.OTYPE { + n.SetOTYPE(types.NewPtr(l.Type())) + // Ensure l.Type gets dowidth'd for the backend. Issue 20174. + types.CheckSize(l.Type()) + return n + } + + if !t.IsPtr() { + if top&(ctxExpr|ctxStmt) != 0 { + base.Errorf("invalid indirect of %L", n.X) + n.SetType(nil) + return n + } + base.Errorf("%v is not a type", l) + return n + } + + n.SetType(t.Elem()) + return n +} + +// tcUnaryArith typechecks a unary arithmetic expression. +func tcUnaryArith(n *ir.UnaryExpr) ir.Node { + n.X = Expr(n.X) + l := n.X + t := l.Type() + if t == nil { + n.SetType(nil) + return n + } + if !okfor[n.Op()][defaultType(t).Kind()] { + base.Errorf("invalid operation: %v (operator %v not defined on %s)", n, n.Op(), typekind(t)) + n.SetType(nil) + return n + } + + n.SetType(t) + return n +} diff --git a/src/cmd/compile/internal/typecheck/func.go b/src/cmd/compile/internal/typecheck/func.go index 4675de6cad..99d81dcede 100644 --- a/src/cmd/compile/internal/typecheck/func.go +++ b/src/cmd/compile/internal/typecheck/func.go @@ -10,6 +10,8 @@ import ( "cmd/compile/internal/types" "fmt" + "go/constant" + "go/token" ) // package all the arguments that match a ... T parameter into a []T. @@ -156,66 +158,6 @@ func CaptureVars(fn *ir.Func) { base.Pos = lno } -// typecheckclosure typechecks an OCLOSURE node. It also creates the named -// function associated with the closure. -// TODO: This creation of the named function should probably really be done in a -// separate pass from type-checking. -func typecheckclosure(clo *ir.ClosureExpr, top int) { - fn := clo.Func - // Set current associated iota value, so iota can be used inside - // function in ConstSpec, see issue #22344 - if x := getIotaValue(); x >= 0 { - fn.Iota = x - } - - fn.ClosureType = check(fn.ClosureType, ctxType) - clo.SetType(fn.ClosureType.Type()) - fn.SetClosureCalled(top&ctxCallee != 0) - - // Do not typecheck fn twice, otherwise, we will end up pushing - // fn to Target.Decls multiple times, causing initLSym called twice. - // See #30709 - if fn.Typecheck() == 1 { - return - } - - for _, ln := range fn.ClosureVars { - n := ln.Defn - if !n.Name().Captured() { - n.Name().SetCaptured(true) - if n.Name().Decldepth == 0 { - base.Fatalf("typecheckclosure: var %v does not have decldepth assigned", n) - } - - // Ignore assignments to the variable in straightline code - // preceding the first capturing by a closure. - if n.Name().Decldepth == decldepth { - n.Name().SetAssigned(false) - } - } - } - - fn.Nname.SetSym(closurename(ir.CurFunc)) - ir.MarkFunc(fn.Nname) - Func(fn) - - // Type check the body now, but only if we're inside a function. - // At top level (in a variable initialization: curfn==nil) we're not - // ready to type check code yet; we'll check it later, because the - // underlying closure function we create is added to Target.Decls. - if ir.CurFunc != nil && clo.Type() != nil { - oldfn := ir.CurFunc - ir.CurFunc = fn - olddd := decldepth - decldepth = 1 - Stmts(fn.Body) - decldepth = olddd - ir.CurFunc = oldfn - } - - Target.Decls = append(Target.Decls, fn) -} - // Lazy typechecking of imported bodies. For local functions, caninl will set ->typecheck // because they're a copy of an already checked body. func ImportedBody(fn *ir.Func) { @@ -380,7 +322,67 @@ func makepartialcall(dot *ir.SelectorExpr, t0 *types.Type, meth *types.Sym) *ir. return fn } -func typecheckpartialcall(n ir.Node, sym *types.Sym) *ir.CallPartExpr { +// tcClosure typechecks an OCLOSURE node. It also creates the named +// function associated with the closure. +// TODO: This creation of the named function should probably really be done in a +// separate pass from type-checking. +func tcClosure(clo *ir.ClosureExpr, top int) { + fn := clo.Func + // Set current associated iota value, so iota can be used inside + // function in ConstSpec, see issue #22344 + if x := getIotaValue(); x >= 0 { + fn.Iota = x + } + + fn.ClosureType = typecheck(fn.ClosureType, ctxType) + clo.SetType(fn.ClosureType.Type()) + fn.SetClosureCalled(top&ctxCallee != 0) + + // Do not typecheck fn twice, otherwise, we will end up pushing + // fn to Target.Decls multiple times, causing initLSym called twice. + // See #30709 + if fn.Typecheck() == 1 { + return + } + + for _, ln := range fn.ClosureVars { + n := ln.Defn + if !n.Name().Captured() { + n.Name().SetCaptured(true) + if n.Name().Decldepth == 0 { + base.Fatalf("typecheckclosure: var %v does not have decldepth assigned", n) + } + + // Ignore assignments to the variable in straightline code + // preceding the first capturing by a closure. + if n.Name().Decldepth == decldepth { + n.Name().SetAssigned(false) + } + } + } + + fn.Nname.SetSym(closurename(ir.CurFunc)) + ir.MarkFunc(fn.Nname) + Func(fn) + + // Type check the body now, but only if we're inside a function. + // At top level (in a variable initialization: curfn==nil) we're not + // ready to type check code yet; we'll check it later, because the + // underlying closure function we create is added to Target.Decls. + if ir.CurFunc != nil && clo.Type() != nil { + oldfn := ir.CurFunc + ir.CurFunc = fn + olddd := decldepth + decldepth = 1 + Stmts(fn.Body) + decldepth = olddd + ir.CurFunc = oldfn + } + + Target.Decls = append(Target.Decls, fn) +} + +func tcCallPart(n ir.Node, sym *types.Sym) *ir.CallPartExpr { switch n.Op() { case ir.ODOTINTER, ir.ODOTMETH: break @@ -396,3 +398,632 @@ func typecheckpartialcall(n ir.Node, sym *types.Sym) *ir.CallPartExpr { return ir.NewCallPartExpr(dot.Pos(), dot.X, dot.Selection, fn) } + +// type check function definition +// To be called by typecheck, not directly. +// (Call typecheckFunc instead.) +func tcFunc(n *ir.Func) { + if base.EnableTrace && base.Flag.LowerT { + defer tracePrint("typecheckfunc", n)(nil) + } + + for _, ln := range n.Dcl { + if ln.Op() == ir.ONAME && (ln.Class_ == ir.PPARAM || ln.Class_ == ir.PPARAMOUT) { + ln.Decldepth = 1 + } + } + + n.Nname = AssignExpr(n.Nname).(*ir.Name) + t := n.Nname.Type() + if t == nil { + return + } + n.SetType(t) + rcvr := t.Recv() + if rcvr != nil && n.Shortname != nil { + m := addmethod(n, n.Shortname, t, true, n.Pragma&ir.Nointerface != 0) + if m == nil { + return + } + + n.Nname.SetSym(ir.MethodSym(rcvr.Type, n.Shortname)) + Declare(n.Nname, ir.PFUNC) + } + + if base.Ctxt.Flag_dynlink && !inimport && n.Nname != nil { + NeedFuncSym(n.Sym()) + } +} + +// tcCall typechecks an OCALL node. +func tcCall(n *ir.CallExpr, top int) ir.Node { + n.Use = ir.CallUseExpr + if top == ctxStmt { + n.Use = ir.CallUseStmt + } + Stmts(n.Init()) // imported rewritten f(g()) calls (#30907) + n.X = typecheck(n.X, ctxExpr|ctxType|ctxCallee) + if n.X.Diag() { + n.SetDiag(true) + } + + l := n.X + + if l.Op() == ir.ONAME && l.(*ir.Name).BuiltinOp != 0 { + l := l.(*ir.Name) + if n.IsDDD && l.BuiltinOp != ir.OAPPEND { + base.Errorf("invalid use of ... with builtin %v", l) + } + + // builtin: OLEN, OCAP, etc. + switch l.BuiltinOp { + default: + base.Fatalf("unknown builtin %v", l) + + case ir.OAPPEND, ir.ODELETE, ir.OMAKE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER: + n.SetOp(l.BuiltinOp) + n.X = nil + n.SetTypecheck(0) // re-typechecking new op is OK, not a loop + return typecheck(n, top) + + case ir.OCAP, ir.OCLOSE, ir.OIMAG, ir.OLEN, ir.OPANIC, ir.OREAL: + typecheckargs(n) + fallthrough + case ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: + arg, ok := needOneArg(n, "%v", n.Op()) + if !ok { + n.SetType(nil) + return n + } + u := ir.NewUnaryExpr(n.Pos(), l.BuiltinOp, arg) + return typecheck(ir.InitExpr(n.Init(), u), top) // typecheckargs can add to old.Init + + case ir.OCOMPLEX, ir.OCOPY: + typecheckargs(n) + arg1, arg2, ok := needTwoArgs(n) + if !ok { + n.SetType(nil) + return n + } + b := ir.NewBinaryExpr(n.Pos(), l.BuiltinOp, arg1, arg2) + return typecheck(ir.InitExpr(n.Init(), b), top) // typecheckargs can add to old.Init + } + panic("unreachable") + } + + n.X = DefaultLit(n.X, nil) + l = n.X + if l.Op() == ir.OTYPE { + if n.IsDDD { + if !l.Type().Broke() { + base.Errorf("invalid use of ... in type conversion to %v", l.Type()) + } + n.SetDiag(true) + } + + // pick off before type-checking arguments + arg, ok := needOneArg(n, "conversion to %v", l.Type()) + if !ok { + n.SetType(nil) + return n + } + + n := ir.NewConvExpr(n.Pos(), ir.OCONV, nil, arg) + n.SetType(l.Type()) + return typecheck1(n, top) + } + + typecheckargs(n) + t := l.Type() + if t == nil { + n.SetType(nil) + return n + } + types.CheckSize(t) + + switch l.Op() { + case ir.ODOTINTER: + n.SetOp(ir.OCALLINTER) + + case ir.ODOTMETH: + l := l.(*ir.SelectorExpr) + n.SetOp(ir.OCALLMETH) + + // typecheckaste was used here but there wasn't enough + // information further down the call chain to know if we + // were testing a method receiver for unexported fields. + // It isn't necessary, so just do a sanity check. + tp := t.Recv().Type + + if l.X == nil || !types.Identical(l.X.Type(), tp) { + base.Fatalf("method receiver") + } + + default: + n.SetOp(ir.OCALLFUNC) + if t.Kind() != types.TFUNC { + // TODO(mdempsky): Remove "o.Sym() != nil" once we stop + // using ir.Name for numeric literals. + if o := ir.Orig(l); o.Name() != nil && o.Sym() != nil && types.BuiltinPkg.Lookup(o.Sym().Name).Def != nil { + // be more specific when the non-function + // name matches a predeclared function + base.Errorf("cannot call non-function %L, declared at %s", + l, base.FmtPos(o.Name().Pos())) + } else { + base.Errorf("cannot call non-function %L", l) + } + n.SetType(nil) + return n + } + } + + typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args, func() string { return fmt.Sprintf("argument to %v", n.X) }) + if t.NumResults() == 0 { + return n + } + if t.NumResults() == 1 { + n.SetType(l.Type().Results().Field(0).Type) + + if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.ONAME { + if sym := n.X.(*ir.Name).Sym(); types.IsRuntimePkg(sym.Pkg) && sym.Name == "getg" { + // Emit code for runtime.getg() directly instead of calling function. + // Most such rewrites (for example the similar one for math.Sqrt) should be done in walk, + // so that the ordering pass can make sure to preserve the semantics of the original code + // (in particular, the exact time of the function call) by introducing temporaries. + // In this case, we know getg() always returns the same result within a given function + // and we want to avoid the temporaries, so we do the rewrite earlier than is typical. + n.SetOp(ir.OGETG) + } + } + return n + } + + // multiple return + if top&(ctxMultiOK|ctxStmt) == 0 { + base.Errorf("multiple-value %v() in single-value context", l) + return n + } + + n.SetType(l.Type().Results()) + return n +} + +// tcAppend typechecks an OAPPEND node. +func tcAppend(n *ir.CallExpr) ir.Node { + typecheckargs(n) + args := n.Args + if len(args) == 0 { + base.Errorf("missing arguments to append") + n.SetType(nil) + return n + } + + t := args[0].Type() + if t == nil { + n.SetType(nil) + return n + } + + n.SetType(t) + if !t.IsSlice() { + if ir.IsNil(args[0]) { + base.Errorf("first argument to append must be typed slice; have untyped nil") + n.SetType(nil) + return n + } + + base.Errorf("first argument to append must be slice; have %L", t) + n.SetType(nil) + return n + } + + if n.IsDDD { + if len(args) == 1 { + base.Errorf("cannot use ... on first argument to append") + n.SetType(nil) + return n + } + + if len(args) != 2 { + base.Errorf("too many arguments to append") + n.SetType(nil) + return n + } + + if t.Elem().IsKind(types.TUINT8) && args[1].Type().IsString() { + args[1] = DefaultLit(args[1], types.Types[types.TSTRING]) + return n + } + + args[1] = AssignConv(args[1], t.Underlying(), "append") + return n + } + + as := args[1:] + for i, n := range as { + if n.Type() == nil { + continue + } + as[i] = AssignConv(n, t.Elem(), "append") + types.CheckSize(as[i].Type()) // ensure width is calculated for backend + } + return n +} + +// tcClose typechecks an OCLOSE node. +func tcClose(n *ir.UnaryExpr) ir.Node { + n.X = Expr(n.X) + n.X = DefaultLit(n.X, nil) + l := n.X + t := l.Type() + if t == nil { + n.SetType(nil) + return n + } + if !t.IsChan() { + base.Errorf("invalid operation: %v (non-chan type %v)", n, t) + n.SetType(nil) + return n + } + + if !t.ChanDir().CanSend() { + base.Errorf("invalid operation: %v (cannot close receive-only channel)", n) + n.SetType(nil) + return n + } + return n +} + +// tcComplex typechecks an OCOMPLEX node. +func tcComplex(n *ir.BinaryExpr) ir.Node { + l := Expr(n.X) + r := Expr(n.Y) + if l.Type() == nil || r.Type() == nil { + n.SetType(nil) + return n + } + l, r = defaultlit2(l, r, false) + if l.Type() == nil || r.Type() == nil { + n.SetType(nil) + return n + } + n.X = l + n.Y = r + + if !types.Identical(l.Type(), r.Type()) { + base.Errorf("invalid operation: %v (mismatched types %v and %v)", n, l.Type(), r.Type()) + n.SetType(nil) + return n + } + + var t *types.Type + switch l.Type().Kind() { + default: + base.Errorf("invalid operation: %v (arguments have type %v, expected floating-point)", n, l.Type()) + n.SetType(nil) + return n + + case types.TIDEAL: + t = types.UntypedComplex + + case types.TFLOAT32: + t = types.Types[types.TCOMPLEX64] + + case types.TFLOAT64: + t = types.Types[types.TCOMPLEX128] + } + n.SetType(t) + return n +} + +// tcCopy typechecks an OCOPY node. +func tcCopy(n *ir.BinaryExpr) ir.Node { + n.SetType(types.Types[types.TINT]) + n.X = Expr(n.X) + n.X = DefaultLit(n.X, nil) + n.Y = Expr(n.Y) + n.Y = DefaultLit(n.Y, nil) + if n.X.Type() == nil || n.Y.Type() == nil { + n.SetType(nil) + return n + } + + // copy([]byte, string) + if n.X.Type().IsSlice() && n.Y.Type().IsString() { + if types.Identical(n.X.Type().Elem(), types.ByteType) { + return n + } + base.Errorf("arguments to copy have different element types: %L and string", n.X.Type()) + n.SetType(nil) + return n + } + + if !n.X.Type().IsSlice() || !n.Y.Type().IsSlice() { + if !n.X.Type().IsSlice() && !n.Y.Type().IsSlice() { + base.Errorf("arguments to copy must be slices; have %L, %L", n.X.Type(), n.Y.Type()) + } else if !n.X.Type().IsSlice() { + base.Errorf("first argument to copy should be slice; have %L", n.X.Type()) + } else { + base.Errorf("second argument to copy should be slice or string; have %L", n.Y.Type()) + } + n.SetType(nil) + return n + } + + if !types.Identical(n.X.Type().Elem(), n.Y.Type().Elem()) { + base.Errorf("arguments to copy have different element types: %L and %L", n.X.Type(), n.Y.Type()) + n.SetType(nil) + return n + } + return n +} + +// tcDelete typechecks an ODELETE node. +func tcDelete(n *ir.CallExpr) ir.Node { + typecheckargs(n) + args := n.Args + if len(args) == 0 { + base.Errorf("missing arguments to delete") + n.SetType(nil) + return n + } + + if len(args) == 1 { + base.Errorf("missing second (key) argument to delete") + n.SetType(nil) + return n + } + + if len(args) != 2 { + base.Errorf("too many arguments to delete") + n.SetType(nil) + return n + } + + l := args[0] + r := args[1] + if l.Type() != nil && !l.Type().IsMap() { + base.Errorf("first argument to delete must be map; have %L", l.Type()) + n.SetType(nil) + return n + } + + args[1] = AssignConv(r, l.Type().Key(), "delete") + return n +} + +// tcMake typechecks an OMAKE node. +func tcMake(n *ir.CallExpr) ir.Node { + args := n.Args + if len(args) == 0 { + base.Errorf("missing argument to make") + n.SetType(nil) + return n + } + + n.Args.Set(nil) + l := args[0] + l = typecheck(l, ctxType) + t := l.Type() + if t == nil { + n.SetType(nil) + return n + } + + i := 1 + var nn ir.Node + switch t.Kind() { + default: + base.Errorf("cannot make type %v", t) + n.SetType(nil) + return n + + case types.TSLICE: + if i >= len(args) { + base.Errorf("missing len argument to make(%v)", t) + n.SetType(nil) + return n + } + + l = args[i] + i++ + l = Expr(l) + var r ir.Node + if i < len(args) { + r = args[i] + i++ + r = Expr(r) + } + + if l.Type() == nil || (r != nil && r.Type() == nil) { + n.SetType(nil) + return n + } + if !checkmake(t, "len", &l) || r != nil && !checkmake(t, "cap", &r) { + n.SetType(nil) + return n + } + if ir.IsConst(l, constant.Int) && r != nil && ir.IsConst(r, constant.Int) && constant.Compare(l.Val(), token.GTR, r.Val()) { + base.Errorf("len larger than cap in make(%v)", t) + n.SetType(nil) + return n + } + nn = ir.NewMakeExpr(n.Pos(), ir.OMAKESLICE, l, r) + + case types.TMAP: + if i < len(args) { + l = args[i] + i++ + l = Expr(l) + l = DefaultLit(l, types.Types[types.TINT]) + if l.Type() == nil { + n.SetType(nil) + return n + } + if !checkmake(t, "size", &l) { + n.SetType(nil) + return n + } + } else { + l = ir.NewInt(0) + } + nn = ir.NewMakeExpr(n.Pos(), ir.OMAKEMAP, l, nil) + nn.SetEsc(n.Esc()) + + case types.TCHAN: + l = nil + if i < len(args) { + l = args[i] + i++ + l = Expr(l) + l = DefaultLit(l, types.Types[types.TINT]) + if l.Type() == nil { + n.SetType(nil) + return n + } + if !checkmake(t, "buffer", &l) { + n.SetType(nil) + return n + } + } else { + l = ir.NewInt(0) + } + nn = ir.NewMakeExpr(n.Pos(), ir.OMAKECHAN, l, nil) + } + + if i < len(args) { + base.Errorf("too many arguments to make(%v)", t) + n.SetType(nil) + return n + } + + nn.SetType(t) + return nn +} + +// tcMakeSliceCopy typechecks an OMAKESLICECOPY node. +func tcMakeSliceCopy(n *ir.MakeExpr) ir.Node { + // Errors here are Fatalf instead of Errorf because only the compiler + // can construct an OMAKESLICECOPY node. + // Components used in OMAKESCLICECOPY that are supplied by parsed source code + // have already been typechecked in OMAKE and OCOPY earlier. + t := n.Type() + + if t == nil { + base.Fatalf("no type specified for OMAKESLICECOPY") + } + + if !t.IsSlice() { + base.Fatalf("invalid type %v for OMAKESLICECOPY", n.Type()) + } + + if n.Len == nil { + base.Fatalf("missing len argument for OMAKESLICECOPY") + } + + if n.Cap == nil { + base.Fatalf("missing slice argument to copy for OMAKESLICECOPY") + } + + n.Len = Expr(n.Len) + n.Cap = Expr(n.Cap) + + n.Len = DefaultLit(n.Len, types.Types[types.TINT]) + + if !n.Len.Type().IsInteger() && n.Type().Kind() != types.TIDEAL { + base.Errorf("non-integer len argument in OMAKESLICECOPY") + } + + if ir.IsConst(n.Len, constant.Int) { + if ir.ConstOverflow(n.Len.Val(), types.Types[types.TINT]) { + base.Fatalf("len for OMAKESLICECOPY too large") + } + if constant.Sign(n.Len.Val()) < 0 { + base.Fatalf("len for OMAKESLICECOPY must be non-negative") + } + } + return n +} + +// tcNew typechecks an ONEW node. +func tcNew(n *ir.UnaryExpr) ir.Node { + if n.X == nil { + // Fatalf because the OCALL above checked for us, + // so this must be an internally-generated mistake. + base.Fatalf("missing argument to new") + } + l := n.X + l = typecheck(l, ctxType) + t := l.Type() + if t == nil { + n.SetType(nil) + return n + } + n.X = l + n.SetType(types.NewPtr(t)) + return n +} + +// tcPanic typechecks an OPANIC node. +func tcPanic(n *ir.UnaryExpr) ir.Node { + n.X = Expr(n.X) + n.X = DefaultLit(n.X, types.Types[types.TINTER]) + if n.X.Type() == nil { + n.SetType(nil) + return n + } + return n +} + +// tcPrint typechecks an OPRINT or OPRINTN node. +func tcPrint(n *ir.CallExpr) ir.Node { + typecheckargs(n) + ls := n.Args + for i1, n1 := range ls { + // Special case for print: int constant is int64, not int. + if ir.IsConst(n1, constant.Int) { + ls[i1] = DefaultLit(ls[i1], types.Types[types.TINT64]) + } else { + ls[i1] = DefaultLit(ls[i1], nil) + } + } + return n +} + +// tcRealImag typechecks an OREAL or OIMAG node. +func tcRealImag(n *ir.UnaryExpr) ir.Node { + n.X = Expr(n.X) + l := n.X + t := l.Type() + if t == nil { + n.SetType(nil) + return n + } + + // Determine result type. + switch t.Kind() { + case types.TIDEAL: + n.SetType(types.UntypedFloat) + case types.TCOMPLEX64: + n.SetType(types.Types[types.TFLOAT32]) + case types.TCOMPLEX128: + n.SetType(types.Types[types.TFLOAT64]) + default: + base.Errorf("invalid argument %L for %v", l, n.Op()) + n.SetType(nil) + return n + } + return n +} + +// tcRecover typechecks an ORECOVER node. +func tcRecover(n *ir.CallExpr) ir.Node { + if len(n.Args) != 0 { + base.Errorf("too many arguments to recover") + n.SetType(nil) + return n + } + + n.SetType(types.Types[types.TINTER]) + return n +} diff --git a/src/cmd/compile/internal/typecheck/stmt.go b/src/cmd/compile/internal/typecheck/stmt.go index 889ee06d6e..bf3801eea2 100644 --- a/src/cmd/compile/internal/typecheck/stmt.go +++ b/src/cmd/compile/internal/typecheck/stmt.go @@ -11,33 +11,6 @@ import ( "cmd/internal/src" ) -// range -func typecheckrange(n *ir.RangeStmt) { - // Typechecking order is important here: - // 0. first typecheck range expression (slice/map/chan), - // it is evaluated only once and so logically it is not part of the loop. - // 1. typecheck produced values, - // this part can declare new vars and so it must be typechecked before body, - // because body can contain a closure that captures the vars. - // 2. decldepth++ to denote loop body. - // 3. typecheck body. - // 4. decldepth--. - typecheckrangeExpr(n) - - // second half of dance, the first half being typecheckrangeExpr - n.SetTypecheck(1) - ls := n.Vars - for i1, n1 := range ls { - if n1.Typecheck() == 0 { - ls[i1] = AssignExpr(ls[i1]) - } - } - - decldepth++ - Stmts(n.Body) - decldepth-- -} - func typecheckrangeExpr(n *ir.RangeStmt) { n.X = Expr(n.X) @@ -136,8 +109,326 @@ func typecheckrangeExpr(n *ir.RangeStmt) { } } +// type check assignment. +// if this assignment is the definition of a var on the left side, +// fill in the var's type. +func tcAssign(n *ir.AssignStmt) { + if base.EnableTrace && base.Flag.LowerT { + defer tracePrint("typecheckas", n)(nil) + } + + // delicate little dance. + // the definition of n may refer to this assignment + // as its definition, in which case it will call typecheckas. + // in that case, do not call typecheck back, or it will cycle. + // if the variable has a type (ntype) then typechecking + // will not look at defn, so it is okay (and desirable, + // so that the conversion below happens). + n.X = Resolve(n.X) + + if !ir.DeclaredBy(n.X, n) || n.X.Name().Ntype != nil { + n.X = AssignExpr(n.X) + } + + // Use ctxMultiOK so we can emit an "N variables but M values" error + // to be consistent with typecheckas2 (#26616). + n.Y = typecheck(n.Y, ctxExpr|ctxMultiOK) + checkassign(n, n.X) + if n.Y != nil && n.Y.Type() != nil { + if n.Y.Type().IsFuncArgStruct() { + base.Errorf("assignment mismatch: 1 variable but %v returns %d values", n.Y.(*ir.CallExpr).X, n.Y.Type().NumFields()) + // Multi-value RHS isn't actually valid for OAS; nil out + // to indicate failed typechecking. + n.Y.SetType(nil) + } else if n.X.Type() != nil { + n.Y = AssignConv(n.Y, n.X.Type(), "assignment") + } + } + + if ir.DeclaredBy(n.X, n) && n.X.Name().Ntype == nil { + n.Y = DefaultLit(n.Y, nil) + n.X.SetType(n.Y.Type()) + } + + // second half of dance. + // now that right is done, typecheck the left + // just to get it over with. see dance above. + n.SetTypecheck(1) + + if n.X.Typecheck() == 0 { + n.X = AssignExpr(n.X) + } + if !ir.IsBlank(n.X) { + types.CheckSize(n.X.Type()) // ensure width is calculated for backend + } +} + +func tcAssignList(n *ir.AssignListStmt) { + if base.EnableTrace && base.Flag.LowerT { + defer tracePrint("typecheckas2", n)(nil) + } + + ls := n.Lhs + for i1, n1 := range ls { + // delicate little dance. + n1 = Resolve(n1) + ls[i1] = n1 + + if !ir.DeclaredBy(n1, n) || n1.Name().Ntype != nil { + ls[i1] = AssignExpr(ls[i1]) + } + } + + cl := len(n.Lhs) + cr := len(n.Rhs) + if cl > 1 && cr == 1 { + n.Rhs[0] = typecheck(n.Rhs[0], ctxExpr|ctxMultiOK) + } else { + Exprs(n.Rhs) + } + checkassignlist(n, n.Lhs) + + var l ir.Node + var r ir.Node + if cl == cr { + // easy + ls := n.Lhs + rs := n.Rhs + for il, nl := range ls { + nr := rs[il] + if nl.Type() != nil && nr.Type() != nil { + rs[il] = AssignConv(nr, nl.Type(), "assignment") + } + if ir.DeclaredBy(nl, n) && nl.Name().Ntype == nil { + rs[il] = DefaultLit(rs[il], nil) + nl.SetType(rs[il].Type()) + } + } + + goto out + } + + l = n.Lhs[0] + r = n.Rhs[0] + + // x,y,z = f() + if cr == 1 { + if r.Type() == nil { + goto out + } + switch r.Op() { + case ir.OCALLMETH, ir.OCALLINTER, ir.OCALLFUNC: + if !r.Type().IsFuncArgStruct() { + break + } + cr = r.Type().NumFields() + if cr != cl { + goto mismatch + } + r.(*ir.CallExpr).Use = ir.CallUseList + n.SetOp(ir.OAS2FUNC) + for i, l := range n.Lhs { + f := r.Type().Field(i) + if f.Type != nil && l.Type() != nil { + checkassignto(f.Type, l) + } + if ir.DeclaredBy(l, n) && l.Name().Ntype == nil { + l.SetType(f.Type) + } + } + goto out + } + } + + // x, ok = y + if cl == 2 && cr == 1 { + if r.Type() == nil { + goto out + } + switch r.Op() { + case ir.OINDEXMAP, ir.ORECV, ir.ODOTTYPE: + switch r.Op() { + case ir.OINDEXMAP: + n.SetOp(ir.OAS2MAPR) + case ir.ORECV: + n.SetOp(ir.OAS2RECV) + case ir.ODOTTYPE: + r := r.(*ir.TypeAssertExpr) + n.SetOp(ir.OAS2DOTTYPE) + r.SetOp(ir.ODOTTYPE2) + } + if l.Type() != nil { + checkassignto(r.Type(), l) + } + if ir.DeclaredBy(l, n) { + l.SetType(r.Type()) + } + l := n.Lhs[1] + if l.Type() != nil && !l.Type().IsBoolean() { + checkassignto(types.Types[types.TBOOL], l) + } + if ir.DeclaredBy(l, n) && l.Name().Ntype == nil { + l.SetType(types.Types[types.TBOOL]) + } + goto out + } + } + +mismatch: + switch r.Op() { + default: + base.Errorf("assignment mismatch: %d variables but %d values", cl, cr) + case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER: + r := r.(*ir.CallExpr) + base.Errorf("assignment mismatch: %d variables but %v returns %d values", cl, r.X, cr) + } + + // second half of dance +out: + n.SetTypecheck(1) + ls = n.Lhs + for i1, n1 := range ls { + if n1.Typecheck() == 0 { + ls[i1] = AssignExpr(ls[i1]) + } + } +} + +// tcFor typechecks an OFOR node. +func tcFor(n *ir.ForStmt) ir.Node { + Stmts(n.Init()) + decldepth++ + n.Cond = Expr(n.Cond) + n.Cond = DefaultLit(n.Cond, nil) + if n.Cond != nil { + t := n.Cond.Type() + if t != nil && !t.IsBoolean() { + base.Errorf("non-bool %L used as for condition", n.Cond) + } + } + n.Post = Stmt(n.Post) + if n.Op() == ir.OFORUNTIL { + Stmts(n.Late) + } + Stmts(n.Body) + decldepth-- + return n +} + +func tcGoDefer(n *ir.GoDeferStmt) { + what := "defer" + if n.Op() == ir.OGO { + what = "go" + } + + switch n.Call.Op() { + // ok + case ir.OCALLINTER, + ir.OCALLMETH, + ir.OCALLFUNC, + ir.OCLOSE, + ir.OCOPY, + ir.ODELETE, + ir.OPANIC, + ir.OPRINT, + ir.OPRINTN, + ir.ORECOVER: + return + + case ir.OAPPEND, + ir.OCAP, + ir.OCOMPLEX, + ir.OIMAG, + ir.OLEN, + ir.OMAKE, + ir.OMAKESLICE, + ir.OMAKECHAN, + ir.OMAKEMAP, + ir.ONEW, + ir.OREAL, + ir.OLITERAL: // conversion or unsafe.Alignof, Offsetof, Sizeof + if orig := ir.Orig(n.Call); orig.Op() == ir.OCONV { + break + } + base.ErrorfAt(n.Pos(), "%s discards result of %v", what, n.Call) + return + } + + // type is broken or missing, most likely a method call on a broken type + // we will warn about the broken type elsewhere. no need to emit a potentially confusing error + if n.Call.Type() == nil || n.Call.Type().Broke() { + return + } + + if !n.Diag() { + // The syntax made sure it was a call, so this must be + // a conversion. + n.SetDiag(true) + base.ErrorfAt(n.Pos(), "%s requires function call, not conversion", what) + } +} + +// tcIf typechecks an OIF node. +func tcIf(n *ir.IfStmt) ir.Node { + Stmts(n.Init()) + n.Cond = Expr(n.Cond) + n.Cond = DefaultLit(n.Cond, nil) + if n.Cond != nil { + t := n.Cond.Type() + if t != nil && !t.IsBoolean() { + base.Errorf("non-bool %L used as if condition", n.Cond) + } + } + Stmts(n.Body) + Stmts(n.Else) + return n +} + +// range +func tcRange(n *ir.RangeStmt) { + // Typechecking order is important here: + // 0. first typecheck range expression (slice/map/chan), + // it is evaluated only once and so logically it is not part of the loop. + // 1. typecheck produced values, + // this part can declare new vars and so it must be typechecked before body, + // because body can contain a closure that captures the vars. + // 2. decldepth++ to denote loop body. + // 3. typecheck body. + // 4. decldepth--. + typecheckrangeExpr(n) + + // second half of dance, the first half being typecheckrangeExpr + n.SetTypecheck(1) + ls := n.Vars + for i1, n1 := range ls { + if n1.Typecheck() == 0 { + ls[i1] = AssignExpr(ls[i1]) + } + } + + decldepth++ + Stmts(n.Body) + decldepth-- +} + +// tcReturn typechecks an ORETURN node. +func tcReturn(n *ir.ReturnStmt) ir.Node { + typecheckargs(n) + if ir.CurFunc == nil { + base.Errorf("return outside function") + n.SetType(nil) + return n + } + + if ir.HasNamedResults(ir.CurFunc) && len(n.Results) == 0 { + return n + } + typecheckaste(ir.ORETURN, nil, false, ir.CurFunc.Type().Results(), n.Results, func() string { return "return argument" }) + return n +} + // select -func typecheckselect(sel *ir.SelectStmt) { +func tcSelect(sel *ir.SelectStmt) { var def ir.Node lno := ir.SetPos(sel) Stmts(sel.Init()) @@ -219,35 +510,43 @@ func typecheckselect(sel *ir.SelectStmt) { base.Pos = lno } -type typeSet struct { - m map[string][]typeSetEntry -} - -func (s *typeSet) add(pos src.XPos, typ *types.Type) { - if s.m == nil { - s.m = make(map[string][]typeSetEntry) +// tcSend typechecks an OSEND node. +func tcSend(n *ir.SendStmt) ir.Node { + n.Chan = Expr(n.Chan) + n.Value = Expr(n.Value) + n.Chan = DefaultLit(n.Chan, nil) + t := n.Chan.Type() + if t == nil { + return n + } + if !t.IsChan() { + base.Errorf("invalid operation: %v (send to non-chan type %v)", n, t) + return n } - // LongString does not uniquely identify types, so we need to - // disambiguate collisions with types.Identical. - // TODO(mdempsky): Add a method that *is* unique. - ls := typ.LongString() - prevs := s.m[ls] - for _, prev := range prevs { - if types.Identical(typ, prev.typ) { - base.ErrorfAt(pos, "duplicate case %v in type switch\n\tprevious case at %s", typ, base.FmtPos(prev.pos)) - return - } + if !t.ChanDir().CanSend() { + base.Errorf("invalid operation: %v (send to receive-only type %v)", n, t) + return n } - s.m[ls] = append(prevs, typeSetEntry{pos, typ}) + + n.Value = AssignConv(n.Value, t.Elem(), "send") + if n.Value.Type() == nil { + return n + } + return n } -type typeSetEntry struct { - pos src.XPos - typ *types.Type +// tcSwitch typechecks a switch statement. +func tcSwitch(n *ir.SwitchStmt) { + Stmts(n.Init()) + if n.Tag != nil && n.Tag.Op() == ir.OTYPESW { + tcSwitchType(n) + } else { + tcSwitchExpr(n) + } } -func typecheckExprSwitch(n *ir.SwitchStmt) { +func tcSwitchExpr(n *ir.SwitchStmt) { t := types.Types[types.TBOOL] if n.Tag != nil { n.Tag = Expr(n.Tag) @@ -328,7 +627,7 @@ func typecheckExprSwitch(n *ir.SwitchStmt) { } } -func typecheckTypeSwitch(n *ir.SwitchStmt) { +func tcSwitchType(n *ir.SwitchStmt) { guard := n.Tag.(*ir.TypeSwitchGuard) guard.X = Expr(guard.X) t := guard.X.Type() @@ -358,7 +657,7 @@ func typecheckTypeSwitch(n *ir.SwitchStmt) { } for i := range ls { - ls[i] = check(ls[i], ctxExpr|ctxType) + ls[i] = typecheck(ls[i], ctxExpr|ctxType) n1 := ls[i] if t == nil || n1.Type() == nil { continue @@ -424,12 +723,30 @@ func typecheckTypeSwitch(n *ir.SwitchStmt) { } } -// typecheckswitch typechecks a switch statement. -func typecheckswitch(n *ir.SwitchStmt) { - Stmts(n.Init()) - if n.Tag != nil && n.Tag.Op() == ir.OTYPESW { - typecheckTypeSwitch(n) - } else { - typecheckExprSwitch(n) - } +type typeSet struct { + m map[string][]typeSetEntry +} + +type typeSetEntry struct { + pos src.XPos + typ *types.Type +} + +func (s *typeSet) add(pos src.XPos, typ *types.Type) { + if s.m == nil { + s.m = make(map[string][]typeSetEntry) + } + + // LongString does not uniquely identify types, so we need to + // disambiguate collisions with types.Identical. + // TODO(mdempsky): Add a method that *is* unique. + ls := typ.LongString() + prevs := s.m[ls] + for _, prev := range prevs { + if types.Identical(typ, prev.typ) { + base.ErrorfAt(pos, "duplicate case %v in type switch\n\tprevious case at %s", typ, base.FmtPos(prev.pos)) + return + } + } + s.m[ls] = append(prevs, typeSetEntry{pos, typ}) } diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go index 22ebf2a4b3..178eba4484 100644 --- a/src/cmd/compile/internal/typecheck/subr.go +++ b/src/cmd/compile/internal/typecheck/subr.go @@ -81,7 +81,7 @@ func NodNil() ir.Node { // will give shortest unique addressing. // modify the tree with missing type names. func AddImplicitDots(n *ir.SelectorExpr) *ir.SelectorExpr { - n.X = check(n.X, ctxType|ctxExpr) + n.X = typecheck(n.X, ctxType|ctxExpr) if n.X.Diag() { n.SetDiag(true) } diff --git a/src/cmd/compile/internal/typecheck/type.go b/src/cmd/compile/internal/typecheck/type.go new file mode 100644 index 0000000000..4782bb9c31 --- /dev/null +++ b/src/cmd/compile/internal/typecheck/type.go @@ -0,0 +1,122 @@ +// Copyright 2009 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 typecheck + +import ( + "go/constant" + + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/types" +) + +// tcArrayType typechecks an OTARRAY node. +func tcArrayType(n *ir.ArrayType) ir.Node { + n.Elem = typecheck(n.Elem, ctxType) + if n.Elem.Type() == nil { + return n + } + if n.Len == nil { // [...]T + if !n.Diag() { + n.SetDiag(true) + base.Errorf("use of [...] array outside of array literal") + } + return n + } + n.Len = indexlit(Expr(n.Len)) + size := n.Len + if ir.ConstType(size) != constant.Int { + switch { + case size.Type() == nil: + // Error already reported elsewhere. + case size.Type().IsInteger() && size.Op() != ir.OLITERAL: + base.Errorf("non-constant array bound %v", size) + default: + base.Errorf("invalid array bound %v", size) + } + return n + } + + v := size.Val() + if ir.ConstOverflow(v, types.Types[types.TINT]) { + base.Errorf("array bound is too large") + return n + } + + if constant.Sign(v) < 0 { + base.Errorf("array bound must be non-negative") + return n + } + + bound, _ := constant.Int64Val(v) + t := types.NewArray(n.Elem.Type(), bound) + n.SetOTYPE(t) + types.CheckSize(t) + return n +} + +// tcChanType typechecks an OTCHAN node. +func tcChanType(n *ir.ChanType) ir.Node { + n.Elem = typecheck(n.Elem, ctxType) + l := n.Elem + if l.Type() == nil { + return n + } + if l.Type().NotInHeap() { + base.Errorf("chan of incomplete (or unallocatable) type not allowed") + } + n.SetOTYPE(types.NewChan(l.Type(), n.Dir)) + return n +} + +// tcFuncType typechecks an OTFUNC node. +func tcFuncType(n *ir.FuncType) ir.Node { + n.SetOTYPE(NewFuncType(n.Recv, n.Params, n.Results)) + return n +} + +// tcInterfaceType typechecks an OTINTER node. +func tcInterfaceType(n *ir.InterfaceType) ir.Node { + n.SetOTYPE(tointerface(n.Methods)) + return n +} + +// tcMapType typechecks an OTMAP node. +func tcMapType(n *ir.MapType) ir.Node { + n.Key = typecheck(n.Key, ctxType) + n.Elem = typecheck(n.Elem, ctxType) + l := n.Key + r := n.Elem + if l.Type() == nil || r.Type() == nil { + return n + } + if l.Type().NotInHeap() { + base.Errorf("incomplete (or unallocatable) map key not allowed") + } + if r.Type().NotInHeap() { + base.Errorf("incomplete (or unallocatable) map value not allowed") + } + n.SetOTYPE(types.NewMap(l.Type(), r.Type())) + mapqueue = append(mapqueue, n) // check map keys when all types are settled + return n +} + +// tcSliceType typechecks an OTSLICE node. +func tcSliceType(n *ir.SliceType) ir.Node { + n.Elem = typecheck(n.Elem, ctxType) + if n.Elem.Type() == nil { + return n + } + t := types.NewSlice(n.Elem.Type()) + n.SetOTYPE(t) + types.CheckSize(t) + return n +} + +// tcStructType typechecks an OTSTRUCT node. +func tcStructType(n *ir.StructType) ir.Node { + n.SetOTYPE(NewStructType(n.Fields)) + return n +} diff --git a/src/cmd/compile/internal/typecheck/typecheck.go b/src/cmd/compile/internal/typecheck/typecheck.go index 2abf0a7824..bf43402d3d 100644 --- a/src/cmd/compile/internal/typecheck/typecheck.go +++ b/src/cmd/compile/internal/typecheck/typecheck.go @@ -122,9 +122,9 @@ func Package() { } } -func AssignExpr(n ir.Node) ir.Node { return check(n, ctxExpr|ctxAssign) } -func Expr(n ir.Node) ir.Node { return check(n, ctxExpr) } -func Stmt(n ir.Node) ir.Node { return check(n, ctxStmt) } +func AssignExpr(n ir.Node) ir.Node { return typecheck(n, ctxExpr|ctxAssign) } +func Expr(n ir.Node) ir.Node { return typecheck(n, ctxExpr) } +func Stmt(n ir.Node) ir.Node { return typecheck(n, ctxStmt) } func Exprs(exprs []ir.Node) { typecheckslice(exprs, ctxExpr) } func Stmts(stmts []ir.Node) { typecheckslice(stmts, ctxStmt) } @@ -138,13 +138,13 @@ func Call(call *ir.CallExpr) { if t.NumResults() > 0 { ctx = ctxExpr | ctxMultiOK } - if check(call, ctx) != call { + if typecheck(call, ctx) != call { panic("bad typecheck") } } func Callee(n ir.Node) ir.Node { - return check(n, ctxExpr|ctxCallee) + return typecheck(n, ctxExpr|ctxCallee) } func FuncBody(n *ir.Func) { @@ -277,7 +277,7 @@ func Resolve(n ir.Node) (res ir.Node) { func typecheckslice(l []ir.Node, top int) { for i := range l { - l[i] = check(l[i], top) + l[i] = typecheck(l[i], top) } } @@ -367,13 +367,13 @@ func Func(fn *ir.Func) { } func typecheckNtype(n ir.Ntype) ir.Ntype { - return check(n, ctxType).(ir.Ntype) + return typecheck(n, ctxType).(ir.Ntype) } -// check type checks node n. -// The result of check MUST be assigned back to n, e.g. -// n.Left = check(n.Left, top) -func check(n ir.Node, top int) (res ir.Node) { +// typecheck type checks node n. +// The result of typecheck MUST be assigned back to n, e.g. +// n.Left = typecheck(n.Left, top) +func typecheck(n ir.Node, top int) (res ir.Node) { // cannot type check until all the source has been parsed if !TypecheckAllowed { base.Fatalf("early typecheck") @@ -572,11 +572,7 @@ func indexlit(n ir.Node) ir.Node { } // typecheck1 should ONLY be called from typecheck. -func typecheck1(n ir.Node, top int) (res ir.Node) { - if base.EnableTrace && base.Flag.LowerT { - defer tracePrint("typecheck1", n)(&res) - } - +func typecheck1(n ir.Node, top int) ir.Node { switch n.Op() { case ir.OLITERAL, ir.ONAME, ir.ONONAME, ir.OTYPE: if n.Sym() == nil { @@ -653,136 +649,35 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { case ir.OTSLICE: n := n.(*ir.SliceType) - n.Elem = check(n.Elem, ctxType) - if n.Elem.Type() == nil { - return n - } - t := types.NewSlice(n.Elem.Type()) - n.SetOTYPE(t) - types.CheckSize(t) - return n + return tcSliceType(n) case ir.OTARRAY: n := n.(*ir.ArrayType) - n.Elem = check(n.Elem, ctxType) - if n.Elem.Type() == nil { - return n - } - if n.Len == nil { // [...]T - if !n.Diag() { - n.SetDiag(true) - base.Errorf("use of [...] array outside of array literal") - } - return n - } - n.Len = indexlit(Expr(n.Len)) - size := n.Len - if ir.ConstType(size) != constant.Int { - switch { - case size.Type() == nil: - // Error already reported elsewhere. - case size.Type().IsInteger() && size.Op() != ir.OLITERAL: - base.Errorf("non-constant array bound %v", size) - default: - base.Errorf("invalid array bound %v", size) - } - return n - } - - v := size.Val() - if ir.ConstOverflow(v, types.Types[types.TINT]) { - base.Errorf("array bound is too large") - return n - } - - if constant.Sign(v) < 0 { - base.Errorf("array bound must be non-negative") - return n - } - - bound, _ := constant.Int64Val(v) - t := types.NewArray(n.Elem.Type(), bound) - n.SetOTYPE(t) - types.CheckSize(t) - return n + return tcArrayType(n) case ir.OTMAP: n := n.(*ir.MapType) - n.Key = check(n.Key, ctxType) - n.Elem = check(n.Elem, ctxType) - l := n.Key - r := n.Elem - if l.Type() == nil || r.Type() == nil { - return n - } - if l.Type().NotInHeap() { - base.Errorf("incomplete (or unallocatable) map key not allowed") - } - if r.Type().NotInHeap() { - base.Errorf("incomplete (or unallocatable) map value not allowed") - } - n.SetOTYPE(types.NewMap(l.Type(), r.Type())) - mapqueue = append(mapqueue, n) // check map keys when all types are settled - return n + return tcMapType(n) case ir.OTCHAN: n := n.(*ir.ChanType) - n.Elem = check(n.Elem, ctxType) - l := n.Elem - if l.Type() == nil { - return n - } - if l.Type().NotInHeap() { - base.Errorf("chan of incomplete (or unallocatable) type not allowed") - } - n.SetOTYPE(types.NewChan(l.Type(), n.Dir)) - return n + return tcChanType(n) case ir.OTSTRUCT: n := n.(*ir.StructType) - n.SetOTYPE(NewStructType(n.Fields)) - return n + return tcStructType(n) case ir.OTINTER: n := n.(*ir.InterfaceType) - n.SetOTYPE(tointerface(n.Methods)) - return n + return tcInterfaceType(n) case ir.OTFUNC: n := n.(*ir.FuncType) - n.SetOTYPE(NewFuncType(n.Recv, n.Params, n.Results)) - return n - + return tcFuncType(n) // type or expr case ir.ODEREF: n := n.(*ir.StarExpr) - n.X = check(n.X, ctxExpr|ctxType) - l := n.X - t := l.Type() - if t == nil { - n.SetType(nil) - return n - } - if l.Op() == ir.OTYPE { - n.SetOTYPE(types.NewPtr(l.Type())) - // Ensure l.Type gets dowidth'd for the backend. Issue 20174. - types.CheckSize(l.Type()) - return n - } - - if !t.IsPtr() { - if top&(ctxExpr|ctxStmt) != 0 { - base.Errorf("invalid indirect of %L", n.X) - n.SetType(nil) - return n - } - base.Errorf("%v is not a type", l) - return n - } - - n.SetType(t.Elem()) - return n - + return tcStar(n, top) // arithmetic exprs case ir.OASOP, ir.OADD, @@ -804,858 +699,56 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { ir.OOROR, ir.OSUB, ir.OXOR: - var l, r ir.Node - var setLR func() - switch n := n.(type) { - case *ir.AssignOpStmt: - l, r = n.X, n.Y - setLR = func() { n.X = l; n.Y = r } - case *ir.BinaryExpr: - l, r = n.X, n.Y - setLR = func() { n.X = l; n.Y = r } - case *ir.LogicalExpr: - l, r = n.X, n.Y - setLR = func() { n.X = l; n.Y = r } - } - l = Expr(l) - r = Expr(r) - setLR() - if l.Type() == nil || r.Type() == nil { - n.SetType(nil) - return n - } - op := n.Op() - if n.Op() == ir.OASOP { - n := n.(*ir.AssignOpStmt) - checkassign(n, l) - if n.IncDec && !okforarith[l.Type().Kind()] { - base.Errorf("invalid operation: %v (non-numeric type %v)", n, l.Type()) - n.SetType(nil) - return n - } - // TODO(marvin): Fix Node.EType type union. - op = n.AsOp - } - if op == ir.OLSH || op == ir.ORSH { - r = DefaultLit(r, types.Types[types.TUINT]) - setLR() - t := r.Type() - if !t.IsInteger() { - base.Errorf("invalid operation: %v (shift count type %v, must be integer)", n, r.Type()) - n.SetType(nil) - return n - } - if t.IsSigned() && !types.AllowsGoVersion(curpkg(), 1, 13) { - base.ErrorfVers("go1.13", "invalid operation: %v (signed shift count type %v)", n, r.Type()) - n.SetType(nil) - return n - } - t = l.Type() - if t != nil && t.Kind() != types.TIDEAL && !t.IsInteger() { - base.Errorf("invalid operation: %v (shift of type %v)", n, t) - n.SetType(nil) - return n - } - - // no defaultlit for left - // the outer context gives the type - n.SetType(l.Type()) - if (l.Type() == types.UntypedFloat || l.Type() == types.UntypedComplex) && r.Op() == ir.OLITERAL { - n.SetType(types.UntypedInt) - } - return n - } - - // For "x == x && len(s)", it's better to report that "len(s)" (type int) - // can't be used with "&&" than to report that "x == x" (type untyped bool) - // can't be converted to int (see issue #41500). - if n.Op() == ir.OANDAND || n.Op() == ir.OOROR { - n := n.(*ir.LogicalExpr) - if !n.X.Type().IsBoolean() { - base.Errorf("invalid operation: %v (operator %v not defined on %s)", n, n.Op(), typekind(n.X.Type())) - n.SetType(nil) - return n - } - if !n.Y.Type().IsBoolean() { - base.Errorf("invalid operation: %v (operator %v not defined on %s)", n, n.Op(), typekind(n.Y.Type())) - n.SetType(nil) - return n - } - } - - // ideal mixed with non-ideal - l, r = defaultlit2(l, r, false) - setLR() - - if l.Type() == nil || r.Type() == nil { - n.SetType(nil) - return n - } - t := l.Type() - if t.Kind() == types.TIDEAL { - t = r.Type() - } - et := t.Kind() - if et == types.TIDEAL { - et = types.TINT - } - aop := ir.OXXX - if iscmp[n.Op()] && t.Kind() != types.TIDEAL && !types.Identical(l.Type(), r.Type()) { - // comparison is okay as long as one side is - // assignable to the other. convert so they have - // the same type. - // - // the only conversion that isn't a no-op is concrete == interface. - // in that case, check comparability of the concrete type. - // The conversion allocates, so only do it if the concrete type is huge. - converted := false - if r.Type().Kind() != types.TBLANK { - aop, _ = assignop(l.Type(), r.Type()) - if aop != ir.OXXX { - if r.Type().IsInterface() && !l.Type().IsInterface() && !types.IsComparable(l.Type()) { - base.Errorf("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(l.Type())) - n.SetType(nil) - return n - } - - types.CalcSize(l.Type()) - if r.Type().IsInterface() == l.Type().IsInterface() || l.Type().Width >= 1<<16 { - l = ir.NewConvExpr(base.Pos, aop, r.Type(), l) - l.SetTypecheck(1) - setLR() - } - - t = r.Type() - converted = true - } - } - - if !converted && l.Type().Kind() != types.TBLANK { - aop, _ = assignop(r.Type(), l.Type()) - if aop != ir.OXXX { - if l.Type().IsInterface() && !r.Type().IsInterface() && !types.IsComparable(r.Type()) { - base.Errorf("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(r.Type())) - n.SetType(nil) - return n - } - - types.CalcSize(r.Type()) - if r.Type().IsInterface() == l.Type().IsInterface() || r.Type().Width >= 1<<16 { - r = ir.NewConvExpr(base.Pos, aop, l.Type(), r) - r.SetTypecheck(1) - setLR() - } - - t = l.Type() - } - } - - et = t.Kind() - } - - if t.Kind() != types.TIDEAL && !types.Identical(l.Type(), r.Type()) { - l, r = defaultlit2(l, r, true) - if l.Type() == nil || r.Type() == nil { - n.SetType(nil) - return n - } - if l.Type().IsInterface() == r.Type().IsInterface() || aop == 0 { - base.Errorf("invalid operation: %v (mismatched types %v and %v)", n, l.Type(), r.Type()) - n.SetType(nil) - return n - } - } - - if t.Kind() == types.TIDEAL { - t = mixUntyped(l.Type(), r.Type()) - } - if dt := defaultType(t); !okfor[op][dt.Kind()] { - base.Errorf("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(t)) - n.SetType(nil) - return n - } - - // okfor allows any array == array, map == map, func == func. - // restrict to slice/map/func == nil and nil == slice/map/func. - if l.Type().IsArray() && !types.IsComparable(l.Type()) { - base.Errorf("invalid operation: %v (%v cannot be compared)", n, l.Type()) - n.SetType(nil) - return n - } - - if l.Type().IsSlice() && !ir.IsNil(l) && !ir.IsNil(r) { - base.Errorf("invalid operation: %v (slice can only be compared to nil)", n) - n.SetType(nil) - return n - } - - if l.Type().IsMap() && !ir.IsNil(l) && !ir.IsNil(r) { - base.Errorf("invalid operation: %v (map can only be compared to nil)", n) - n.SetType(nil) - return n - } - - if l.Type().Kind() == types.TFUNC && !ir.IsNil(l) && !ir.IsNil(r) { - base.Errorf("invalid operation: %v (func can only be compared to nil)", n) - n.SetType(nil) - return n - } - - if l.Type().IsStruct() { - if f := types.IncomparableField(l.Type()); f != nil { - base.Errorf("invalid operation: %v (struct containing %v cannot be compared)", n, f.Type) - n.SetType(nil) - return n - } - } - - if iscmp[n.Op()] { - t = types.UntypedBool - n.SetType(t) - if con := EvalConst(n); con.Op() == ir.OLITERAL { - return con - } - l, r = defaultlit2(l, r, true) - setLR() - return n - } - - if et == types.TSTRING && n.Op() == ir.OADD { - // create or update OADDSTR node with list of strings in x + y + z + (w + v) + ... - n := n.(*ir.BinaryExpr) - var add *ir.AddStringExpr - if l.Op() == ir.OADDSTR { - add = l.(*ir.AddStringExpr) - add.SetPos(n.Pos()) - } else { - add = ir.NewAddStringExpr(n.Pos(), []ir.Node{l}) - } - if r.Op() == ir.OADDSTR { - r := r.(*ir.AddStringExpr) - add.List.Append(r.List.Take()...) - } else { - add.List.Append(r) - } - add.SetType(t) - return add - } - - if (op == ir.ODIV || op == ir.OMOD) && ir.IsConst(r, constant.Int) { - if constant.Sign(r.Val()) == 0 { - base.Errorf("division by zero") - n.SetType(nil) - return n - } - } - - n.SetType(t) - return n + return tcArith(n) case ir.OBITNOT, ir.ONEG, ir.ONOT, ir.OPLUS: n := n.(*ir.UnaryExpr) - n.X = Expr(n.X) - l := n.X - t := l.Type() - if t == nil { - n.SetType(nil) - return n - } - if !okfor[n.Op()][defaultType(t).Kind()] { - base.Errorf("invalid operation: %v (operator %v not defined on %s)", n, n.Op(), typekind(t)) - n.SetType(nil) - return n - } - - n.SetType(t) - return n + return tcUnaryArith(n) // exprs case ir.OADDR: n := n.(*ir.AddrExpr) - n.X = Expr(n.X) - if n.X.Type() == nil { - n.SetType(nil) - return n - } - - switch n.X.Op() { - case ir.OARRAYLIT, ir.OMAPLIT, ir.OSLICELIT, ir.OSTRUCTLIT: - n.SetOp(ir.OPTRLIT) - - default: - checklvalue(n.X, "take the address of") - r := ir.OuterValue(n.X) - if r.Op() == ir.ONAME { - r := r.(*ir.Name) - if ir.Orig(r) != r { - base.Fatalf("found non-orig name node %v", r) // TODO(mdempsky): What does this mean? - } - r.Name().SetAddrtaken(true) - if r.Name().IsClosureVar() && !CaptureVarsComplete { - // Mark the original variable as Addrtaken so that capturevars - // knows not to pass it by value. - // But if the capturevars phase is complete, don't touch it, - // in case l.Name's containing function has not yet been compiled. - r.Name().Defn.Name().SetAddrtaken(true) - } - } - n.X = DefaultLit(n.X, nil) - if n.X.Type() == nil { - n.SetType(nil) - return n - } - } - - n.SetType(types.NewPtr(n.X.Type())) - return n + return tcAddr(n) case ir.OCOMPLIT: - return typecheckcomplit(n.(*ir.CompLitExpr)) + return tcCompLit(n.(*ir.CompLitExpr)) case ir.OXDOT, ir.ODOT: n := n.(*ir.SelectorExpr) - if n.Op() == ir.OXDOT { - n = AddImplicitDots(n) - n.SetOp(ir.ODOT) - if n.X == nil { - n.SetType(nil) - return n - } - } - - n.X = check(n.X, ctxExpr|ctxType) - - n.X = DefaultLit(n.X, nil) - - t := n.X.Type() - if t == nil { - base.UpdateErrorDot(ir.Line(n), fmt.Sprint(n.X), fmt.Sprint(n)) - n.SetType(nil) - return n - } - - s := n.Sel - - if n.X.Op() == ir.OTYPE { - return typecheckMethodExpr(n) - } - - if t.IsPtr() && !t.Elem().IsInterface() { - t = t.Elem() - if t == nil { - n.SetType(nil) - return n - } - n.SetOp(ir.ODOTPTR) - types.CheckSize(t) - } - - if n.Sel.IsBlank() { - base.Errorf("cannot refer to blank field or method") - n.SetType(nil) - return n - } - - if lookdot(n, t, 0) == nil { - // Legitimate field or method lookup failed, try to explain the error - switch { - case t.IsEmptyInterface(): - base.Errorf("%v undefined (type %v is interface with no methods)", n, n.X.Type()) - - case t.IsPtr() && t.Elem().IsInterface(): - // Pointer to interface is almost always a mistake. - base.Errorf("%v undefined (type %v is pointer to interface, not interface)", n, n.X.Type()) - - case lookdot(n, t, 1) != nil: - // Field or method matches by name, but it is not exported. - base.Errorf("%v undefined (cannot refer to unexported field or method %v)", n, n.Sel) - - default: - if mt := lookdot(n, t, 2); mt != nil && visible(mt.Sym) { // Case-insensitive lookup. - base.Errorf("%v undefined (type %v has no field or method %v, but does have %v)", n, n.X.Type(), n.Sel, mt.Sym) - } else { - base.Errorf("%v undefined (type %v has no field or method %v)", n, n.X.Type(), n.Sel) - } - } - n.SetType(nil) - return n - } - - if (n.Op() == ir.ODOTINTER || n.Op() == ir.ODOTMETH) && top&ctxCallee == 0 { - return typecheckpartialcall(n, s) - } - return n + return tcDot(n, top) case ir.ODOTTYPE: n := n.(*ir.TypeAssertExpr) - n.X = Expr(n.X) - n.X = DefaultLit(n.X, nil) - l := n.X - t := l.Type() - if t == nil { - n.SetType(nil) - return n - } - if !t.IsInterface() { - base.Errorf("invalid type assertion: %v (non-interface type %v on left)", n, t) - n.SetType(nil) - return n - } - - if n.Ntype != nil { - n.Ntype = check(n.Ntype, ctxType) - n.SetType(n.Ntype.Type()) - n.Ntype = nil - if n.Type() == nil { - return n - } - } - - if n.Type() != nil && !n.Type().IsInterface() { - var missing, have *types.Field - var ptr int - if !implements(n.Type(), t, &missing, &have, &ptr) { - if have != nil && have.Sym == missing.Sym { - base.Errorf("impossible type assertion:\n\t%v does not implement %v (wrong type for %v method)\n"+ - "\t\thave %v%S\n\t\twant %v%S", n.Type(), t, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type) - } else if ptr != 0 { - base.Errorf("impossible type assertion:\n\t%v does not implement %v (%v method has pointer receiver)", n.Type(), t, missing.Sym) - } else if have != nil { - base.Errorf("impossible type assertion:\n\t%v does not implement %v (missing %v method)\n"+ - "\t\thave %v%S\n\t\twant %v%S", n.Type(), t, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type) - } else { - base.Errorf("impossible type assertion:\n\t%v does not implement %v (missing %v method)", n.Type(), t, missing.Sym) - } - n.SetType(nil) - return n - } - } - return n + return tcDotType(n) case ir.OINDEX: n := n.(*ir.IndexExpr) - n.X = Expr(n.X) - n.X = DefaultLit(n.X, nil) - n.X = implicitstar(n.X) - l := n.X - n.Index = Expr(n.Index) - r := n.Index - t := l.Type() - if t == nil || r.Type() == nil { - n.SetType(nil) - return n - } - switch t.Kind() { - default: - base.Errorf("invalid operation: %v (type %v does not support indexing)", n, t) - n.SetType(nil) - return n - - case types.TSTRING, types.TARRAY, types.TSLICE: - n.Index = indexlit(n.Index) - if t.IsString() { - n.SetType(types.ByteType) - } else { - n.SetType(t.Elem()) - } - why := "string" - if t.IsArray() { - why = "array" - } else if t.IsSlice() { - why = "slice" - } - - if n.Index.Type() != nil && !n.Index.Type().IsInteger() { - base.Errorf("non-integer %s index %v", why, n.Index) - return n - } - - if !n.Bounded() && ir.IsConst(n.Index, constant.Int) { - x := n.Index.Val() - if constant.Sign(x) < 0 { - base.Errorf("invalid %s index %v (index must be non-negative)", why, n.Index) - } else if t.IsArray() && constant.Compare(x, token.GEQ, constant.MakeInt64(t.NumElem())) { - base.Errorf("invalid array index %v (out of bounds for %d-element array)", n.Index, t.NumElem()) - } else if ir.IsConst(n.X, constant.String) && constant.Compare(x, token.GEQ, constant.MakeInt64(int64(len(ir.StringVal(n.X))))) { - base.Errorf("invalid string index %v (out of bounds for %d-byte string)", n.Index, len(ir.StringVal(n.X))) - } else if ir.ConstOverflow(x, types.Types[types.TINT]) { - base.Errorf("invalid %s index %v (index too large)", why, n.Index) - } - } - - case types.TMAP: - n.Index = AssignConv(n.Index, t.Key(), "map index") - n.SetType(t.Elem()) - n.SetOp(ir.OINDEXMAP) - n.Assigned = false - } - return n + return tcIndex(n) case ir.ORECV: n := n.(*ir.UnaryExpr) - n.X = Expr(n.X) - n.X = DefaultLit(n.X, nil) - l := n.X - t := l.Type() - if t == nil { - n.SetType(nil) - return n - } - if !t.IsChan() { - base.Errorf("invalid operation: %v (receive from non-chan type %v)", n, t) - n.SetType(nil) - return n - } - - if !t.ChanDir().CanRecv() { - base.Errorf("invalid operation: %v (receive from send-only type %v)", n, t) - n.SetType(nil) - return n - } - - n.SetType(t.Elem()) - return n + return tcRecv(n) case ir.OSEND: n := n.(*ir.SendStmt) - n.Chan = Expr(n.Chan) - n.Value = Expr(n.Value) - n.Chan = DefaultLit(n.Chan, nil) - t := n.Chan.Type() - if t == nil { - return n - } - if !t.IsChan() { - base.Errorf("invalid operation: %v (send to non-chan type %v)", n, t) - return n - } - - if !t.ChanDir().CanSend() { - base.Errorf("invalid operation: %v (send to receive-only type %v)", n, t) - return n - } - - n.Value = AssignConv(n.Value, t.Elem(), "send") - if n.Value.Type() == nil { - return n - } - return n + return tcSend(n) case ir.OSLICEHEADER: - // Errors here are Fatalf instead of Errorf because only the compiler - // can construct an OSLICEHEADER node. - // Components used in OSLICEHEADER that are supplied by parsed source code - // have already been typechecked in e.g. OMAKESLICE earlier. n := n.(*ir.SliceHeaderExpr) - t := n.Type() - if t == nil { - base.Fatalf("no type specified for OSLICEHEADER") - } - - if !t.IsSlice() { - base.Fatalf("invalid type %v for OSLICEHEADER", n.Type()) - } - - if n.Ptr == nil || n.Ptr.Type() == nil || !n.Ptr.Type().IsUnsafePtr() { - base.Fatalf("need unsafe.Pointer for OSLICEHEADER") - } - - if x := len(n.LenCap); x != 2 { - base.Fatalf("expected 2 params (len, cap) for OSLICEHEADER, got %d", x) - } - - n.Ptr = Expr(n.Ptr) - l := Expr(n.LenCap[0]) - c := Expr(n.LenCap[1]) - l = DefaultLit(l, types.Types[types.TINT]) - c = DefaultLit(c, types.Types[types.TINT]) - - if ir.IsConst(l, constant.Int) && ir.Int64Val(l) < 0 { - base.Fatalf("len for OSLICEHEADER must be non-negative") - } - - if ir.IsConst(c, constant.Int) && ir.Int64Val(c) < 0 { - base.Fatalf("cap for OSLICEHEADER must be non-negative") - } - - if ir.IsConst(l, constant.Int) && ir.IsConst(c, constant.Int) && constant.Compare(l.Val(), token.GTR, c.Val()) { - base.Fatalf("len larger than cap for OSLICEHEADER") - } - - n.LenCap[0] = l - n.LenCap[1] = c - return n + return tcSliceHeader(n) case ir.OMAKESLICECOPY: - // Errors here are Fatalf instead of Errorf because only the compiler - // can construct an OMAKESLICECOPY node. - // Components used in OMAKESCLICECOPY that are supplied by parsed source code - // have already been typechecked in OMAKE and OCOPY earlier. n := n.(*ir.MakeExpr) - t := n.Type() - - if t == nil { - base.Fatalf("no type specified for OMAKESLICECOPY") - } - - if !t.IsSlice() { - base.Fatalf("invalid type %v for OMAKESLICECOPY", n.Type()) - } - - if n.Len == nil { - base.Fatalf("missing len argument for OMAKESLICECOPY") - } - - if n.Cap == nil { - base.Fatalf("missing slice argument to copy for OMAKESLICECOPY") - } - - n.Len = Expr(n.Len) - n.Cap = Expr(n.Cap) - - n.Len = DefaultLit(n.Len, types.Types[types.TINT]) - - if !n.Len.Type().IsInteger() && n.Type().Kind() != types.TIDEAL { - base.Errorf("non-integer len argument in OMAKESLICECOPY") - } - - if ir.IsConst(n.Len, constant.Int) { - if ir.ConstOverflow(n.Len.Val(), types.Types[types.TINT]) { - base.Fatalf("len for OMAKESLICECOPY too large") - } - if constant.Sign(n.Len.Val()) < 0 { - base.Fatalf("len for OMAKESLICECOPY must be non-negative") - } - } - return n + return tcMakeSliceCopy(n) case ir.OSLICE, ir.OSLICE3: n := n.(*ir.SliceExpr) - n.X = Expr(n.X) - low, high, max := n.SliceBounds() - hasmax := n.Op().IsSlice3() - low = Expr(low) - high = Expr(high) - max = Expr(max) - n.X = DefaultLit(n.X, nil) - low = indexlit(low) - high = indexlit(high) - max = indexlit(max) - n.SetSliceBounds(low, high, max) - l := n.X - if l.Type() == nil { - n.SetType(nil) - return n - } - if l.Type().IsArray() { - if !ir.IsAssignable(n.X) { - base.Errorf("invalid operation %v (slice of unaddressable value)", n) - n.SetType(nil) - return n - } - - addr := NodAddr(n.X) - addr.SetImplicit(true) - n.X = Expr(addr) - l = n.X - } - t := l.Type() - var tp *types.Type - if t.IsString() { - if hasmax { - base.Errorf("invalid operation %v (3-index slice of string)", n) - n.SetType(nil) - return n - } - n.SetType(t) - n.SetOp(ir.OSLICESTR) - } else if t.IsPtr() && t.Elem().IsArray() { - tp = t.Elem() - n.SetType(types.NewSlice(tp.Elem())) - types.CalcSize(n.Type()) - if hasmax { - n.SetOp(ir.OSLICE3ARR) - } else { - n.SetOp(ir.OSLICEARR) - } - } else if t.IsSlice() { - n.SetType(t) - } else { - base.Errorf("cannot slice %v (type %v)", l, t) - n.SetType(nil) - return n - } - - if low != nil && !checksliceindex(l, low, tp) { - n.SetType(nil) - return n - } - if high != nil && !checksliceindex(l, high, tp) { - n.SetType(nil) - return n - } - if max != nil && !checksliceindex(l, max, tp) { - n.SetType(nil) - return n - } - if !checksliceconst(low, high) || !checksliceconst(low, max) || !checksliceconst(high, max) { - n.SetType(nil) - return n - } - return n + return tcSlice(n) // call and call like case ir.OCALL: n := n.(*ir.CallExpr) - n.Use = ir.CallUseExpr - if top == ctxStmt { - n.Use = ir.CallUseStmt - } - Stmts(n.Init()) // imported rewritten f(g()) calls (#30907) - n.X = check(n.X, ctxExpr|ctxType|ctxCallee) - if n.X.Diag() { - n.SetDiag(true) - } - - l := n.X - - if l.Op() == ir.ONAME && l.(*ir.Name).BuiltinOp != 0 { - l := l.(*ir.Name) - if n.IsDDD && l.BuiltinOp != ir.OAPPEND { - base.Errorf("invalid use of ... with builtin %v", l) - } - - // builtin: OLEN, OCAP, etc. - switch l.BuiltinOp { - default: - base.Fatalf("unknown builtin %v", l) - - case ir.OAPPEND, ir.ODELETE, ir.OMAKE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER: - n.SetOp(l.BuiltinOp) - n.X = nil - n.SetTypecheck(0) // re-typechecking new op is OK, not a loop - return check(n, top) - - case ir.OCAP, ir.OCLOSE, ir.OIMAG, ir.OLEN, ir.OPANIC, ir.OREAL: - typecheckargs(n) - fallthrough - case ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: - arg, ok := needOneArg(n, "%v", n.Op()) - if !ok { - n.SetType(nil) - return n - } - u := ir.NewUnaryExpr(n.Pos(), l.BuiltinOp, arg) - return check(ir.InitExpr(n.Init(), u), top) // typecheckargs can add to old.Init - - case ir.OCOMPLEX, ir.OCOPY: - typecheckargs(n) - arg1, arg2, ok := needTwoArgs(n) - if !ok { - n.SetType(nil) - return n - } - b := ir.NewBinaryExpr(n.Pos(), l.BuiltinOp, arg1, arg2) - return check(ir.InitExpr(n.Init(), b), top) // typecheckargs can add to old.Init - } - panic("unreachable") - } - - n.X = DefaultLit(n.X, nil) - l = n.X - if l.Op() == ir.OTYPE { - if n.IsDDD { - if !l.Type().Broke() { - base.Errorf("invalid use of ... in type conversion to %v", l.Type()) - } - n.SetDiag(true) - } - - // pick off before type-checking arguments - arg, ok := needOneArg(n, "conversion to %v", l.Type()) - if !ok { - n.SetType(nil) - return n - } - - n := ir.NewConvExpr(n.Pos(), ir.OCONV, nil, arg) - n.SetType(l.Type()) - return typecheck1(n, top) - } - - typecheckargs(n) - t := l.Type() - if t == nil { - n.SetType(nil) - return n - } - types.CheckSize(t) - - switch l.Op() { - case ir.ODOTINTER: - n.SetOp(ir.OCALLINTER) - - case ir.ODOTMETH: - l := l.(*ir.SelectorExpr) - n.SetOp(ir.OCALLMETH) - - // typecheckaste was used here but there wasn't enough - // information further down the call chain to know if we - // were testing a method receiver for unexported fields. - // It isn't necessary, so just do a sanity check. - tp := t.Recv().Type - - if l.X == nil || !types.Identical(l.X.Type(), tp) { - base.Fatalf("method receiver") - } - - default: - n.SetOp(ir.OCALLFUNC) - if t.Kind() != types.TFUNC { - // TODO(mdempsky): Remove "o.Sym() != nil" once we stop - // using ir.Name for numeric literals. - if o := ir.Orig(l); o.Name() != nil && o.Sym() != nil && types.BuiltinPkg.Lookup(o.Sym().Name).Def != nil { - // be more specific when the non-function - // name matches a predeclared function - base.Errorf("cannot call non-function %L, declared at %s", - l, base.FmtPos(o.Name().Pos())) - } else { - base.Errorf("cannot call non-function %L", l) - } - n.SetType(nil) - return n - } - } - - typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args, func() string { return fmt.Sprintf("argument to %v", n.X) }) - if t.NumResults() == 0 { - return n - } - if t.NumResults() == 1 { - n.SetType(l.Type().Results().Field(0).Type) - - if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.ONAME { - if sym := n.X.(*ir.Name).Sym(); types.IsRuntimePkg(sym.Pkg) && sym.Name == "getg" { - // Emit code for runtime.getg() directly instead of calling function. - // Most such rewrites (for example the similar one for math.Sqrt) should be done in walk, - // so that the ordering pass can make sure to preserve the semantics of the original code - // (in particular, the exact time of the function call) by introducing temporaries. - // In this case, we know getg() always returns the same result within a given function - // and we want to avoid the temporaries, so we do the rewrite earlier than is typical. - n.SetOp(ir.OGETG) - } - } - return n - } - - // multiple return - if top&(ctxMultiOK|ctxStmt) == 0 { - base.Errorf("multiple-value %v() in single-value context", l) - return n - } - - n.SetType(l.Type().Results()) - return n + return tcCall(n, top) case ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: n := n.(*ir.UnaryExpr) @@ -1664,464 +757,59 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { case ir.OCAP, ir.OLEN: n := n.(*ir.UnaryExpr) - n.X = Expr(n.X) - n.X = DefaultLit(n.X, nil) - n.X = implicitstar(n.X) - l := n.X - t := l.Type() - if t == nil { - n.SetType(nil) - return n - } - - var ok bool - if n.Op() == ir.OLEN { - ok = okforlen[t.Kind()] - } else { - ok = okforcap[t.Kind()] - } - if !ok { - base.Errorf("invalid argument %L for %v", l, n.Op()) - n.SetType(nil) - return n - } - - n.SetType(types.Types[types.TINT]) - return n + return tcLenCap(n) case ir.OREAL, ir.OIMAG: n := n.(*ir.UnaryExpr) - n.X = Expr(n.X) - l := n.X - t := l.Type() - if t == nil { - n.SetType(nil) - return n - } - - // Determine result type. - switch t.Kind() { - case types.TIDEAL: - n.SetType(types.UntypedFloat) - case types.TCOMPLEX64: - n.SetType(types.Types[types.TFLOAT32]) - case types.TCOMPLEX128: - n.SetType(types.Types[types.TFLOAT64]) - default: - base.Errorf("invalid argument %L for %v", l, n.Op()) - n.SetType(nil) - return n - } - return n + return tcRealImag(n) case ir.OCOMPLEX: n := n.(*ir.BinaryExpr) - l := Expr(n.X) - r := Expr(n.Y) - if l.Type() == nil || r.Type() == nil { - n.SetType(nil) - return n - } - l, r = defaultlit2(l, r, false) - if l.Type() == nil || r.Type() == nil { - n.SetType(nil) - return n - } - n.X = l - n.Y = r - - if !types.Identical(l.Type(), r.Type()) { - base.Errorf("invalid operation: %v (mismatched types %v and %v)", n, l.Type(), r.Type()) - n.SetType(nil) - return n - } - - var t *types.Type - switch l.Type().Kind() { - default: - base.Errorf("invalid operation: %v (arguments have type %v, expected floating-point)", n, l.Type()) - n.SetType(nil) - return n - - case types.TIDEAL: - t = types.UntypedComplex - - case types.TFLOAT32: - t = types.Types[types.TCOMPLEX64] - - case types.TFLOAT64: - t = types.Types[types.TCOMPLEX128] - } - n.SetType(t) - return n + return tcComplex(n) case ir.OCLOSE: n := n.(*ir.UnaryExpr) - n.X = Expr(n.X) - n.X = DefaultLit(n.X, nil) - l := n.X - t := l.Type() - if t == nil { - n.SetType(nil) - return n - } - if !t.IsChan() { - base.Errorf("invalid operation: %v (non-chan type %v)", n, t) - n.SetType(nil) - return n - } - - if !t.ChanDir().CanSend() { - base.Errorf("invalid operation: %v (cannot close receive-only channel)", n) - n.SetType(nil) - return n - } - return n + return tcClose(n) case ir.ODELETE: n := n.(*ir.CallExpr) - typecheckargs(n) - args := n.Args - if len(args) == 0 { - base.Errorf("missing arguments to delete") - n.SetType(nil) - return n - } - - if len(args) == 1 { - base.Errorf("missing second (key) argument to delete") - n.SetType(nil) - return n - } - - if len(args) != 2 { - base.Errorf("too many arguments to delete") - n.SetType(nil) - return n - } - - l := args[0] - r := args[1] - if l.Type() != nil && !l.Type().IsMap() { - base.Errorf("first argument to delete must be map; have %L", l.Type()) - n.SetType(nil) - return n - } - - args[1] = AssignConv(r, l.Type().Key(), "delete") - return n + return tcDelete(n) case ir.OAPPEND: n := n.(*ir.CallExpr) - typecheckargs(n) - args := n.Args - if len(args) == 0 { - base.Errorf("missing arguments to append") - n.SetType(nil) - return n - } - - t := args[0].Type() - if t == nil { - n.SetType(nil) - return n - } - - n.SetType(t) - if !t.IsSlice() { - if ir.IsNil(args[0]) { - base.Errorf("first argument to append must be typed slice; have untyped nil") - n.SetType(nil) - return n - } - - base.Errorf("first argument to append must be slice; have %L", t) - n.SetType(nil) - return n - } - - if n.IsDDD { - if len(args) == 1 { - base.Errorf("cannot use ... on first argument to append") - n.SetType(nil) - return n - } - - if len(args) != 2 { - base.Errorf("too many arguments to append") - n.SetType(nil) - return n - } - - if t.Elem().IsKind(types.TUINT8) && args[1].Type().IsString() { - args[1] = DefaultLit(args[1], types.Types[types.TSTRING]) - return n - } - - args[1] = AssignConv(args[1], t.Underlying(), "append") - return n - } - - as := args[1:] - for i, n := range as { - if n.Type() == nil { - continue - } - as[i] = AssignConv(n, t.Elem(), "append") - types.CheckSize(as[i].Type()) // ensure width is calculated for backend - } - return n + return tcAppend(n) case ir.OCOPY: n := n.(*ir.BinaryExpr) - n.SetType(types.Types[types.TINT]) - n.X = Expr(n.X) - n.X = DefaultLit(n.X, nil) - n.Y = Expr(n.Y) - n.Y = DefaultLit(n.Y, nil) - if n.X.Type() == nil || n.Y.Type() == nil { - n.SetType(nil) - return n - } - - // copy([]byte, string) - if n.X.Type().IsSlice() && n.Y.Type().IsString() { - if types.Identical(n.X.Type().Elem(), types.ByteType) { - return n - } - base.Errorf("arguments to copy have different element types: %L and string", n.X.Type()) - n.SetType(nil) - return n - } - - if !n.X.Type().IsSlice() || !n.Y.Type().IsSlice() { - if !n.X.Type().IsSlice() && !n.Y.Type().IsSlice() { - base.Errorf("arguments to copy must be slices; have %L, %L", n.X.Type(), n.Y.Type()) - } else if !n.X.Type().IsSlice() { - base.Errorf("first argument to copy should be slice; have %L", n.X.Type()) - } else { - base.Errorf("second argument to copy should be slice or string; have %L", n.Y.Type()) - } - n.SetType(nil) - return n - } - - if !types.Identical(n.X.Type().Elem(), n.Y.Type().Elem()) { - base.Errorf("arguments to copy have different element types: %L and %L", n.X.Type(), n.Y.Type()) - n.SetType(nil) - return n - } - return n + return tcCopy(n) case ir.OCONV: n := n.(*ir.ConvExpr) - types.CheckSize(n.Type()) // ensure width is calculated for backend - n.X = Expr(n.X) - n.X = convlit1(n.X, n.Type(), true, nil) - t := n.X.Type() - if t == nil || n.Type() == nil { - n.SetType(nil) - return n - } - op, why := convertop(n.X.Op() == ir.OLITERAL, t, n.Type()) - if op == ir.OXXX { - if !n.Diag() && !n.Type().Broke() && !n.X.Diag() { - base.Errorf("cannot convert %L to type %v%s", n.X, n.Type(), why) - n.SetDiag(true) - } - n.SetOp(ir.OCONV) - n.SetType(nil) - return n - } - - n.SetOp(op) - switch n.Op() { - case ir.OCONVNOP: - if t.Kind() == n.Type().Kind() { - switch t.Kind() { - case types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128: - // Floating point casts imply rounding and - // so the conversion must be kept. - n.SetOp(ir.OCONV) - } - } - - // do not convert to []byte literal. See CL 125796. - // generated code and compiler memory footprint is better without it. - case ir.OSTR2BYTES: - // ok - - case ir.OSTR2RUNES: - if n.X.Op() == ir.OLITERAL { - return stringtoruneslit(n) - } - } - return n + return tcConv(n) case ir.OMAKE: n := n.(*ir.CallExpr) - args := n.Args - if len(args) == 0 { - base.Errorf("missing argument to make") - n.SetType(nil) - return n - } - - n.Args.Set(nil) - l := args[0] - l = check(l, ctxType) - t := l.Type() - if t == nil { - n.SetType(nil) - return n - } - - i := 1 - var nn ir.Node - switch t.Kind() { - default: - base.Errorf("cannot make type %v", t) - n.SetType(nil) - return n - - case types.TSLICE: - if i >= len(args) { - base.Errorf("missing len argument to make(%v)", t) - n.SetType(nil) - return n - } - - l = args[i] - i++ - l = Expr(l) - var r ir.Node - if i < len(args) { - r = args[i] - i++ - r = Expr(r) - } - - if l.Type() == nil || (r != nil && r.Type() == nil) { - n.SetType(nil) - return n - } - if !checkmake(t, "len", &l) || r != nil && !checkmake(t, "cap", &r) { - n.SetType(nil) - return n - } - if ir.IsConst(l, constant.Int) && r != nil && ir.IsConst(r, constant.Int) && constant.Compare(l.Val(), token.GTR, r.Val()) { - base.Errorf("len larger than cap in make(%v)", t) - n.SetType(nil) - return n - } - nn = ir.NewMakeExpr(n.Pos(), ir.OMAKESLICE, l, r) - - case types.TMAP: - if i < len(args) { - l = args[i] - i++ - l = Expr(l) - l = DefaultLit(l, types.Types[types.TINT]) - if l.Type() == nil { - n.SetType(nil) - return n - } - if !checkmake(t, "size", &l) { - n.SetType(nil) - return n - } - } else { - l = ir.NewInt(0) - } - nn = ir.NewMakeExpr(n.Pos(), ir.OMAKEMAP, l, nil) - nn.SetEsc(n.Esc()) - - case types.TCHAN: - l = nil - if i < len(args) { - l = args[i] - i++ - l = Expr(l) - l = DefaultLit(l, types.Types[types.TINT]) - if l.Type() == nil { - n.SetType(nil) - return n - } - if !checkmake(t, "buffer", &l) { - n.SetType(nil) - return n - } - } else { - l = ir.NewInt(0) - } - nn = ir.NewMakeExpr(n.Pos(), ir.OMAKECHAN, l, nil) - } - - if i < len(args) { - base.Errorf("too many arguments to make(%v)", t) - n.SetType(nil) - return n - } - - nn.SetType(t) - return nn + return tcMake(n) case ir.ONEW: n := n.(*ir.UnaryExpr) - if n.X == nil { - // Fatalf because the OCALL above checked for us, - // so this must be an internally-generated mistake. - base.Fatalf("missing argument to new") - } - l := n.X - l = check(l, ctxType) - t := l.Type() - if t == nil { - n.SetType(nil) - return n - } - n.X = l - n.SetType(types.NewPtr(t)) - return n + return tcNew(n) case ir.OPRINT, ir.OPRINTN: n := n.(*ir.CallExpr) - typecheckargs(n) - ls := n.Args - for i1, n1 := range ls { - // Special case for print: int constant is int64, not int. - if ir.IsConst(n1, constant.Int) { - ls[i1] = DefaultLit(ls[i1], types.Types[types.TINT64]) - } else { - ls[i1] = DefaultLit(ls[i1], nil) - } - } - return n + return tcPrint(n) case ir.OPANIC: n := n.(*ir.UnaryExpr) - n.X = Expr(n.X) - n.X = DefaultLit(n.X, types.Types[types.TINTER]) - if n.X.Type() == nil { - n.SetType(nil) - return n - } - return n + return tcPanic(n) case ir.ORECOVER: n := n.(*ir.CallExpr) - if len(n.Args) != 0 { - base.Errorf("too many arguments to recover") - n.SetType(nil) - return n - } - - n.SetType(types.Types[types.TINTER]) - return n + return tcRecover(n) case ir.OCLOSURE: n := n.(*ir.ClosureExpr) - typecheckclosure(n, top) + tcClosure(n, top) if n.Type() == nil { return n } @@ -2129,17 +817,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { case ir.OITAB: n := n.(*ir.UnaryExpr) - n.X = Expr(n.X) - t := n.X.Type() - if t == nil { - n.SetType(nil) - return n - } - if !t.IsInterface() { - base.Fatalf("OITAB of %v", t) - } - n.SetType(types.NewPtr(types.Types[types.TUINTPTR])) - return n + return tcITab(n) case ir.OIDATA: // Whoever creates the OIDATA node must know a priori the concrete type at that moment, @@ -2150,21 +828,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { case ir.OSPTR: n := n.(*ir.UnaryExpr) - n.X = Expr(n.X) - t := n.X.Type() - if t == nil { - n.SetType(nil) - return n - } - if !t.IsSlice() && !t.IsString() { - base.Fatalf("OSPTR of %v", t) - } - if t.IsString() { - n.SetType(types.NewPtr(types.Types[types.TUINT8])) - } else { - n.SetType(types.NewPtr(t.Elem())) - } - return n + return tcSPtr(n) case ir.OCLOSUREREAD: return n @@ -2183,7 +847,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { // statements case ir.OAS: n := n.(*ir.AssignStmt) - typecheckas(n) + tcAssign(n) // Code that creates temps does not bother to set defn, so do it here. if n.X.Op() == ir.ONAME && ir.IsAutoTmp(n.X) { @@ -2192,7 +856,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { return n case ir.OAS2: - typecheckas2(n.(*ir.AssignListStmt)) + tcAssignList(n.(*ir.AssignListStmt)) return n case ir.OBREAK, @@ -2221,76 +885,38 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { case ir.ODEFER, ir.OGO: n := n.(*ir.GoDeferStmt) - n.Call = check(n.Call, ctxStmt|ctxExpr) + n.Call = typecheck(n.Call, ctxStmt|ctxExpr) if !n.Call.Diag() { - checkdefergo(n) + tcGoDefer(n) } return n case ir.OFOR, ir.OFORUNTIL: n := n.(*ir.ForStmt) - Stmts(n.Init()) - decldepth++ - n.Cond = Expr(n.Cond) - n.Cond = DefaultLit(n.Cond, nil) - if n.Cond != nil { - t := n.Cond.Type() - if t != nil && !t.IsBoolean() { - base.Errorf("non-bool %L used as for condition", n.Cond) - } - } - n.Post = Stmt(n.Post) - if n.Op() == ir.OFORUNTIL { - Stmts(n.Late) - } - Stmts(n.Body) - decldepth-- - return n + return tcFor(n) case ir.OIF: n := n.(*ir.IfStmt) - Stmts(n.Init()) - n.Cond = Expr(n.Cond) - n.Cond = DefaultLit(n.Cond, nil) - if n.Cond != nil { - t := n.Cond.Type() - if t != nil && !t.IsBoolean() { - base.Errorf("non-bool %L used as if condition", n.Cond) - } - } - Stmts(n.Body) - Stmts(n.Else) - return n + return tcIf(n) case ir.ORETURN: n := n.(*ir.ReturnStmt) - typecheckargs(n) - if ir.CurFunc == nil { - base.Errorf("return outside function") - n.SetType(nil) - return n - } - - if ir.HasNamedResults(ir.CurFunc) && len(n.Results) == 0 { - return n - } - typecheckaste(ir.ORETURN, nil, false, ir.CurFunc.Type().Results(), n.Results, func() string { return "return argument" }) - return n + return tcReturn(n) case ir.ORETJMP: n := n.(*ir.BranchStmt) return n case ir.OSELECT: - typecheckselect(n.(*ir.SelectStmt)) + tcSelect(n.(*ir.SelectStmt)) return n case ir.OSWITCH: - typecheckswitch(n.(*ir.SwitchStmt)) + tcSwitch(n.(*ir.SwitchStmt)) return n case ir.ORANGE: - typecheckrange(n.(*ir.RangeStmt)) + tcRange(n.(*ir.RangeStmt)) return n case ir.OTYPESW: @@ -2300,7 +926,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { return n case ir.ODCLFUNC: - typecheckfunc(n.(*ir.Func)) + tcFunc(n.(*ir.Func)) return n case ir.ODCLCONST: @@ -2310,7 +936,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { case ir.ODCLTYPE: n := n.(*ir.Decl) - n.X = check(n.X, ctxType) + n.X = typecheck(n.X, ctxType) types.CheckSize(n.X.Type()) return n } @@ -2424,59 +1050,6 @@ func checksliceconst(lo ir.Node, hi ir.Node) bool { return true } -func checkdefergo(n *ir.GoDeferStmt) { - what := "defer" - if n.Op() == ir.OGO { - what = "go" - } - - switch n.Call.Op() { - // ok - case ir.OCALLINTER, - ir.OCALLMETH, - ir.OCALLFUNC, - ir.OCLOSE, - ir.OCOPY, - ir.ODELETE, - ir.OPANIC, - ir.OPRINT, - ir.OPRINTN, - ir.ORECOVER: - return - - case ir.OAPPEND, - ir.OCAP, - ir.OCOMPLEX, - ir.OIMAG, - ir.OLEN, - ir.OMAKE, - ir.OMAKESLICE, - ir.OMAKECHAN, - ir.OMAKEMAP, - ir.ONEW, - ir.OREAL, - ir.OLITERAL: // conversion or unsafe.Alignof, Offsetof, Sizeof - if orig := ir.Orig(n.Call); orig.Op() == ir.OCONV { - break - } - base.ErrorfAt(n.Pos(), "%s discards result of %v", what, n.Call) - return - } - - // type is broken or missing, most likely a method call on a broken type - // we will warn about the broken type elsewhere. no need to emit a potentially confusing error - if n.Call.Type() == nil || n.Call.Type().Broke() { - return - } - - if !n.Diag() { - // The syntax made sure it was a call, so this must be - // a conversion. - n.SetDiag(true) - base.ErrorfAt(n.Pos(), "%s requires function call, not conversion", what) - } -} - // The result of implicitstar MUST be assigned back to n, e.g. // n.Left = implicitstar(n.Left) func implicitstar(n ir.Node) ir.Node { @@ -2687,11 +1260,11 @@ func lookdot(n *ir.SelectorExpr, t *types.Type, dostrcmp int) *types.Field { checklvalue(n.X, "call pointer method on") addr := NodAddr(n.X) addr.SetImplicit(true) - n.X = check(addr, ctxType|ctxExpr) + n.X = typecheck(addr, ctxType|ctxExpr) } else if tt.IsPtr() && (!rcvr.IsPtr() || rcvr.IsPtr() && rcvr.Elem().NotInHeap()) && types.Identical(tt.Elem(), rcvr) { star := ir.NewStarExpr(base.Pos, n.X) star.SetImplicit(true) - n.X = check(star, ctxType|ctxExpr) + n.X = typecheck(star, ctxType|ctxExpr) } else if tt.IsPtr() && tt.Elem().IsPtr() && types.Identical(derefall(tt), derefall(rcvr)) { base.Errorf("calling method %v with receiver %L requires explicit dereference", n.Sel, n.X) for tt.IsPtr() { @@ -2701,7 +1274,7 @@ func lookdot(n *ir.SelectorExpr, t *types.Type, dostrcmp int) *types.Field { } star := ir.NewStarExpr(base.Pos, n.X) star.SetImplicit(true) - n.X = check(star, ctxType|ctxExpr) + n.X = typecheck(star, ctxType|ctxExpr) tt = tt.Elem() } } else { @@ -2985,214 +1558,6 @@ func pushtype(nn ir.Node, t *types.Type) ir.Node { return n } -// The result of typecheckcomplit MUST be assigned back to n, e.g. -// n.Left = typecheckcomplit(n.Left) -func typecheckcomplit(n *ir.CompLitExpr) (res ir.Node) { - if base.EnableTrace && base.Flag.LowerT { - defer tracePrint("typecheckcomplit", n)(&res) - } - - lno := base.Pos - defer func() { - base.Pos = lno - }() - - if n.Ntype == nil { - base.ErrorfAt(n.Pos(), "missing type in composite literal") - n.SetType(nil) - return n - } - - // Save original node (including n.Right) - n.SetOrig(ir.Copy(n)) - - ir.SetPos(n.Ntype) - - // Need to handle [...]T arrays specially. - if array, ok := n.Ntype.(*ir.ArrayType); ok && array.Elem != nil && array.Len == nil { - array.Elem = check(array.Elem, ctxType) - elemType := array.Elem.Type() - if elemType == nil { - n.SetType(nil) - return n - } - length := typecheckarraylit(elemType, -1, n.List, "array literal") - n.SetOp(ir.OARRAYLIT) - n.SetType(types.NewArray(elemType, length)) - n.Ntype = nil - return n - } - - n.Ntype = ir.Node(check(n.Ntype, ctxType)).(ir.Ntype) - t := n.Ntype.Type() - if t == nil { - n.SetType(nil) - return n - } - n.SetType(t) - - switch t.Kind() { - default: - base.Errorf("invalid composite literal type %v", t) - n.SetType(nil) - - case types.TARRAY: - typecheckarraylit(t.Elem(), t.NumElem(), n.List, "array literal") - n.SetOp(ir.OARRAYLIT) - n.Ntype = nil - - case types.TSLICE: - length := typecheckarraylit(t.Elem(), -1, n.List, "slice literal") - n.SetOp(ir.OSLICELIT) - n.Ntype = nil - n.Len = length - - case types.TMAP: - var cs constSet - for i3, l := range n.List { - ir.SetPos(l) - if l.Op() != ir.OKEY { - n.List[i3] = Expr(l) - base.Errorf("missing key in map literal") - continue - } - l := l.(*ir.KeyExpr) - - r := l.Key - r = pushtype(r, t.Key()) - r = Expr(r) - l.Key = AssignConv(r, t.Key(), "map key") - cs.add(base.Pos, l.Key, "key", "map literal") - - r = l.Value - r = pushtype(r, t.Elem()) - r = Expr(r) - l.Value = AssignConv(r, t.Elem(), "map value") - } - - n.SetOp(ir.OMAPLIT) - n.Ntype = nil - - case types.TSTRUCT: - // Need valid field offsets for Xoffset below. - types.CalcSize(t) - - errored := false - if len(n.List) != 0 && nokeys(n.List) { - // simple list of variables - ls := n.List - for i, n1 := range ls { - ir.SetPos(n1) - n1 = Expr(n1) - ls[i] = n1 - if i >= t.NumFields() { - if !errored { - base.Errorf("too many values in %v", n) - errored = true - } - continue - } - - f := t.Field(i) - s := f.Sym - if s != nil && !types.IsExported(s.Name) && s.Pkg != types.LocalPkg { - base.Errorf("implicit assignment of unexported field '%s' in %v literal", s.Name, t) - } - // No pushtype allowed here. Must name fields for that. - n1 = AssignConv(n1, f.Type, "field value") - sk := ir.NewStructKeyExpr(base.Pos, f.Sym, n1) - sk.Offset = f.Offset - ls[i] = sk - } - if len(ls) < t.NumFields() { - base.Errorf("too few values in %v", n) - } - } else { - hash := make(map[string]bool) - - // keyed list - ls := n.List - for i, l := range ls { - ir.SetPos(l) - - if l.Op() == ir.OKEY { - kv := l.(*ir.KeyExpr) - key := kv.Key - - // Sym might have resolved to name in other top-level - // package, because of import dot. Redirect to correct sym - // before we do the lookup. - s := key.Sym() - if id, ok := key.(*ir.Ident); ok && DotImportRefs[id] != nil { - s = Lookup(s.Name) - } - - // An OXDOT uses the Sym field to hold - // the field to the right of the dot, - // so s will be non-nil, but an OXDOT - // is never a valid struct literal key. - if s == nil || s.Pkg != types.LocalPkg || key.Op() == ir.OXDOT || s.IsBlank() { - base.Errorf("invalid field name %v in struct initializer", key) - continue - } - - l = ir.NewStructKeyExpr(l.Pos(), s, kv.Value) - ls[i] = l - } - - if l.Op() != ir.OSTRUCTKEY { - if !errored { - base.Errorf("mixture of field:value and value initializers") - errored = true - } - ls[i] = Expr(ls[i]) - continue - } - l := l.(*ir.StructKeyExpr) - - f := lookdot1(nil, l.Field, t, t.Fields(), 0) - if f == nil { - if ci := lookdot1(nil, l.Field, t, t.Fields(), 2); ci != nil { // Case-insensitive lookup. - if visible(ci.Sym) { - base.Errorf("unknown field '%v' in struct literal of type %v (but does have %v)", l.Field, t, ci.Sym) - } else if nonexported(l.Field) && l.Field.Name == ci.Sym.Name { // Ensure exactness before the suggestion. - base.Errorf("cannot refer to unexported field '%v' in struct literal of type %v", l.Field, t) - } else { - base.Errorf("unknown field '%v' in struct literal of type %v", l.Field, t) - } - continue - } - var f *types.Field - p, _ := dotpath(l.Field, t, &f, true) - if p == nil || f.IsMethod() { - base.Errorf("unknown field '%v' in struct literal of type %v", l.Field, t) - continue - } - // dotpath returns the parent embedded types in reverse order. - var ep []string - for ei := len(p) - 1; ei >= 0; ei-- { - ep = append(ep, p[ei].field.Sym.Name) - } - ep = append(ep, l.Field.Name) - base.Errorf("cannot use promoted field %v in struct literal of type %v", strings.Join(ep, "."), t) - continue - } - fielddup(f.Sym.Name, hash) - l.Offset = f.Offset - - // No pushtype allowed here. Tried and rejected. - l.Value = Expr(l.Value) - l.Value = AssignConv(l.Value, f.Type, "field value") - } - } - - n.SetOp(ir.OSTRUCTLIT) - n.Ntype = nil - } - - return n -} - // typecheckarraylit type-checks a sequence of slice/array literal elements. func typecheckarraylit(elemType *types.Type, bound int64, elts []ir.Node, ctx string) int64 { // If there are key/value pairs, create a map to keep seen @@ -3324,60 +1689,6 @@ func checkassignlist(stmt ir.Node, l ir.Nodes) { } } -// type check assignment. -// if this assignment is the definition of a var on the left side, -// fill in the var's type. -func typecheckas(n *ir.AssignStmt) { - if base.EnableTrace && base.Flag.LowerT { - defer tracePrint("typecheckas", n)(nil) - } - - // delicate little dance. - // the definition of n may refer to this assignment - // as its definition, in which case it will call typecheckas. - // in that case, do not call typecheck back, or it will cycle. - // if the variable has a type (ntype) then typechecking - // will not look at defn, so it is okay (and desirable, - // so that the conversion below happens). - n.X = Resolve(n.X) - - if !ir.DeclaredBy(n.X, n) || n.X.Name().Ntype != nil { - n.X = AssignExpr(n.X) - } - - // Use ctxMultiOK so we can emit an "N variables but M values" error - // to be consistent with typecheckas2 (#26616). - n.Y = check(n.Y, ctxExpr|ctxMultiOK) - checkassign(n, n.X) - if n.Y != nil && n.Y.Type() != nil { - if n.Y.Type().IsFuncArgStruct() { - base.Errorf("assignment mismatch: 1 variable but %v returns %d values", n.Y.(*ir.CallExpr).X, n.Y.Type().NumFields()) - // Multi-value RHS isn't actually valid for OAS; nil out - // to indicate failed typechecking. - n.Y.SetType(nil) - } else if n.X.Type() != nil { - n.Y = AssignConv(n.Y, n.X.Type(), "assignment") - } - } - - if ir.DeclaredBy(n.X, n) && n.X.Name().Ntype == nil { - n.Y = DefaultLit(n.Y, nil) - n.X.SetType(n.Y.Type()) - } - - // second half of dance. - // now that right is done, typecheck the left - // just to get it over with. see dance above. - n.SetTypecheck(1) - - if n.X.Typecheck() == 0 { - n.X = AssignExpr(n.X) - } - if !ir.IsBlank(n.X) { - types.CheckSize(n.X.Type()) // ensure width is calculated for backend - } -} - func checkassignto(src *types.Type, dst ir.Node) { if op, why := assignop(src, dst.Type()); op == ir.OXXX { base.Errorf("cannot assign %v to %L in multiple assignment%s", src, dst, why) @@ -3385,173 +1696,6 @@ func checkassignto(src *types.Type, dst ir.Node) { } } -func typecheckas2(n *ir.AssignListStmt) { - if base.EnableTrace && base.Flag.LowerT { - defer tracePrint("typecheckas2", n)(nil) - } - - ls := n.Lhs - for i1, n1 := range ls { - // delicate little dance. - n1 = Resolve(n1) - ls[i1] = n1 - - if !ir.DeclaredBy(n1, n) || n1.Name().Ntype != nil { - ls[i1] = AssignExpr(ls[i1]) - } - } - - cl := len(n.Lhs) - cr := len(n.Rhs) - if cl > 1 && cr == 1 { - n.Rhs[0] = check(n.Rhs[0], ctxExpr|ctxMultiOK) - } else { - Exprs(n.Rhs) - } - checkassignlist(n, n.Lhs) - - var l ir.Node - var r ir.Node - if cl == cr { - // easy - ls := n.Lhs - rs := n.Rhs - for il, nl := range ls { - nr := rs[il] - if nl.Type() != nil && nr.Type() != nil { - rs[il] = AssignConv(nr, nl.Type(), "assignment") - } - if ir.DeclaredBy(nl, n) && nl.Name().Ntype == nil { - rs[il] = DefaultLit(rs[il], nil) - nl.SetType(rs[il].Type()) - } - } - - goto out - } - - l = n.Lhs[0] - r = n.Rhs[0] - - // x,y,z = f() - if cr == 1 { - if r.Type() == nil { - goto out - } - switch r.Op() { - case ir.OCALLMETH, ir.OCALLINTER, ir.OCALLFUNC: - if !r.Type().IsFuncArgStruct() { - break - } - cr = r.Type().NumFields() - if cr != cl { - goto mismatch - } - r.(*ir.CallExpr).Use = ir.CallUseList - n.SetOp(ir.OAS2FUNC) - for i, l := range n.Lhs { - f := r.Type().Field(i) - if f.Type != nil && l.Type() != nil { - checkassignto(f.Type, l) - } - if ir.DeclaredBy(l, n) && l.Name().Ntype == nil { - l.SetType(f.Type) - } - } - goto out - } - } - - // x, ok = y - if cl == 2 && cr == 1 { - if r.Type() == nil { - goto out - } - switch r.Op() { - case ir.OINDEXMAP, ir.ORECV, ir.ODOTTYPE: - switch r.Op() { - case ir.OINDEXMAP: - n.SetOp(ir.OAS2MAPR) - case ir.ORECV: - n.SetOp(ir.OAS2RECV) - case ir.ODOTTYPE: - r := r.(*ir.TypeAssertExpr) - n.SetOp(ir.OAS2DOTTYPE) - r.SetOp(ir.ODOTTYPE2) - } - if l.Type() != nil { - checkassignto(r.Type(), l) - } - if ir.DeclaredBy(l, n) { - l.SetType(r.Type()) - } - l := n.Lhs[1] - if l.Type() != nil && !l.Type().IsBoolean() { - checkassignto(types.Types[types.TBOOL], l) - } - if ir.DeclaredBy(l, n) && l.Name().Ntype == nil { - l.SetType(types.Types[types.TBOOL]) - } - goto out - } - } - -mismatch: - switch r.Op() { - default: - base.Errorf("assignment mismatch: %d variables but %d values", cl, cr) - case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER: - r := r.(*ir.CallExpr) - base.Errorf("assignment mismatch: %d variables but %v returns %d values", cl, r.X, cr) - } - - // second half of dance -out: - n.SetTypecheck(1) - ls = n.Lhs - for i1, n1 := range ls { - if n1.Typecheck() == 0 { - ls[i1] = AssignExpr(ls[i1]) - } - } -} - -// type check function definition -// To be called by typecheck, not directly. -// (Call typecheckFunc instead.) -func typecheckfunc(n *ir.Func) { - if base.EnableTrace && base.Flag.LowerT { - defer tracePrint("typecheckfunc", n)(nil) - } - - for _, ln := range n.Dcl { - if ln.Op() == ir.ONAME && (ln.Class_ == ir.PPARAM || ln.Class_ == ir.PPARAMOUT) { - ln.Decldepth = 1 - } - } - - n.Nname = AssignExpr(n.Nname).(*ir.Name) - t := n.Nname.Type() - if t == nil { - return - } - n.SetType(t) - rcvr := t.Recv() - if rcvr != nil && n.Shortname != nil { - m := addmethod(n, n.Shortname, t, true, n.Pragma&ir.Nointerface != 0) - if m == nil { - return - } - - n.Nname.SetSym(ir.MethodSym(rcvr.Type, n.Shortname)) - Declare(n.Nname, ir.PFUNC) - } - - if base.Ctxt.Flag_dynlink && !inimport && n.Nname != nil { - NeedFuncSym(n.Sym()) - } -} - // The result of stringtoruneslit MUST be assigned back to n, e.g. // n.Left = stringtoruneslit(n.Left) func stringtoruneslit(n *ir.ConvExpr) ir.Node {