From ef5964dd6b092f7e0d9bd4332a5d258eb80ecef8 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 4 Dec 2020 11:37:54 -0500 Subject: [PATCH] [dev.regabi] cmd/compile: arrange for typecheck1 to end in switch Ending typecheck1 in the switch makes it safe for each case to do an appropriate type assertion. The main change is dropping the computation of "ok" and using the syntax nodes themselves to decide what's OK. Passes buildall w/ toolstash -cmp. Change-Id: I2a1873a51e3f1194d74bb87a6653cb9857a02a1b Reviewed-on: https://go-review.googlesource.com/c/go/+/275444 Trust: Russ Cox Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/typecheck.go | 336 +++++++++++------------ src/cmd/compile/internal/ir/expr.go | 12 +- src/cmd/compile/internal/ir/func.go | 2 + src/cmd/compile/internal/ir/name.go | 2 + src/cmd/compile/internal/ir/stmt.go | 11 + test/used.go | 7 +- 6 files changed, 195 insertions(+), 175 deletions(-) diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index c22786f148..dc9e23069e 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -307,17 +307,91 @@ func typecheck(n ir.Node, top int) (res ir.Node) { return n } - n.SetTypecheck(2) - typecheck_tcstack = append(typecheck_tcstack, n) - n = typecheck1(n, top) + n.SetTypecheck(2) + n = typecheck1(n, top) n.SetTypecheck(1) last := len(typecheck_tcstack) - 1 typecheck_tcstack[last] = nil typecheck_tcstack = typecheck_tcstack[:last] + _, isExpr := n.(ir.Expr) + _, isStmt := n.(ir.Stmt) + isMulti := false + switch n.Op() { + case ir.OCALLFUNC, ir.OCALLINTER, ir.OCALLMETH: + if t := n.Left().Type(); t != nil && t.Kind() == types.TFUNC { + nr := t.NumResults() + isMulti = nr > 1 + if nr == 0 { + isExpr = false + } + } + case ir.OAPPEND: + // Must be used (and not BinaryExpr/UnaryExpr). + isStmt = false + case ir.OCLOSE, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN, ir.OVARKILL, ir.OVARLIVE: + // Must not be used. + isExpr = false + isStmt = true + case ir.OCOPY, ir.ORECOVER, ir.ORECV: + // Can be used or not. + isStmt = true + } + + t := n.Type() + if t != nil && !t.IsFuncArgStruct() && n.Op() != ir.OTYPE { + switch t.Kind() { + case types.TFUNC, // might have TANY; wait until it's called + types.TANY, types.TFORW, types.TIDEAL, types.TNIL, types.TBLANK: + break + + default: + checkwidth(t) + } + } + if t != nil { + n = evalConst(n) + t = n.Type() + } + + // TODO(rsc): Lots of the complexity here is because typecheck can + // see OTYPE, ONAME, and OLITERAL nodes multiple times. + // Once we make the IR a proper tree, we should be able to simplify + // this code a bit, especially the final case. + switch { + case top&(ctxStmt|ctxExpr) == ctxExpr && !isExpr && n.Op() != ir.OTYPE && !isMulti: + if !n.Diag() { + base.Errorf("%v used as value", n) + n.SetDiag(true) + } + if t != nil { + n.SetType(nil) + } + + case top&ctxType == 0 && n.Op() == ir.OTYPE && t != nil: + if !n.Type().Broke() { + base.Errorf("type %v is not an expression", n.Type()) + } + n.SetType(nil) + + case top&(ctxStmt|ctxExpr) == ctxStmt && !isStmt && t != nil: + if !n.Diag() { + base.Errorf("%v evaluated but not used", n) + n.SetDiag(true) + } + n.SetType(nil) + + case top&(ctxType|ctxExpr) == ctxType && n.Op() != ir.OTYPE && n.Op() != ir.ONONAME && (t != nil || n.Op() == ir.ONAME): + base.Errorf("%v is not a type", n) + if t != nil { + n.SetType(nil) + } + + } + base.Pos = lno return n } @@ -335,8 +409,7 @@ func indexlit(n ir.Node) ir.Node { return n } -// The result of typecheck1 MUST be assigned back to n, e.g. -// n.Left = typecheck1(n.Left, top) +// typecheck1 should ONLY be called from typecheck. func typecheck1(n ir.Node, top int) (res ir.Node) { if enableTrace && base.Flag.LowerT { defer tracePrint("typecheck1", n)(&res) @@ -345,7 +418,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { switch n.Op() { case ir.OLITERAL, ir.ONAME, ir.ONONAME, ir.OTYPE: if n.Sym() == nil { - break + return n } if n.Op() == ir.ONAME && n.SubOp() != 0 && top&ctxCallee == 0 { @@ -361,34 +434,29 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } } - ok := 0 switch n.Op() { - // until typecheck is complete, do nothing. default: ir.Dump("typecheck", n) - base.Fatalf("typecheck %v", n.Op()) + panic("unreachable") // names case ir.OLITERAL: - ok |= ctxExpr - if n.Type() == nil && n.Val().Kind() == constant.String { base.Fatalf("string literal missing type") } + return n case ir.ONIL, ir.ONONAME: - ok |= ctxExpr + return n case ir.ONAME: if n.Name().Decldepth == 0 { n.Name().Decldepth = decldepth } if n.SubOp() != 0 { - ok |= ctxCallee - break + return n } - if top&ctxAssign == 0 { // not a write to the variable if ir.IsBlank(n) { @@ -396,11 +464,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { n.SetType(nil) return n } - n.Name().SetUsed(true) } - - ok |= ctxExpr + return n case ir.OPACK: base.Errorf("use of package %v without selector", n.Sym()) @@ -409,14 +475,12 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { // types (ODEREF is with exprs) case ir.OTYPE: - ok |= ctxType - if n.Type() == nil { return n } + return n case ir.OTSLICE: - ok |= ctxType n := n.(*ir.SliceType) n.Elem = typecheck(n.Elem, ctxType) if n.Elem.Type() == nil { @@ -425,9 +489,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { t := types.NewSlice(n.Elem.Type()) n.SetOTYPE(t) checkwidth(t) + return n case ir.OTARRAY: - ok |= ctxType n := n.(*ir.ArrayType) n.Elem = typecheck(n.Elem, ctxType) if n.Elem.Type() == nil { @@ -469,9 +533,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { t := types.NewArray(n.Elem.Type(), bound) n.SetOTYPE(t) checkwidth(t) + return n case ir.OTMAP: - ok |= ctxType n := n.(*ir.MapType) n.Key = typecheck(n.Key, ctxType) n.Elem = typecheck(n.Elem, ctxType) @@ -488,9 +552,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } n.SetOTYPE(types.NewMap(l.Type(), r.Type())) mapqueue = append(mapqueue, n) // check map keys when all types are settled + return n case ir.OTCHAN: - ok |= ctxType n := n.(*ir.ChanType) n.Elem = typecheck(n.Elem, ctxType) l := n.Elem @@ -501,21 +565,22 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { base.Errorf("chan of incomplete (or unallocatable) type not allowed") } n.SetOTYPE(types.NewChan(l.Type(), n.Dir)) + return n case ir.OTSTRUCT: - ok |= ctxType n := n.(*ir.StructType) n.SetOTYPE(tostruct(n.Fields)) + return n case ir.OTINTER: - ok |= ctxType n := n.(*ir.InterfaceType) n.SetOTYPE(tointerface(n.Methods)) + return n case ir.OTFUNC: - ok |= ctxType n := n.(*ir.FuncType) n.SetOTYPE(functype(n.Recv, n.Params, n.Results)) + return n // type or expr case ir.ODEREF: @@ -528,11 +593,10 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { return n } if l.Op() == ir.OTYPE { - ok |= ctxType n.SetOTYPE(types.NewPtr(l.Type())) // Ensure l.Type gets dowidth'd for the backend. Issue 20174. checkwidth(l.Type()) - break + return n } if !t.IsPtr() { @@ -541,12 +605,12 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { n.SetType(nil) return n } - - break + base.Errorf("%v is not a type", l) + return n } - ok |= ctxExpr n.SetType(t.Elem()) + return n // arithmetic exprs case ir.OASOP, @@ -573,7 +637,6 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { var op ir.Op var r ir.Node if n.Op() == ir.OASOP { - ok |= ctxStmt n.SetLeft(typecheck(n.Left(), ctxExpr)) n.SetRight(typecheck(n.Right(), ctxExpr)) l = n.Left() @@ -591,7 +654,6 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { // TODO(marvin): Fix Node.EType type union. op = n.SubOp() } else { - ok |= ctxExpr n.SetLeft(typecheck(n.Left(), ctxExpr)) n.SetRight(typecheck(n.Right(), ctxExpr)) l = n.Left() @@ -629,8 +691,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { if (l.Type() == types.UntypedFloat || l.Type() == types.UntypedComplex) && r.Op() == ir.OLITERAL { n.SetType(types.UntypedInt) } - - break + return n } // For "x == x && len(s)", it's better to report that "len(s)" (type int) @@ -815,9 +876,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } n.SetType(t) + return n case ir.OBITNOT, ir.ONEG, ir.ONOT, ir.OPLUS: - ok |= ctxExpr n.SetLeft(typecheck(n.Left(), ctxExpr)) l := n.Left() t := l.Type() @@ -832,11 +893,10 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } n.SetType(t) + return n // exprs case ir.OADDR: - ok |= ctxExpr - n.SetLeft(typecheck(n.Left(), ctxExpr)) if n.Left().Type() == nil { n.SetType(nil) @@ -871,13 +931,10 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } n.SetType(types.NewPtr(n.Left().Type())) + return n case ir.OCOMPLIT: - ok |= ctxExpr - n = typecheckcomplit(n) - if n.Type() == nil { - return n - } + return typecheckcomplit(n) case ir.OXDOT, ir.ODOT: if n.Op() == ir.OXDOT { @@ -903,12 +960,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { s := n.Sym() if n.Left().Op() == ir.OTYPE { - n = typecheckMethodExpr(n) - if n.Type() == nil { - return n - } - ok = ctxExpr - break + return typecheckMethodExpr(n) } if t.IsPtr() && !t.Elem().IsInterface() { @@ -952,21 +1004,12 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { return n } - switch n.Op() { - case ir.ODOTINTER, ir.ODOTMETH: - if top&ctxCallee != 0 { - ok |= ctxCallee - } else { - n = typecheckpartialcall(n, s) - ok |= ctxExpr - } - - default: - ok |= ctxExpr + if (n.Op() == ir.ODOTINTER || n.Op() == ir.ODOTMETH) && top&ctxCallee == 0 { + n = typecheckpartialcall(n, s) } + return n case ir.ODOTTYPE: - ok |= ctxExpr n.SetLeft(typecheck(n.Left(), ctxExpr)) n.SetLeft(defaultlit(n.Left(), nil)) l := n.Left() @@ -1009,9 +1052,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { return n } } + return n case ir.OINDEX: - ok |= ctxExpr n.SetLeft(typecheck(n.Left(), ctxExpr)) n.SetLeft(defaultlit(n.Left(), nil)) n.SetLeft(implicitstar(n.Left())) @@ -1045,7 +1088,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { if n.Right().Type() != nil && !n.Right().Type().IsInteger() { base.Errorf("non-integer %s index %v", why, n.Right()) - break + return n } if !n.Bounded() && ir.IsConst(n.Right(), constant.Int) { @@ -1067,9 +1110,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { n.SetOp(ir.OINDEXMAP) n.SetIndexMapLValue(false) } + return n case ir.ORECV: - ok |= ctxStmt | ctxExpr n.SetLeft(typecheck(n.Left(), ctxExpr)) n.SetLeft(defaultlit(n.Left(), nil)) l := n.Left() @@ -1091,9 +1134,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } n.SetType(t.Elem()) + return n case ir.OSEND: - ok |= ctxStmt n.SetLeft(typecheck(n.Left(), ctxExpr)) n.SetRight(typecheck(n.Right(), ctxExpr)) n.SetLeft(defaultlit(n.Left(), nil)) @@ -1115,14 +1158,13 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { if n.Right().Type() == nil { return n } + return 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. - ok |= ctxExpr - t := n.Type() if t == nil { base.Fatalf("no type specified for OSLICEHEADER") @@ -1160,14 +1202,13 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { n.List().SetFirst(l) n.List().SetSecond(c) + return 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. - ok |= ctxExpr - t := n.Type() if t == nil { @@ -1203,9 +1244,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { base.Fatalf("len for OMAKESLICECOPY must be non-negative") } } + return n case ir.OSLICE, ir.OSLICE3: - ok |= ctxExpr n.SetLeft(typecheck(n.Left(), ctxExpr)) low, high, max := n.SliceBounds() hasmax := n.Op().IsSlice3() @@ -1277,6 +1318,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { n.SetType(nil) return n } + return n // call and call like case ir.OCALL: @@ -1306,6 +1348,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { case ir.OAPPEND, ir.ODELETE, ir.OMAKE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER: n.SetOp(l.SubOp()) n.SetLeft(nil) + n.SetTypecheck(0) // re-typechecking new op is OK, not a loop case ir.OCAP, ir.OCLOSE, ir.OIMAG, ir.OLEN, ir.OPANIC, ir.OREAL: typecheckargs(n) @@ -1331,8 +1374,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { n = ir.NodAt(n.Pos(), l.SubOp(), arg1, arg2) n = initExpr(old.Init().Slice(), n) // typecheckargs can add to old.Init } - n = typecheck1(n, top) - return n + return typecheck(n, top) } n.SetLeft(defaultlit(n.Left(), nil)) @@ -1346,8 +1388,6 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } // pick off before type-checking arguments - ok |= ctxExpr - arg, ok := needOneArg(n, "conversion to %v", l.Type()) if !ok { n.SetType(nil) @@ -1356,8 +1396,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { n = ir.NodAt(n.Pos(), ir.OCONV, arg, nil) n.SetType(l.Type()) - n = typecheck1(n, top) - return n + return typecheck1(n, top) } typecheckargs(n) @@ -1403,11 +1442,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } typecheckaste(ir.OCALL, n.Left(), n.IsDDD(), t.Params(), n.List(), func() string { return fmt.Sprintf("argument to %v", n.Left()) }) - ok |= ctxStmt if t.NumResults() == 0 { - break + return n } - ok |= ctxExpr if t.NumResults() == 1 { n.SetType(l.Type().Results().Field(0).Type) @@ -1420,24 +1457,23 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { // and we want to avoid the temporaries, so we do the rewrite earlier than is typical. n.SetOp(ir.OGETG) } - - break + return n } // multiple return if top&(ctxMultiOK|ctxStmt) == 0 { base.Errorf("multiple-value %v() in single-value context", l) - break + return n } n.SetType(l.Type().Results()) + return n case ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: - ok |= ctxExpr n.SetType(types.Types[types.TUINTPTR]) + return n case ir.OCAP, ir.OLEN: - ok |= ctxExpr n.SetLeft(typecheck(n.Left(), ctxExpr)) n.SetLeft(defaultlit(n.Left(), nil)) n.SetLeft(implicitstar(n.Left())) @@ -1461,9 +1497,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } n.SetType(types.Types[types.TINT]) + return n case ir.OREAL, ir.OIMAG: - ok |= ctxExpr n.SetLeft(typecheck(n.Left(), ctxExpr)) l := n.Left() t := l.Type() @@ -1485,9 +1521,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { n.SetType(nil) return n } + return n case ir.OCOMPLEX: - ok |= ctxExpr l := typecheck(n.Left(), ctxExpr) r := typecheck(n.Right(), ctxExpr) if l.Type() == nil || r.Type() == nil { @@ -1525,6 +1561,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { t = types.Types[types.TCOMPLEX128] } n.SetType(t) + return n case ir.OCLOSE: n.SetLeft(typecheck(n.Left(), ctxExpr)) @@ -1546,11 +1583,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { n.SetType(nil) return n } - - ok |= ctxStmt + return n case ir.ODELETE: - ok |= ctxStmt typecheckargs(n) args := n.List() if args.Len() == 0 { @@ -1580,9 +1615,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } args.SetSecond(assignconv(r, l.Type().Key(), "delete")) + return n case ir.OAPPEND: - ok |= ctxExpr typecheckargs(n) args := n.List() if args.Len() == 0 { @@ -1625,11 +1660,11 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { if t.Elem().IsKind(types.TUINT8) && args.Second().Type().IsString() { args.SetSecond(defaultlit(args.Second(), types.Types[types.TSTRING])) - break + return n } args.SetSecond(assignconv(args.Second(), t.Underlying(), "append")) - break + return n } as := args.Slice()[1:] @@ -1640,9 +1675,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { as[i] = assignconv(n, t.Elem(), "append") checkwidth(as[i].Type()) // ensure width is calculated for backend } + return n case ir.OCOPY: - ok |= ctxStmt | ctxExpr n.SetType(types.Types[types.TINT]) n.SetLeft(typecheck(n.Left(), ctxExpr)) n.SetLeft(defaultlit(n.Left(), nil)) @@ -1656,7 +1691,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { // copy([]byte, string) if n.Left().Type().IsSlice() && n.Right().Type().IsString() { if types.Identical(n.Left().Type().Elem(), types.ByteType) { - break + return n } base.Errorf("arguments to copy have different element types: %L and string", n.Left().Type()) n.SetType(nil) @@ -1680,9 +1715,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { n.SetType(nil) return n } + return n case ir.OCONV: - ok |= ctxExpr checkwidth(n.Type()) // ensure width is calculated for backend n.SetLeft(typecheck(n.Left(), ctxExpr)) n.SetLeft(convlit1(n.Left(), n.Type(), true, nil)) @@ -1717,16 +1752,16 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { // do not convert to []byte literal. See CL 125796. // generated code and compiler memory footprint is better without it. case ir.OSTR2BYTES: - break + // ok case ir.OSTR2RUNES: if n.Left().Op() == ir.OLITERAL { n = stringtoruneslit(n) } } + return n case ir.OMAKE: - ok |= ctxExpr args := n.List().Slice() if len(args) == 0 { base.Errorf("missing argument to make") @@ -1832,9 +1867,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { nn.SetType(t) n = nn + return n case ir.ONEW: - ok |= ctxExpr if n.Left() == nil { // Fatalf because the OCALL above checked for us, // so this must be an internally-generated mistake. @@ -1849,9 +1884,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } n.SetLeft(l) n.SetType(types.NewPtr(t)) + return n case ir.OPRINT, ir.OPRINTN: - ok |= ctxStmt typecheckargs(n) ls := n.List().Slice() for i1, n1 := range ls { @@ -1862,18 +1897,18 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { ls[i1] = defaultlit(ls[i1], nil) } } + return n case ir.OPANIC: - ok |= ctxStmt n.SetLeft(typecheck(n.Left(), ctxExpr)) n.SetLeft(defaultlit(n.Left(), types.Types[types.TINTER])) if n.Left().Type() == nil { n.SetType(nil) return n } + return n case ir.ORECOVER: - ok |= ctxExpr | ctxStmt if n.List().Len() != 0 { base.Errorf("too many arguments to recover") n.SetType(nil) @@ -1881,16 +1916,16 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } n.SetType(types.Types[types.TINTER]) + return n case ir.OCLOSURE: - ok |= ctxExpr typecheckclosure(n, top) if n.Type() == nil { return n } + return n case ir.OITAB: - ok |= ctxExpr n.SetLeft(typecheck(n.Left(), ctxExpr)) t := n.Left().Type() if t == nil { @@ -1901,14 +1936,15 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { base.Fatalf("OITAB of %v", t) } n.SetType(types.NewPtr(types.Types[types.TUINTPTR])) + return n case ir.OIDATA: // Whoever creates the OIDATA node must know a priori the concrete type at that moment, // usually by just having checked the OITAB. base.Fatalf("cannot typecheck interface data %v", n) + panic("unreachable") case ir.OSPTR: - ok |= ctxExpr n.SetLeft(typecheck(n.Left(), ctxExpr)) t := n.Left().Type() if t == nil { @@ -1923,33 +1959,33 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } else { n.SetType(types.NewPtr(t.Elem())) } + return n case ir.OCLOSUREREAD: - ok |= ctxExpr + return n case ir.OCFUNC: - ok |= ctxExpr n.SetLeft(typecheck(n.Left(), ctxExpr)) n.SetType(types.Types[types.TUINTPTR]) + return n case ir.OCONVNOP: - ok |= ctxExpr n.SetLeft(typecheck(n.Left(), ctxExpr)) + return n // statements case ir.OAS: - ok |= ctxStmt - typecheckas(n) // Code that creates temps does not bother to set defn, so do it here. if n.Left().Op() == ir.ONAME && ir.IsAutoTmp(n.Left()) { n.Left().Name().Defn = n } + return n case ir.OAS2: - ok |= ctxStmt typecheckas2(n) + return n case ir.OBREAK, ir.OCONTINUE, @@ -1958,14 +1994,13 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { ir.OFALL, ir.OVARKILL, ir.OVARLIVE: - ok |= ctxStmt + return n case ir.OBLOCK: - ok |= ctxStmt typecheckslice(n.List().Slice(), ctxStmt) + return n case ir.OLABEL: - ok |= ctxStmt decldepth++ if n.Sym().IsBlank() { // Empty identifier is valid but useless. @@ -1973,21 +2008,21 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { // See issues 7538, 11589, 11593. n = ir.NodAt(n.Pos(), ir.OBLOCK, nil, nil) } + return n case ir.ODEFER: - ok |= ctxStmt n.SetLeft(typecheck(n.Left(), ctxStmt|ctxExpr)) if !n.Left().Diag() { checkdefergo(n) } + return n case ir.OGO: - ok |= ctxStmt n.SetLeft(typecheck(n.Left(), ctxStmt|ctxExpr)) checkdefergo(n) + return n case ir.OFOR, ir.OFORUNTIL: - ok |= ctxStmt typecheckslice(n.Init().Slice(), ctxStmt) decldepth++ n.SetLeft(typecheck(n.Left(), ctxExpr)) @@ -2004,9 +2039,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } typecheckslice(n.Body().Slice(), ctxStmt) decldepth-- + return n case ir.OIF: - ok |= ctxStmt typecheckslice(n.Init().Slice(), ctxStmt) n.SetLeft(typecheck(n.Left(), ctxExpr)) n.SetLeft(defaultlit(n.Left(), nil)) @@ -2018,9 +2053,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } typecheckslice(n.Body().Slice(), ctxStmt) typecheckslice(n.Rlist().Slice(), ctxStmt) + return n case ir.ORETURN: - ok |= ctxStmt typecheckargs(n) if Curfn == nil { base.Errorf("return outside function") @@ -2029,24 +2064,25 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } if hasNamedResults(Curfn) && n.List().Len() == 0 { - break + return n } typecheckaste(ir.ORETURN, nil, false, Curfn.Type().Results(), n.List(), func() string { return "return argument" }) + return n case ir.ORETJMP: - ok |= ctxStmt + return n case ir.OSELECT: - ok |= ctxStmt typecheckselect(n) + return n case ir.OSWITCH: - ok |= ctxStmt typecheckswitch(n) + return n case ir.ORANGE: - ok |= ctxStmt typecheckrange(n) + return n case ir.OTYPESW: base.Errorf("use of .(type) outside type switch") @@ -2054,64 +2090,22 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { return n case ir.ODCLFUNC: - ok |= ctxStmt typecheckfunc(n.(*ir.Func)) + return n case ir.ODCLCONST: - ok |= ctxStmt n.SetLeft(typecheck(n.Left(), ctxExpr)) + return n case ir.ODCLTYPE: - ok |= ctxStmt n.SetLeft(typecheck(n.Left(), ctxType)) checkwidth(n.Left().Type()) - } - - t := n.Type() - if t != nil && !t.IsFuncArgStruct() && n.Op() != ir.OTYPE { - switch t.Kind() { - case types.TFUNC, // might have TANY; wait until it's called - types.TANY, types.TFORW, types.TIDEAL, types.TNIL, types.TBLANK: - break - - default: - checkwidth(t) - } - } - - n = evalConst(n) - if n.Op() == ir.OTYPE && top&ctxType == 0 { - if !n.Type().Broke() { - base.Errorf("type %v is not an expression", n.Type()) - } - n.SetType(nil) return n } - if top&(ctxExpr|ctxType) == ctxType && n.Op() != ir.OTYPE { - base.Errorf("%v is not a type", n) - n.SetType(nil) - return n - } - - // TODO(rsc): simplify - if (top&(ctxCallee|ctxExpr|ctxType) != 0) && top&ctxStmt == 0 && ok&(ctxExpr|ctxType|ctxCallee) == 0 { - base.Errorf("%v used as value", n) - n.SetType(nil) - return n - } - - if (top&ctxStmt != 0) && top&(ctxCallee|ctxExpr|ctxType) == 0 && ok&ctxStmt == 0 { - if !n.Diag() { - base.Errorf("%v evaluated but not used", n) - n.SetDiag(true) - } - - n.SetType(nil) - return n - } - - return n + // No return n here! + // Individual cases can type-assert n, introducing a new one. + // Each must execute its own return n. } func typecheckargs(n ir.Node) { diff --git a/src/cmd/compile/internal/ir/expr.go b/src/cmd/compile/internal/ir/expr.go index 7b1aeedcdf..7165a06b25 100644 --- a/src/cmd/compile/internal/ir/expr.go +++ b/src/cmd/compile/internal/ir/expr.go @@ -32,7 +32,13 @@ func maybeEdit(x Node, edit func(Node) Node) Node { return edit(x) } -// A miniStmt is a miniNode with extra fields common to expressions. +// An Expr is a Node that can appear as an expression. +type Expr interface { + Node + isExpr() +} + +// A miniExpr is a miniNode with extra fields common to expressions. // TODO(rsc): Once we are sure about the contents, compact the bools // into a bit field and leave extra bits available for implementations // embedding miniExpr. Right now there are ~60 unused bits sitting here. @@ -52,6 +58,8 @@ const ( miniExprBounded ) +func (*miniExpr) isExpr() {} + func (n *miniExpr) Type() *types.Type { return n.typ } func (n *miniExpr) SetType(x *types.Type) { n.typ = x } func (n *miniExpr) Opt() interface{} { return n.opt } @@ -192,6 +200,8 @@ func NewCallExpr(pos src.XPos, fun Node, args []Node) *CallExpr { return n } +func (*CallExpr) isStmt() {} + func (n *CallExpr) Orig() Node { return n.orig } func (n *CallExpr) SetOrig(x Node) { n.orig = x } func (n *CallExpr) Left() Node { return n.X } diff --git a/src/cmd/compile/internal/ir/func.go b/src/cmd/compile/internal/ir/func.go index 38e00da7da..3bca25b504 100644 --- a/src/cmd/compile/internal/ir/func.go +++ b/src/cmd/compile/internal/ir/func.go @@ -114,6 +114,8 @@ func NewFunc(pos src.XPos) *Func { return f } +func (f *Func) isStmt() {} + func (f *Func) Func() *Func { return f } func (f *Func) Body() Nodes { return f.body } func (f *Func) PtrBody() *Nodes { return &f.body } diff --git a/src/cmd/compile/internal/ir/name.go b/src/cmd/compile/internal/ir/name.go index 06cffe0325..c527ba281d 100644 --- a/src/cmd/compile/internal/ir/name.go +++ b/src/cmd/compile/internal/ir/name.go @@ -121,6 +121,8 @@ type Name struct { Outer *Name } +func (n *Name) isExpr() {} + // NewNameAt returns a new ONAME Node associated with symbol s at position pos. // The caller is responsible for setting Curfn. func NewNameAt(pos src.XPos, sym *types.Sym) *Name { diff --git a/src/cmd/compile/internal/ir/stmt.go b/src/cmd/compile/internal/ir/stmt.go index 19f90ce1fa..836bbcb453 100644 --- a/src/cmd/compile/internal/ir/stmt.go +++ b/src/cmd/compile/internal/ir/stmt.go @@ -27,15 +27,26 @@ func NewDecl(pos src.XPos, op Op, x Node) *Decl { return n } +func (*Decl) isStmt() {} + func (n *Decl) Left() Node { return n.X } func (n *Decl) SetLeft(x Node) { n.X = x } +// A Stmt is a Node that can appear as a statement. +// This includes statement-like expressions such as <-c and f(). +type Stmt interface { + Node + isStmt() +} + // A miniStmt is a miniNode with extra fields common to statements. type miniStmt struct { miniNode init Nodes } +func (*miniStmt) isStmt() {} + func (n *miniStmt) Init() Nodes { return n.init } func (n *miniStmt) SetInit(x Nodes) { n.init = x } func (n *miniStmt) PtrInit() *Nodes { return &n.init } diff --git a/test/used.go b/test/used.go index adf2bfcb95..5c7aad24a6 100644 --- a/test/used.go +++ b/test/used.go @@ -10,7 +10,7 @@ import "unsafe" const C = 1 -var x1, x int +var x, x1, x2 int var b bool var s string var c chan int @@ -120,7 +120,6 @@ func _() { _ = print(1) // ERROR "print\(1\) used as value" println(1) // ok _ = println(1) // ERROR "println\(1\) used as value" - (x) // ERROR "x evaluated but not used" c <- 1 // ok slice[1:1] // ERROR "slice\[1:1\] evaluated but not used" array[1:1] // ERROR "array\[1:1\] evaluated but not used" @@ -137,6 +136,8 @@ func _() { unsafe.Alignof(t.X) // ERROR "unsafe.Alignof\(t.X\) evaluated but not used" unsafe.Offsetof(t.X) // ERROR "unsafe.Offsetof\(t.X\) evaluated but not used" unsafe.Sizeof(t) // ERROR "unsafe.Sizeof\(t\) evaluated but not used" - _ = new(x) // ERROR "x is not a type" _ = int // ERROR "type int is not an expression" + (x) // ERROR "x evaluated but not used" + _ = new(x2) // ERROR "x2 is not a type" + _ = new(1 + 1) // ERROR "1 \+ 1 is not a type" } -- 2.50.0