| // 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 ( |
| "cmd/compile/internal/base" |
| "cmd/compile/internal/ir" |
| "cmd/compile/internal/types" |
| "cmd/internal/src" |
| "internal/types/errors" |
| ) |
| |
| func RangeExprType(t *types.Type) *types.Type { |
| if t.IsPtr() && t.Elem().IsArray() { |
| return t.Elem() |
| } |
| return t |
| } |
| |
| 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("tcAssign", n)(nil) |
| } |
| |
| if n.Y == nil { |
| n.X = AssignExpr(n.X) |
| return |
| } |
| |
| lhs, rhs := []ir.Node{n.X}, []ir.Node{n.Y} |
| assign(n, lhs, rhs) |
| n.X, n.Y = lhs[0], rhs[0] |
| |
| // TODO(mdempsky): This seems out of place. |
| 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("tcAssignList", n)(nil) |
| } |
| |
| assign(n, n.Lhs, n.Rhs) |
| } |
| |
| func assign(stmt ir.Node, lhs, rhs []ir.Node) { |
| // delicate little dance. |
| // the definition of lhs may refer to this assignment |
| // as its definition, in which case it will call tcAssign. |
| // 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). |
| |
| checkLHS := func(i int, typ *types.Type) { |
| if n := lhs[i]; typ != nil && ir.DeclaredBy(n, stmt) && n.Type() == nil { |
| base.Assertf(typ.Kind() == types.TNIL, "unexpected untyped nil") |
| n.SetType(defaultType(typ)) |
| } |
| if lhs[i].Typecheck() == 0 { |
| lhs[i] = AssignExpr(lhs[i]) |
| } |
| checkassign(lhs[i]) |
| } |
| |
| assignType := func(i int, typ *types.Type) { |
| checkLHS(i, typ) |
| if typ != nil { |
| checkassignto(typ, lhs[i]) |
| } |
| } |
| |
| cr := len(rhs) |
| if len(rhs) == 1 { |
| rhs[0] = typecheck(rhs[0], ctxExpr|ctxMultiOK) |
| if rtyp := rhs[0].Type(); rtyp != nil && rtyp.IsFuncArgStruct() { |
| cr = rtyp.NumFields() |
| } |
| } else { |
| Exprs(rhs) |
| } |
| |
| // x, ok = y |
| assignOK: |
| for len(lhs) == 2 && cr == 1 { |
| stmt := stmt.(*ir.AssignListStmt) |
| r := rhs[0] |
| |
| switch r.Op() { |
| case ir.OINDEXMAP: |
| stmt.SetOp(ir.OAS2MAPR) |
| case ir.ORECV: |
| stmt.SetOp(ir.OAS2RECV) |
| case ir.ODOTTYPE: |
| r := r.(*ir.TypeAssertExpr) |
| stmt.SetOp(ir.OAS2DOTTYPE) |
| r.SetOp(ir.ODOTTYPE2) |
| case ir.ODYNAMICDOTTYPE: |
| r := r.(*ir.DynamicTypeAssertExpr) |
| stmt.SetOp(ir.OAS2DOTTYPE) |
| r.SetOp(ir.ODYNAMICDOTTYPE2) |
| default: |
| break assignOK |
| } |
| |
| assignType(0, r.Type()) |
| assignType(1, types.UntypedBool) |
| return |
| } |
| |
| if len(lhs) != cr { |
| if r, ok := rhs[0].(*ir.CallExpr); ok && len(rhs) == 1 { |
| if r.Type() != nil { |
| base.ErrorfAt(stmt.Pos(), errors.WrongAssignCount, "assignment mismatch: %d variable%s but %v returns %d value%s", len(lhs), plural(len(lhs)), r.Fun, cr, plural(cr)) |
| } |
| } else { |
| base.ErrorfAt(stmt.Pos(), errors.WrongAssignCount, "assignment mismatch: %d variable%s but %v value%s", len(lhs), plural(len(lhs)), len(rhs), plural(len(rhs))) |
| } |
| |
| for i := range lhs { |
| checkLHS(i, nil) |
| } |
| return |
| } |
| |
| // x,y,z = f() |
| if cr > len(rhs) { |
| stmt := stmt.(*ir.AssignListStmt) |
| stmt.SetOp(ir.OAS2FUNC) |
| r := rhs[0].(*ir.CallExpr) |
| rtyp := r.Type() |
| |
| mismatched := false |
| failed := false |
| for i := range lhs { |
| result := rtyp.Field(i).Type |
| assignType(i, result) |
| |
| if lhs[i].Type() == nil || result == nil { |
| failed = true |
| } else if lhs[i] != ir.BlankNode && !types.Identical(lhs[i].Type(), result) { |
| mismatched = true |
| } |
| } |
| if mismatched && !failed { |
| RewriteMultiValueCall(stmt, r) |
| } |
| return |
| } |
| |
| for i, r := range rhs { |
| checkLHS(i, r.Type()) |
| if lhs[i].Type() != nil { |
| rhs[i] = AssignConv(r, lhs[i].Type(), "assignment") |
| } |
| } |
| } |
| |
| func plural(n int) string { |
| if n == 1 { |
| return "" |
| } |
| return "s" |
| } |
| |
| // tcCheckNil typechecks an OCHECKNIL node. |
| func tcCheckNil(n *ir.UnaryExpr) ir.Node { |
| n.X = Expr(n.X) |
| if !n.X.Type().IsPtrShaped() { |
| base.FatalfAt(n.Pos(), "%L is not pointer shaped", n.X) |
| } |
| return n |
| } |
| |
| // tcFor typechecks an OFOR node. |
| func tcFor(n *ir.ForStmt) 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 for condition", n.Cond) |
| } |
| } |
| n.Post = Stmt(n.Post) |
| Stmts(n.Body) |
| return n |
| } |
| |
| // tcGoDefer typechecks (normalizes) an OGO/ODEFER statement. |
| func tcGoDefer(n *ir.GoDeferStmt) { |
| call := normalizeGoDeferCall(n.Pos(), n.Op(), n.Call, n.PtrInit()) |
| call.GoDefer = true |
| n.Call = call |
| } |
| |
| // normalizeGoDeferCall normalizes call into a normal function call |
| // with no arguments and no results, suitable for use in an OGO/ODEFER |
| // statement. |
| // |
| // For example, it normalizes: |
| // |
| // f(x, y) |
| // |
| // into: |
| // |
| // x1, y1 := x, y // added to init |
| // func() { f(x1, y1) }() // result |
| func normalizeGoDeferCall(pos src.XPos, op ir.Op, call ir.Node, init *ir.Nodes) *ir.CallExpr { |
| init.Append(ir.TakeInit(call)...) |
| |
| if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC { |
| if sig := call.Fun.Type(); sig.NumParams()+sig.NumResults() == 0 { |
| return call // already in normal form |
| } |
| } |
| |
| // Create a new wrapper function without parameters or results. |
| wrapperFn := ir.NewClosureFunc(pos, pos, op, types.NewSignature(nil, nil, nil), ir.CurFunc, Target) |
| wrapperFn.DeclareParams(true) |
| wrapperFn.SetWrapper(true) |
| |
| // argps collects the list of operands within the call expression |
| // that must be evaluated at the go/defer statement. |
| var argps []*ir.Node |
| |
| var visit func(argp *ir.Node) |
| visit = func(argp *ir.Node) { |
| arg := *argp |
| if arg == nil { |
| return |
| } |
| |
| // Recognize a few common expressions that can be evaluated within |
| // the wrapper, so we don't need to allocate space for them within |
| // the closure. |
| switch arg.Op() { |
| case ir.OLITERAL, ir.ONIL, ir.OMETHEXPR, ir.ONEW: |
| return |
| case ir.ONAME: |
| arg := arg.(*ir.Name) |
| if arg.Class == ir.PFUNC { |
| return // reference to global function |
| } |
| case ir.OADDR: |
| arg := arg.(*ir.AddrExpr) |
| if arg.X.Op() == ir.OLINKSYMOFFSET { |
| return // address of global symbol |
| } |
| |
| case ir.OCONVNOP: |
| arg := arg.(*ir.ConvExpr) |
| |
| // For unsafe.Pointer->uintptr conversion arguments, save the |
| // unsafe.Pointer argument. This is necessary to handle cases |
| // like fixedbugs/issue24491a.go correctly. |
| // |
| // TODO(mdempsky): Limit to static callees with |
| // //go:uintptr{escapes,keepalive}? |
| if arg.Type().IsUintptr() && arg.X.Type().IsUnsafePtr() { |
| visit(&arg.X) |
| return |
| } |
| |
| case ir.OARRAYLIT, ir.OSLICELIT, ir.OSTRUCTLIT: |
| // TODO(mdempsky): For very large slices, it may be preferable |
| // to construct them at the go/defer statement instead. |
| list := arg.(*ir.CompLitExpr).List |
| for i, el := range list { |
| switch el := el.(type) { |
| case *ir.KeyExpr: |
| visit(&el.Value) |
| case *ir.StructKeyExpr: |
| visit(&el.Value) |
| default: |
| visit(&list[i]) |
| } |
| } |
| return |
| } |
| |
| argps = append(argps, argp) |
| } |
| |
| visitList := func(list []ir.Node) { |
| for i := range list { |
| visit(&list[i]) |
| } |
| } |
| |
| switch call.Op() { |
| default: |
| base.Fatalf("unexpected call op: %v", call.Op()) |
| |
| case ir.OCALLFUNC: |
| call := call.(*ir.CallExpr) |
| |
| // If the callee is a named function, link to the original callee. |
| if wrapped := ir.StaticCalleeName(call.Fun); wrapped != nil { |
| wrapperFn.WrappedFunc = wrapped.Func |
| } |
| |
| visit(&call.Fun) |
| visitList(call.Args) |
| |
| case ir.OCALLINTER: |
| call := call.(*ir.CallExpr) |
| argps = append(argps, &call.Fun.(*ir.SelectorExpr).X) // must be first for OCHECKNIL; see below |
| visitList(call.Args) |
| |
| case ir.OAPPEND, ir.ODELETE, ir.OPRINT, ir.OPRINTLN, ir.ORECOVERFP: |
| call := call.(*ir.CallExpr) |
| visitList(call.Args) |
| visit(&call.RType) |
| |
| case ir.OCOPY: |
| call := call.(*ir.BinaryExpr) |
| visit(&call.X) |
| visit(&call.Y) |
| visit(&call.RType) |
| |
| case ir.OCLEAR, ir.OCLOSE, ir.OPANIC: |
| call := call.(*ir.UnaryExpr) |
| visit(&call.X) |
| } |
| |
| if len(argps) != 0 { |
| // Found one or more operands that need to be evaluated upfront |
| // and spilled to temporary variables, which can be captured by |
| // the wrapper function. |
| |
| stmtPos := base.Pos |
| callPos := base.Pos |
| |
| as := ir.NewAssignListStmt(callPos, ir.OAS2, make([]ir.Node, len(argps)), make([]ir.Node, len(argps))) |
| for i, argp := range argps { |
| arg := *argp |
| |
| pos := callPos |
| if ir.HasUniquePos(arg) { |
| pos = arg.Pos() |
| } |
| |
| // tmp := arg |
| tmp := TempAt(pos, ir.CurFunc, arg.Type()) |
| init.Append(Stmt(ir.NewDecl(pos, ir.ODCL, tmp))) |
| tmp.Defn = as |
| as.Lhs[i] = tmp |
| as.Rhs[i] = arg |
| |
| // Rewrite original expression to use/capture tmp. |
| *argp = ir.NewClosureVar(pos, wrapperFn, tmp) |
| } |
| init.Append(Stmt(as)) |
| |
| // For "go/defer iface.M()", if iface is nil, we need to panic at |
| // the point of the go/defer statement. |
| if call.Op() == ir.OCALLINTER { |
| iface := as.Lhs[0] |
| init.Append(Stmt(ir.NewUnaryExpr(stmtPos, ir.OCHECKNIL, ir.NewUnaryExpr(iface.Pos(), ir.OITAB, iface)))) |
| } |
| } |
| |
| // Move call into the wrapper function, now that it's safe to |
| // evaluate there. |
| wrapperFn.Body = []ir.Node{call} |
| |
| // Finally, construct a call to the wrapper. |
| return Call(call.Pos(), wrapperFn.OClosure, nil, false).(*ir.CallExpr) |
| } |
| |
| // 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) { |
| n.X = Expr(n.X) |
| |
| // delicate little dance. see tcAssignList |
| if n.Key != nil { |
| if !ir.DeclaredBy(n.Key, n) { |
| n.Key = AssignExpr(n.Key) |
| } |
| checkassign(n.Key) |
| } |
| if n.Value != nil { |
| if !ir.DeclaredBy(n.Value, n) { |
| n.Value = AssignExpr(n.Value) |
| } |
| checkassign(n.Value) |
| } |
| |
| // second half of dance |
| n.SetTypecheck(1) |
| if n.Key != nil && n.Key.Typecheck() == 0 { |
| n.Key = AssignExpr(n.Key) |
| } |
| if n.Value != nil && n.Value.Typecheck() == 0 { |
| n.Value = AssignExpr(n.Value) |
| } |
| |
| Stmts(n.Body) |
| } |
| |
| // tcReturn typechecks an ORETURN node. |
| func tcReturn(n *ir.ReturnStmt) ir.Node { |
| if ir.CurFunc == nil { |
| base.FatalfAt(n.Pos(), "return outside function") |
| } |
| |
| typecheckargs(n) |
| if len(n.Results) != 0 { |
| typecheckaste(ir.ORETURN, nil, false, ir.CurFunc.Type().Results(), n.Results, func() string { return "return argument" }) |
| } |
| return n |
| } |
| |
| // select |
| func tcSelect(sel *ir.SelectStmt) { |
| var def *ir.CommClause |
| lno := ir.SetPos(sel) |
| Stmts(sel.Init()) |
| for _, ncase := range sel.Cases { |
| if ncase.Comm == nil { |
| // default |
| if def != nil { |
| base.ErrorfAt(ncase.Pos(), errors.DuplicateDefault, "multiple defaults in select (first at %v)", ir.Line(def)) |
| } else { |
| def = ncase |
| } |
| } else { |
| n := Stmt(ncase.Comm) |
| ncase.Comm = n |
| oselrecv2 := func(dst, recv ir.Node, def bool) { |
| selrecv := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv}) |
| selrecv.Def = def |
| selrecv.SetTypecheck(1) |
| selrecv.SetInit(n.Init()) |
| ncase.Comm = selrecv |
| } |
| switch n.Op() { |
| default: |
| pos := n.Pos() |
| if n.Op() == ir.ONAME { |
| // We don't have the right position for ONAME nodes (see #15459 and |
| // others). Using ncase.Pos for now as it will provide the correct |
| // line number (assuming the expression follows the "case" keyword |
| // on the same line). This matches the approach before 1.10. |
| pos = ncase.Pos() |
| } |
| base.ErrorfAt(pos, errors.InvalidSelectCase, "select case must be receive, send or assign recv") |
| |
| case ir.OAS: |
| // convert x = <-c into x, _ = <-c |
| // remove implicit conversions; the eventual assignment |
| // will reintroduce them. |
| n := n.(*ir.AssignStmt) |
| if r := n.Y; r.Op() == ir.OCONVNOP || r.Op() == ir.OCONVIFACE { |
| r := r.(*ir.ConvExpr) |
| if r.Implicit() { |
| n.Y = r.X |
| } |
| } |
| if n.Y.Op() != ir.ORECV { |
| base.ErrorfAt(n.Pos(), errors.InvalidSelectCase, "select assignment must have receive on right hand side") |
| break |
| } |
| oselrecv2(n.X, n.Y, n.Def) |
| |
| case ir.OAS2RECV: |
| n := n.(*ir.AssignListStmt) |
| if n.Rhs[0].Op() != ir.ORECV { |
| base.ErrorfAt(n.Pos(), errors.InvalidSelectCase, "select assignment must have receive on right hand side") |
| break |
| } |
| n.SetOp(ir.OSELRECV2) |
| |
| case ir.ORECV: |
| // convert <-c into _, _ = <-c |
| n := n.(*ir.UnaryExpr) |
| oselrecv2(ir.BlankNode, n, false) |
| |
| case ir.OSEND: |
| break |
| } |
| } |
| |
| Stmts(ncase.Body) |
| } |
| |
| base.Pos = lno |
| } |
| |
| // 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 |
| } |
| |
| 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 |
| } |
| |
| // 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 tcSwitchExpr(n *ir.SwitchStmt) { |
| t := types.Types[types.TBOOL] |
| if n.Tag != nil { |
| n.Tag = Expr(n.Tag) |
| n.Tag = DefaultLit(n.Tag, nil) |
| t = n.Tag.Type() |
| } |
| |
| var nilonly string |
| if t != nil { |
| switch { |
| case t.IsMap(): |
| nilonly = "map" |
| case t.Kind() == types.TFUNC: |
| nilonly = "func" |
| case t.IsSlice(): |
| nilonly = "slice" |
| |
| case !types.IsComparable(t): |
| if t.IsStruct() { |
| base.ErrorfAt(n.Pos(), errors.InvalidExprSwitch, "cannot switch on %L (struct containing %v cannot be compared)", n.Tag, types.IncomparableField(t).Type) |
| } else { |
| base.ErrorfAt(n.Pos(), errors.InvalidExprSwitch, "cannot switch on %L", n.Tag) |
| } |
| t = nil |
| } |
| } |
| |
| var defCase ir.Node |
| for _, ncase := range n.Cases { |
| ls := ncase.List |
| if len(ls) == 0 { // default: |
| if defCase != nil { |
| base.ErrorfAt(ncase.Pos(), errors.DuplicateDefault, "multiple defaults in switch (first at %v)", ir.Line(defCase)) |
| } else { |
| defCase = ncase |
| } |
| } |
| |
| for i := range ls { |
| ir.SetPos(ncase) |
| ls[i] = Expr(ls[i]) |
| ls[i] = DefaultLit(ls[i], t) |
| n1 := ls[i] |
| if t == nil || n1.Type() == nil { |
| continue |
| } |
| |
| if nilonly != "" && !ir.IsNil(n1) { |
| base.ErrorfAt(ncase.Pos(), errors.MismatchedTypes, "invalid case %v in switch (can only compare %s %v to nil)", n1, nilonly, n.Tag) |
| } else if t.IsInterface() && !n1.Type().IsInterface() && !types.IsComparable(n1.Type()) { |
| base.ErrorfAt(ncase.Pos(), errors.UndefinedOp, "invalid case %L in switch (incomparable type)", n1) |
| } else { |
| op1, _ := assignOp(n1.Type(), t) |
| op2, _ := assignOp(t, n1.Type()) |
| if op1 == ir.OXXX && op2 == ir.OXXX { |
| if n.Tag != nil { |
| base.ErrorfAt(ncase.Pos(), errors.MismatchedTypes, "invalid case %v in switch on %v (mismatched types %v and %v)", n1, n.Tag, n1.Type(), t) |
| } else { |
| base.ErrorfAt(ncase.Pos(), errors.MismatchedTypes, "invalid case %v in switch (mismatched types %v and bool)", n1, n1.Type()) |
| } |
| } |
| } |
| } |
| |
| Stmts(ncase.Body) |
| } |
| } |
| |
| func tcSwitchType(n *ir.SwitchStmt) { |
| guard := n.Tag.(*ir.TypeSwitchGuard) |
| guard.X = Expr(guard.X) |
| t := guard.X.Type() |
| if t != nil && !t.IsInterface() { |
| base.ErrorfAt(n.Pos(), errors.InvalidTypeSwitch, "cannot type switch on non-interface value %L", guard.X) |
| t = nil |
| } |
| |
| // We don't actually declare the type switch's guarded |
| // declaration itself. So if there are no cases, we won't |
| // notice that it went unused. |
| if v := guard.Tag; v != nil && !ir.IsBlank(v) && len(n.Cases) == 0 { |
| base.ErrorfAt(v.Pos(), errors.UnusedVar, "%v declared but not used", v.Sym()) |
| } |
| |
| var defCase, nilCase ir.Node |
| var ts typeSet |
| for _, ncase := range n.Cases { |
| ls := ncase.List |
| if len(ls) == 0 { // default: |
| if defCase != nil { |
| base.ErrorfAt(ncase.Pos(), errors.DuplicateDefault, "multiple defaults in switch (first at %v)", ir.Line(defCase)) |
| } else { |
| defCase = ncase |
| } |
| } |
| |
| for i := range ls { |
| ls[i] = typecheck(ls[i], ctxExpr|ctxType) |
| n1 := ls[i] |
| if t == nil || n1.Type() == nil { |
| continue |
| } |
| |
| if ir.IsNil(n1) { // case nil: |
| if nilCase != nil { |
| base.ErrorfAt(ncase.Pos(), errors.DuplicateCase, "multiple nil cases in type switch (first at %v)", ir.Line(nilCase)) |
| } else { |
| nilCase = ncase |
| } |
| continue |
| } |
| if n1.Op() == ir.ODYNAMICTYPE { |
| continue |
| } |
| if n1.Op() != ir.OTYPE { |
| base.ErrorfAt(ncase.Pos(), errors.NotAType, "%L is not a type", n1) |
| continue |
| } |
| if !n1.Type().IsInterface() { |
| why := ImplementsExplain(n1.Type(), t) |
| if why != "" { |
| base.ErrorfAt(ncase.Pos(), errors.ImpossibleAssert, "impossible type switch case: %L cannot have dynamic type %v (%s)", guard.X, n1.Type(), why) |
| } |
| continue |
| } |
| |
| ts.add(ncase.Pos(), n1.Type()) |
| } |
| |
| if ncase.Var != nil { |
| // Assign the clause variable's type. |
| vt := t |
| if len(ls) == 1 { |
| if ls[0].Op() == ir.OTYPE || ls[0].Op() == ir.ODYNAMICTYPE { |
| vt = ls[0].Type() |
| } else if !ir.IsNil(ls[0]) { |
| // Invalid single-type case; |
| // mark variable as broken. |
| vt = nil |
| } |
| } |
| |
| nvar := ncase.Var |
| nvar.SetType(vt) |
| if vt != nil { |
| nvar = AssignExpr(nvar).(*ir.Name) |
| } else { |
| // Clause variable is broken; prevent typechecking. |
| nvar.SetTypecheck(1) |
| } |
| ncase.Var = nvar |
| } |
| |
| Stmts(ncase.Body) |
| } |
| } |
| |
| type typeSet struct { |
| m map[string]src.XPos |
| } |
| |
| func (s *typeSet) add(pos src.XPos, typ *types.Type) { |
| if s.m == nil { |
| s.m = make(map[string]src.XPos) |
| } |
| |
| ls := typ.LinkString() |
| if prev, ok := s.m[ls]; ok { |
| base.ErrorfAt(pos, errors.DuplicateCase, "duplicate case %v in type switch\n\tprevious case at %s", typ, base.FmtPos(prev)) |
| return |
| } |
| s.m[ls] = pos |
| } |