// TODO(austin) This is a hack to eliminate a circular dependency // between type.go and expr.go func (a *compiler) compileArrayLen(b *block, expr ast.Expr) (int64, bool) { lenExpr := a.compileExpr(b, true, expr) if lenExpr == nil { return 0, false } // XXX(Spec) Are ideal floats with no fractional part okay? if lenExpr.t.isIdeal() { lenExpr = lenExpr.convertTo(IntType) if lenExpr == nil { return 0, false } } if !lenExpr.t.isInteger() { a.diagAt(expr.Pos(), "array size must be an integer") return 0, false } switch lenExpr.t.lit().(type) { case *intType: return lenExpr.asInt()(nil), true case *uintType: return int64(lenExpr.asUint()(nil)), true } log.Panicf("unexpected integer type %T", lenExpr.t) return 0, false }
func (check *checker) constDecl(obj *Const, typ, init ast.Expr) { if obj.visited { check.errorf(obj.Pos(), "illegal cycle in initialization of constant %s", obj.name) obj.typ = Typ[Invalid] return } obj.visited = true // use the correct value of iota assert(check.iota == nil) check.iota = obj.val // determine type, if any if typ != nil { t := check.typ(typ, nil, false) if !isConstType(t) { check.errorf(typ.Pos(), "invalid constant type %s", t) obj.typ = Typ[Invalid] check.iota = nil return } obj.typ = t } // check initialization var x operand if init != nil { check.expr(&x, init) } check.initConst(obj, &x) check.iota = nil }
func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) { assert(obj.typ == nil) if obj.visited { obj.typ = Typ[Invalid] return } obj.visited = true // use the correct value of iota assert(check.iota == nil) check.iota = obj.val defer func() { check.iota = nil }() // provide valid constant value under all circumstances obj.val = exact.MakeUnknown() // determine type, if any if typ != nil { t := check.typ(typ) if !isConstType(t) { check.errorf(typ.Pos(), "invalid constant type %s", t) obj.typ = Typ[Invalid] return } obj.typ = t } // check initialization var x operand if init != nil { check.expr(&x, init) } check.initConst(obj, &x) }
func checkCopyLocksRangeVar(f *File, rtok token.Token, e ast.Expr) { if e == nil { return } id, isId := e.(*ast.Ident) if isId && id.Name == "_" { return } var typ types.Type if rtok == token.DEFINE { if !isId { return } obj := f.pkg.defs[id] if obj == nil { return } typ = obj.Type() } else { typ = f.pkg.types[e].Type } if typ == nil { return } if path := lockPath(f.pkg.typesPkg, typ); path != nil { f.Badf(e.Pos(), "range var %s copies lock: %v", f.gofmt(e), path) } }
func (c *conditionEvaluator) eval(e ast.Expr) bool { if c.stop { return false } switch e := e.(type) { case *ast.ParenExpr: return c.eval(e.X) case *ast.BinaryExpr: if e.Op == token.LAND { return c.eval(e.X) && c.eval(e.Y) } else if e.Op == token.LOR { return c.eval(e.X) || c.eval(e.Y) } assert(e.Op == token.EQL) value := c.getVarValue(e.X.(*ast.Ident).Name) if !value.isBound() { c.stop = true return false } eqValue := c.cond.equalityValues[e.Pos()] assert(eqValue.isBound()) return value.compare(c.cond.equalityValues[e.Pos()]) == 0 default: panic(errors.New("processCondition must have ensured condition is evaluatable")) } return false }
func (check *Checker) recordCommaOkTypes(x ast.Expr, a [2]Type) { assert(x != nil) if a[0] == nil || a[1] == nil { return } assert(isTyped(a[0]) && isTyped(a[1]) && isBoolean(a[1])) if m := check.Types; m != nil { for { tv := m[x] assert(tv.Type != nil) // should have been recorded already pos := x.Pos() tv.Type = NewTuple( NewVar(pos, check.pkg, "", a[0]), NewVar(pos, check.pkg, "", a[1]), ) m[x] = tv // if x is a parenthesized expression (p.X), update p.X p, _ := x.(*ast.ParenExpr) if p == nil { break } x = p.X } } }
func (f *File) validRetExpr(expr ast.Expr) *Error { _, ok := expr.(*ast.Ident) // can only return identifies, i.e. variables if !ok { return &Error{errors.New("Return expression only allows identifiers"), expr.Pos()} } return nil }
func emitTraceExpr(f *Function, event TraceEvent, syntax ast.Expr) Value { t := &Trace{ Event: event, Start: syntax.Pos(), End: syntax.End(), Breakpoint: false, syntax: syntax, } return emitTraceCommon(f, t) }
func extractText(ctx *Context, t ast.Expr) (string, error) { pos := ctx.Fset.Position(t.Pos()) end := ctx.Fset.Position(t.End()) read, err := ioutil.ReadFile(pos.Filename) if err != nil { return "", err } return string(read[pos.Offset:end.Offset]), nil }
func (check *checker) recordCommaOkTypes(x ast.Expr, t1, t2 Type) { assert(x != nil && isTyped(t1) && isTyped(t2) && isBoolean(t2)) if m := check.Types; m != nil { assert(m[x] != nil) // should have been recorded already pos := x.Pos() m[x] = NewTuple( NewVar(pos, check.pkg, "", t1), NewVar(pos, check.pkg, "", t2), ) } }
func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool { // TODO: for now, we can only test builtin types and untyped constants. typ := f.pkg.types[arg] if typ == nil { return true } basic, ok := typ.(*types.Basic) if !ok { return true } switch basic.Kind { case types.Bool: return t&argBool != 0 case types.Int, types.Int8, types.Int16, types.Int32, types.Int64: fallthrough case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr: return t&argInt != 0 case types.Float32, types.Float64, types.Complex64, types.Complex128: return t&argFloat != 0 case types.String: return t&argString != 0 case types.UnsafePointer: return t&(argPointer|argInt) != 0 case types.UntypedBool: return t&argBool != 0 case types.UntypedComplex: return t&argFloat != 0 case types.UntypedFloat: // If it's integral, we can use an int format. switch f.pkg.values[arg].(type) { case int, int8, int16, int32, int64: return t&(argInt|argFloat) != 0 case uint, uint8, uint16, uint32, uint64: return t&(argInt|argFloat) != 0 } return t&argFloat != 0 case types.UntypedInt: return t&argInt != 0 case types.UntypedRune: return t&(argInt|argRune) != 0 case types.UntypedString: return t&argString != 0 case types.UntypedNil: return t&argPointer != 0 // TODO? case types.Invalid: if *verbose { f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", arg) } return true // Probably a type check problem. } return false }
// argument typechecks passing an argument arg (if arg != nil) or // x (if arg == nil) to the i'th parameter of the given signature. // If passSlice is set, the argument is followed by ... in the call. // func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand, passSlice bool) { // determine parameter var par *Var n := sig.params.Len() if i < n { par = sig.params.vars[i] } else if sig.isVariadic { par = sig.params.vars[n-1] } else { var pos token.Pos switch { case arg != nil: pos = arg.Pos() case x != nil: pos = x.pos() default: // TODO(gri) what position to use? } check.errorf(pos, "too many arguments") return } // determine argument var z operand z.mode = variable z.expr = nil // TODO(gri) can we do better here? (for good error messages) z.typ = par.typ if arg != nil { check.expr(x, arg, z.typ, -1) } if x.mode == invalid { return // ignore this argument } // check last argument of the form x... if passSlice { if i+1 != n { check.errorf(x.pos(), "can only use ... with matching parameter") return // ignore this argument } // spec: "If the final argument is assignable to a slice type []T, // it may be passed unchanged as the value for a ...T parameter if // the argument is followed by ..." z.typ = &Slice{elt: z.typ} // change final parameter type to []T } if !check.assignment(x, z.typ) && x.mode != invalid { check.errorf(x.pos(), "cannot pass argument %s to %s", x, &z) } }
// checkAtomicAddAssignment walks the atomic.Add* method calls checking for assigning the return value // to the same variable being used in the operation func (f *File) checkAtomicAddAssignment(left ast.Expr, call *ast.CallExpr) { arg := call.Args[0] broken := false if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND { broken = f.gofmt(left) == f.gofmt(uarg.X) } else if star, ok := left.(*ast.StarExpr); ok { broken = f.gofmt(star.X) == f.gofmt(arg) } if broken { f.Warn(left.Pos(), "direct assignment to atomic value") } }
func (tr *Transformer) matchWildcard(xobj *types.Var, y ast.Expr) bool { name := xobj.Name() if tr.verbose { fmt.Fprintf(os.Stderr, "%s: wildcard %s -> %s?: ", tr.fset.Position(y.Pos()), name, astString(tr.fset, y)) } // Check that y is assignable to the declared type of the param. yt := tr.info.TypeOf(y) if yt == nil { // y has no type. // Perhaps it is an *ast.Ellipsis in [...]T{}, or // an *ast.KeyValueExpr in T{k: v}. // Clearly these pseudo-expressions cannot match a // wildcard, but it would nice if we had a way to ignore // the difference between T{v} and T{k:v} for structs. return false } if !types.AssignableTo(yt, xobj.Type()) { if tr.verbose { fmt.Fprintf(os.Stderr, "%s not assignable to %s\n", yt, xobj.Type()) } return false } // A wildcard matches any expression. // If it appears multiple times in the pattern, it must match // the same expression each time. if old, ok := tr.env[name]; ok { // found existing binding tr.allowWildcards = false r := tr.matchExpr(old, y) if tr.verbose { fmt.Fprintf(os.Stderr, "%t secondary match, primary was %s\n", r, astString(tr.fset, old)) } tr.allowWildcards = true return r } if tr.verbose { fmt.Fprintf(os.Stderr, "primary match\n") } tr.env[name] = y // record binding return true }
// isUntypedConst reports whether expr is an untyped constant, // and indicates what its default type is. // scope may be nil. func (f *file) isUntypedConst(expr ast.Expr) (defType string, ok bool) { // Re-evaluate expr outside of its context to see if it's untyped. // (An expr evaluated within, for example, an assignment context will get the type of the LHS.) exprStr := f.render(expr) tv, err := types.Eval(f.fset, f.pkg.typesPkg, expr.Pos(), exprStr) if err != nil { return "", false } if b, ok := tv.Type.(*types.Basic); ok { if dt, ok := basicTypeKinds[b.Kind()]; ok { return dt, true } } return "", false }
// typ type-checks the type expression e and returns its type, or Typ[Invalid]. // If def != nil, e is the type specification for the named type def, declared // in a type declaration, and def.UnderlyingT will be set to the type of e before // any components of e are type-checked. // If cycleOk is set, e (or elements of e) may refer to a named type that is not // yet completely set up. // func (check *checker) typ(e ast.Expr, def *Named, cycleOk bool) (T Type) { if trace { check.trace(e.Pos(), "%s", e) check.indent++ defer func() { check.indent-- check.trace(e.Pos(), "=> %s", T) }() } T = check.typInternal(e, def, cycleOk) assert(isTyped(T)) check.recordTypeAndValue(e, T, nil) return }
// typExpr type-checks the type expression e and returns its type, or Typ[Invalid]. // If def != nil, e is the type specification for the named type def, declared // in a type declaration, and def.underlying will be set to the type of e before // any components of e are type-checked. Path contains the path of named types // referring to this type. // func (check *Checker) typExpr(e ast.Expr, def *Named, path []*TypeName) (T Type) { if trace { check.trace(e.Pos(), "%s", e) check.indent++ defer func() { check.indent-- check.trace(e.Pos(), "=> %s", T) }() } T = check.typExprInternal(e, def, path) assert(isTyped(T)) check.recordTypeAndValue(e, typexpr, T, nil) return }
func hasType(pkg *grinder.Package, fn *ast.FuncDecl, x, v ast.Expr) bool { // Does x (by itself) default to v's type? // Find the scope in which x appears. xScope := pkg.Info.Scopes[fn.Type] ast.Inspect(fn.Body, func(z ast.Node) bool { if z == nil { return false } if x.Pos() < z.Pos() || z.End() <= x.Pos() { return false } scope := pkg.Info.Scopes[z] if scope != nil { xScope = scope } return true }) xt, err := types.EvalNode(pkg.FileSet, x, pkg.Types, xScope) if err != nil { return false } vt := pkg.Info.Types[v] if types.Identical(xt.Type, vt.Type) { return true } // Might be untyped. vb, ok1 := vt.Type.(*types.Basic) xb, ok2 := xt.Type.(*types.Basic) if ok1 && ok2 { switch xb.Kind() { case types.UntypedInt: return vb.Kind() == types.Int case types.UntypedBool: return vb.Kind() == types.Bool case types.UntypedRune: return vb.Kind() == types.Rune case types.UntypedFloat: return vb.Kind() == types.Float64 case types.UntypedComplex: return vb.Kind() == types.Complex128 case types.UntypedString: return vb.Kind() == types.String } } return false }
// typ type-checks the type expression e and returns its type, or Typ[Invalid]. // If def != nil, e is the type specification for the named type def, declared // in a type declaration, and def.underlying will be set to the type of e before // any components of e are type-checked. // If cycleOk is set, e (or elements of e) may refer to a named type that is not // yet completely set up. // func (check *checker) typ(e ast.Expr, def *Named, cycleOk bool) Type { if trace { check.trace(e.Pos(), "%s", e) check.indent++ } t := check.typInternal(e, def, cycleOk) assert(e != nil && t != nil && isTyped(t)) check.recordTypeAndValue(e, t, nil) if trace { check.indent-- check.trace(e.Pos(), "=> %s", t) } return t }
func (c *compiler) VisitExpr(expr ast.Expr) Value { c.setDebugLine(expr.Pos()) // Before all else, check if we've got a constant expression. // go/types performs constant folding, and we store the value // alongside the expression's type. if constval := c.typeinfo.Values[expr]; constval != nil { return c.NewConstValue(constval, c.typeinfo.Types[expr]) } // nil-literals are parsed to Ident-nodes for some reason, // treat them like constant values here. // TODO nil literals should be represented more appropriately. if identval, valid := expr.(*ast.Ident); valid && identval.Name == "nil" { return c.NewConstValue(exact.MakeUnknown(), c.typeinfo.Types[expr]) } switch x := expr.(type) { case *ast.BinaryExpr: return c.VisitBinaryExpr(x) case *ast.FuncLit: return c.VisitFuncLit(x) case *ast.CompositeLit: return c.VisitCompositeLit(x) case *ast.UnaryExpr: return c.VisitUnaryExpr(x) case *ast.CallExpr: return c.VisitCallExpr(x) case *ast.IndexExpr: return c.VisitIndexExpr(x) case *ast.SelectorExpr: return c.VisitSelectorExpr(x) case *ast.StarExpr: return c.VisitStarExpr(x) case *ast.ParenExpr: return c.VisitExpr(x.X) case *ast.TypeAssertExpr: return c.VisitTypeAssertExpr(x) case *ast.SliceExpr: return c.VisitSliceExpr(x) case *ast.Ident: return c.Resolve(x) } panic(fmt.Sprintf("Unhandled Expr node: %s", reflect.TypeOf(expr))) }
// rawExpr typechecks expression e and initializes x with the expression // value or type. If an error occurred, x.mode is set to invalid. // If hint != nil, it is the type of a composite literal element. // func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind { if trace { check.trace(e.Pos(), "%s", e) check.indent++ } kind := check.exprInternal(x, e, hint) // convert x into a user-friendly set of values record := true var typ Type var val exact.Value switch x.mode { case invalid: typ = Typ[Invalid] record = false // nothing to do case novalue: typ = (*Tuple)(nil) case constant: typ = x.typ val = x.val default: typ = x.typ } assert(x.expr != nil && typ != nil) if isUntyped(typ) { // delay notification until it becomes typed // or until the end of type checking check.untyped[x.expr] = exprInfo{false, typ.(*Basic), val} } else if record { // TODO(gri) ensure that literals always report // their dynamic (never interface) type. // This is not the case yet. check.recordTypeAndValue(e, typ, val) } if trace { check.indent-- check.trace(e.Pos(), "=> %s", x) } return kind }
// compiles an expression func (w *World) compileExpr(e ast.Expr) Expr { switch e := e.(type) { default: panic(err(e.Pos(), "not allowed:", typ(e))) case *ast.Ident: return w.resolve(e.Pos(), e.Name) case *ast.BasicLit: return w.compileBasicLit(e) case *ast.BinaryExpr: return w.compileBinaryExpr(e) case *ast.UnaryExpr: return w.compileUnaryExpr(e) case *ast.CallExpr: return w.compileCallExpr(e) case *ast.ParenExpr: return w.compileExpr(e.X) case *ast.IndexExpr: return w.compileIndexExpr(e) } }
func isCreateFlag(flag ast.Expr) bool { foundCreate := false foundTrunc := false // OR'ing of flags: is O_CREATE on? + or | would be fine; we just look for os.O_CREATE // and don't worry about the actual operator. p := flag.Pos() for { lhs := flag expr, isBinary := flag.(*ast.BinaryExpr) if isBinary { lhs = expr.Y } sel, ok := lhs.(*ast.SelectorExpr) if !ok || !isTopName(sel.X, "os") { return false } switch sel.Sel.Name { case "O_CREATE": foundCreate = true case "O_TRUNC": foundTrunc = true case "O_RDONLY", "O_WRONLY", "O_RDWR": // okay default: // Unexpected flag, like O_APPEND or O_EXCL. // Be conservative and do not rewrite. return false } if !isBinary { break } flag = expr.X } if !foundCreate { return false } if !foundTrunc { warn(p, "rewrote os.Open with O_CREATE but not O_TRUNC to os.Create") } return foundCreate }
// argument typechecks passing an argument arg (if arg != nil) or // x (if arg == nil) to the i'th parameter of the given signature. // If passSlice is set, the argument is followed by ... in the call. // func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand, passSlice bool) { // determine parameter var par *ast.Object n := len(sig.Params) if i < n { par = sig.Params[i] } else if sig.IsVariadic { par = sig.Params[n-1] } else { check.errorf(arg.Pos(), "too many arguments") return } // determine argument var z operand z.mode = variable z.expr = nil // TODO(gri) can we do better here? (for good error messages) z.typ = par.Type.(Type) if arg != nil { check.expr(x, arg, z.typ, -1) } if x.mode == invalid { return // ignore this argument } // check last argument of the form x... if passSlice { if i+1 != n { check.errorf(x.pos(), "can only use ... with matching parameter") return // ignore this argument } // spec: "If the final argument is assignable to a slice type []T, // it may be passed unchanged as the value for a ...T parameter if // the argument is followed by ..." z.typ = &Slice{Elt: z.typ} // change final parameter type to []T } check.assignOperand(&z, x) }
// rawExpr typechecks expression e and initializes x with the expression // value or type. If an error occurred, x.mode is set to invalid. // If hint != nil, it is the type of a composite literal element. // func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind { if trace { check.trace(e.Pos(), "%s", e) check.indent++ defer func() { check.indent-- check.trace(e.Pos(), "=> %s", x) }() } kind := check.exprInternal(x, e, hint) // convert x into a user-friendly set of values // TODO(gri) this code can be simplified var typ Type var val exact.Value switch x.mode { case invalid: typ = Typ[Invalid] case novalue: typ = (*Tuple)(nil) case constant: typ = x.typ val = x.val default: typ = x.typ } assert(x.expr != nil && typ != nil) if isUntyped(typ) { // delay type and value recording until we know the type // or until the end of type checking check.rememberUntyped(x.expr, false, x.mode, typ.(*Basic), val) } else { check.recordTypeAndValue(e, x.mode, typ, val) } return kind }
func (check *checker) argument(sig *Signature, i int, arg ast.Expr) { var par *ast.Object if n := len(sig.Params); i < n { par = sig.Params[i] } else if sig.IsVariadic { par = sig.Params[n-1] } else { check.errorf(arg.Pos(), "too many arguments") return } // TODO(gri) deal with ... last argument var z, x operand z.mode = variable z.expr = nil // TODO(gri) can we do better here? z.typ = par.Type.(Type) // TODO(gri) should become something like checkObj(&z, ...) eventually check.expr(&x, arg, z.typ, -1) if x.mode == invalid { return // ignore this argument } check.assignOperand(&z, &x) }
func (tr *Transformer) matchWildcard(xobj *types.Var, y ast.Expr) bool { name := xobj.Name() if tr.verbose { fmt.Fprintf(os.Stderr, "%s: wildcard %s -> %s?: ", tr.fset.Position(y.Pos()), name, astString(tr.fset, y)) } // Check that y is assignable to the declared type of the param. if yt := tr.info.TypeOf(y); !types.AssignableTo(yt, xobj.Type()) { if tr.verbose { fmt.Fprintf(os.Stderr, "%s not assignable to %s\n", yt, xobj.Type()) } return false } // A wildcard matches any expression. // If it appears multiple times in the pattern, it must match // the same expression each time. if old, ok := tr.env[name]; ok { // found existing binding tr.allowWildcards = false r := tr.matchExpr(old, y) if tr.verbose { fmt.Fprintf(os.Stderr, "%t secondary match, primary was %s\n", r, astString(tr.fset, old)) } tr.allowWildcards = true return r } if tr.verbose { fmt.Fprintf(os.Stderr, "primary match\n") } tr.env[name] = y // record binding return true }
// rawExpr typechecks expression e and initializes x with the expression // value or type. If an error occurred, x.mode is set to invalid. // If hint != nil, it is the type of a composite literal element. // func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind { if trace { check.trace(e.Pos(), "%s", e) check.indent++ defer func() { check.indent-- check.trace(e.Pos(), "=> %s", x) }() } kind := check.exprInternal(x, e, hint) // convert x into a user-friendly set of values var typ Type var val exact.Value switch x.mode { case invalid: typ = Typ[Invalid] case novalue: typ = (*Tuple)(nil) case constant: typ = x.typ val = x.val default: typ = x.typ } assert(x.expr != nil && typ != nil) if isUntyped(typ) { // delay notification until it becomes typed // or until the end of type checking check.untyped[x.expr] = exprInfo{false, typ.(*Basic), val} } else { check.recordTypeAndValue(e, typ, val) } return kind }
func (c *compiler) VisitExpr(expr ast.Expr) Value { c.setDebugLine(expr.Pos()) // Before all else, check if we've got a constant expression. // go/types performs constant folding, and we store the value // alongside the expression's type. if constval := c.typeinfo.Values[expr]; constval != nil { return c.NewConstValue(constval, c.typeinfo.Types[expr]) } switch x := expr.(type) { case *ast.BinaryExpr: return c.VisitBinaryExpr(x) case *ast.FuncLit: return c.VisitFuncLit(x) case *ast.CompositeLit: return c.VisitCompositeLit(x) case *ast.UnaryExpr: return c.VisitUnaryExpr(x) case *ast.CallExpr: return c.VisitCallExpr(x) case *ast.IndexExpr: return c.VisitIndexExpr(x) case *ast.SelectorExpr: return c.VisitSelectorExpr(x) case *ast.StarExpr: return c.VisitStarExpr(x) case *ast.ParenExpr: return c.VisitExpr(x.X) case *ast.TypeAssertExpr: return c.VisitTypeAssertExpr(x) case *ast.SliceExpr: return c.VisitSliceExpr(x) case *ast.Ident: return c.Resolve(x) } panic(fmt.Sprintf("Unhandled Expr node: %s", reflect.TypeOf(expr))) }
func (a *typeCompiler) compileType(x ast.Expr, allowRec bool) Type { switch x := x.(type) { case *ast.BadExpr: // Error already reported by parser a.silentErrors++ return nil case *ast.Ident: return a.compileIdent(x, allowRec) case *ast.ArrayType: return a.compileArrayType(x, allowRec) case *ast.StructType: return a.compileStructType(x, allowRec) case *ast.StarExpr: return a.compilePtrType(x) case *ast.FuncType: fd := a.compileFuncType(x, allowRec) if fd == nil { return nil } return fd.Type case *ast.InterfaceType: return a.compileInterfaceType(x, allowRec) case *ast.MapType: return a.compileMapType(x) case *ast.ChanType: goto notimpl case *ast.ParenExpr: return a.compileType(x.X, allowRec) case *ast.Ellipsis: a.diagAt(x.Pos(), "illegal use of ellipsis") return nil } a.diagAt(x.Pos(), "expression used as type") return nil notimpl: a.diagAt(x.Pos(), "compileType: %T not implemented", x) return nil }