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 = constant.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 (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 } } }
// 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 }
// 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 constant.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 }
// exprInternal contains the core of type checking of expressions. // Must only be called by rawExpr. // func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { // make sure x has a valid state in case of bailout // (was issue 5770) x.mode = invalid x.typ = Typ[Invalid] switch e := e.(type) { case *ast.BadExpr: goto Error // error was reported before case *ast.Ident: check.ident(x, e, nil, nil) case *ast.Ellipsis: // ellipses are handled explicitly where they are legal // (array composite literals and parameter lists) check.error(e.Pos(), "invalid use of '...'") goto Error case *ast.BasicLit: x.setConst(e.Kind, e.Value) if x.mode == invalid { check.invalidAST(e.Pos(), "invalid literal %v", e.Value) goto Error } case *ast.FuncLit: if sig, ok := check.typ(e.Type).(*Signature); ok { // Anonymous functions are considered part of the // init expression/func declaration which contains // them: use existing package-level declaration info. check.funcBody(check.decl, "", sig, e.Body) x.mode = value x.typ = sig } else { check.invalidAST(e.Pos(), "invalid function literal %s", e) goto Error } case *ast.CompositeLit: typ := hint openArray := false if e.Type != nil { // [...]T array types may only appear with composite literals. // Check for them here so we don't have to handle ... in general. typ = nil if atyp, _ := e.Type.(*ast.ArrayType); atyp != nil && atyp.Len != nil { if ellip, _ := atyp.Len.(*ast.Ellipsis); ellip != nil && ellip.Elt == nil { // We have an "open" [...]T array type. // Create a new ArrayType with unknown length (-1) // and finish setting it up after analyzing the literal. typ = &Array{len: -1, elem: check.typ(atyp.Elt)} openArray = true } } if typ == nil { typ = check.typ(e.Type) } } if typ == nil { // TODO(gri) provide better error messages depending on context check.error(e.Pos(), "missing type in composite literal") goto Error } switch typ, _ := deref(typ); utyp := typ.Underlying().(type) { case *Struct: if len(e.Elts) == 0 { break } fields := utyp.fields if _, ok := e.Elts[0].(*ast.KeyValueExpr); ok { // all elements must have keys visited := make([]bool, len(fields)) for _, e := range e.Elts { kv, _ := e.(*ast.KeyValueExpr) if kv == nil { check.error(e.Pos(), "mixture of field:value and value elements in struct literal") continue } key, _ := kv.Key.(*ast.Ident) if key == nil { check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key) continue } i := fieldIndex(utyp.fields, check.pkg, key.Name) if i < 0 { check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name) continue } fld := fields[i] check.recordUse(key, fld) // 0 <= i < len(fields) if visited[i] { check.errorf(kv.Pos(), "duplicate field name %s in struct literal", key.Name) continue } visited[i] = true check.expr(x, kv.Value) etyp := fld.typ if !check.assignment(x, etyp) { if x.mode != invalid { check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp) } continue } } } else { // no element must have a key for i, e := range e.Elts { if kv, _ := e.(*ast.KeyValueExpr); kv != nil { check.error(kv.Pos(), "mixture of field:value and value elements in struct literal") continue } check.expr(x, e) if i >= len(fields) { check.error(x.pos(), "too many values in struct literal") break // cannot continue } // i < len(fields) fld := fields[i] if !fld.Exported() && fld.pkg != check.pkg { check.errorf(x.pos(), "implicit assignment to unexported field %s in %s literal", fld.name, typ) continue } etyp := fld.typ if !check.assignment(x, etyp) { if x.mode != invalid { check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp) } continue } } if len(e.Elts) < len(fields) { check.error(e.Rbrace, "too few values in struct literal") // ok to continue } } case *Array: n := check.indexedElts(e.Elts, utyp.elem, utyp.len) // if we have an "open" [...]T array, set the length now that we know it if openArray { utyp.len = n } case *Slice: check.indexedElts(e.Elts, utyp.elem, -1) case *Map: visited := make(map[interface{}][]Type, len(e.Elts)) for _, e := range e.Elts { kv, _ := e.(*ast.KeyValueExpr) if kv == nil { check.error(e.Pos(), "missing key in map literal") continue } check.exprWithHint(x, kv.Key, utyp.key) if !check.assignment(x, utyp.key) { if x.mode != invalid { check.errorf(x.pos(), "cannot use %s as %s key in map literal", x, utyp.key) } continue } if x.mode == constant_ { duplicate := false // if the key is of interface type, the type is also significant when checking for duplicates if _, ok := utyp.key.Underlying().(*Interface); ok { for _, vtyp := range visited[x.val] { if Identical(vtyp, x.typ) { duplicate = true break } } visited[x.val] = append(visited[x.val], x.typ) } else { _, duplicate = visited[x.val] visited[x.val] = nil } if duplicate { check.errorf(x.pos(), "duplicate key %s in map literal", x.val) continue } } check.exprWithHint(x, kv.Value, utyp.elem) if !check.assignment(x, utyp.elem) { if x.mode != invalid { check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.elem) } continue } } default: // if utyp is invalid, an error was reported before if utyp != Typ[Invalid] { check.errorf(e.Pos(), "invalid composite literal type %s", typ) goto Error } } x.mode = value x.typ = typ case *ast.ParenExpr: kind := check.rawExpr(x, e.X, nil) x.expr = e return kind case *ast.SelectorExpr: check.selector(x, e) case *ast.IndexExpr: check.expr(x, e.X) if x.mode == invalid { goto Error } valid := false length := int64(-1) // valid if >= 0 switch typ := x.typ.Underlying().(type) { case *Basic: if isString(typ) { valid = true if x.mode == constant_ { length = int64(len(constant.StringVal(x.val))) } // an indexed string always yields a byte value // (not a constant) even if the string and the // index are constant x.mode = value x.typ = universeByte // use 'byte' name } case *Array: valid = true length = typ.len if x.mode != variable { x.mode = value } x.typ = typ.elem case *Pointer: if typ, _ := typ.base.Underlying().(*Array); typ != nil { valid = true length = typ.len x.mode = variable x.typ = typ.elem } case *Slice: valid = true x.mode = variable x.typ = typ.elem case *Map: var key operand check.expr(&key, e.Index) if !check.assignment(&key, typ.key) { if key.mode != invalid { check.invalidOp(key.pos(), "cannot use %s as map index of type %s", &key, typ.key) } goto Error } x.mode = mapindex x.typ = typ.elem x.expr = e return expression } if !valid { check.invalidOp(x.pos(), "cannot index %s", x) goto Error } if e.Index == nil { check.invalidAST(e.Pos(), "missing index for %s", x) goto Error } check.index(e.Index, length) // ok to continue case *ast.SliceExpr: check.expr(x, e.X) if x.mode == invalid { goto Error } valid := false length := int64(-1) // valid if >= 0 switch typ := x.typ.Underlying().(type) { case *Basic: if isString(typ) { if slice3(e) { check.invalidOp(x.pos(), "3-index slice of string") goto Error } valid = true if x.mode == constant_ { length = int64(len(constant.StringVal(x.val))) } // spec: "For untyped string operands the result // is a non-constant value of type string." if typ.kind == UntypedString { x.typ = Typ[String] } } case *Array: valid = true length = typ.len if x.mode != variable { check.invalidOp(x.pos(), "cannot slice %s (value not addressable)", x) goto Error } x.typ = &Slice{elem: typ.elem} case *Pointer: if typ, _ := typ.base.Underlying().(*Array); typ != nil { valid = true length = typ.len x.typ = &Slice{elem: typ.elem} } case *Slice: valid = true // x.typ doesn't change } if !valid { check.invalidOp(x.pos(), "cannot slice %s", x) goto Error } x.mode = value // spec: "Only the first index may be omitted; it defaults to 0." if slice3(e) && (e.High == nil || sliceMax(e) == nil) { check.error(e.Rbrack, "2nd and 3rd index required in 3-index slice") goto Error } // check indices var ind [3]int64 for i, expr := range []ast.Expr{e.Low, e.High, sliceMax(e)} { x := int64(-1) switch { case expr != nil: // The "capacity" is only known statically for strings, arrays, // and pointers to arrays, and it is the same as the length for // those types. max := int64(-1) if length >= 0 { max = length + 1 } if t, ok := check.index(expr, max); ok && t >= 0 { x = t } case i == 0: // default is 0 for the first index x = 0 case length >= 0: // default is length (== capacity) otherwise x = length } ind[i] = x } // constant indices must be in range // (check.index already checks that existing indices >= 0) L: for i, x := range ind[:len(ind)-1] { if x > 0 { for _, y := range ind[i+1:] { if y >= 0 && x > y { check.errorf(e.Rbrack, "invalid slice indices: %d > %d", x, y) break L // only report one error, ok to continue } } } } case *ast.TypeAssertExpr: check.expr(x, e.X) if x.mode == invalid { goto Error } xtyp, _ := x.typ.Underlying().(*Interface) if xtyp == nil { check.invalidOp(x.pos(), "%s is not an interface", x) goto Error } // x.(type) expressions are handled explicitly in type switches if e.Type == nil { check.invalidAST(e.Pos(), "use of .(type) outside type switch") goto Error } T := check.typ(e.Type) if T == Typ[Invalid] { goto Error } check.typeAssertion(x.pos(), x, xtyp, T) x.mode = commaok x.typ = T case *ast.CallExpr: return check.call(x, e) case *ast.StarExpr: check.exprOrType(x, e.X) switch x.mode { case invalid: goto Error case typexpr: x.typ = &Pointer{base: x.typ} default: if typ, ok := x.typ.Underlying().(*Pointer); ok { x.mode = variable x.typ = typ.base } else { check.invalidOp(x.pos(), "cannot indirect %s", x) goto Error } } case *ast.UnaryExpr: check.expr(x, e.X) if x.mode == invalid { goto Error } check.unary(x, e, e.Op) if x.mode == invalid { goto Error } if e.Op == token.ARROW { x.expr = e return statement // receive operations may appear in statement context } case *ast.BinaryExpr: check.binary(x, e, e.X, e.Y, e.Op) if x.mode == invalid { goto Error } case *ast.KeyValueExpr: // key:value expressions are handled in composite literals check.invalidAST(e.Pos(), "no key:value expected") goto Error case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType: x.mode = typexpr x.typ = check.typ(e) // Note: rawExpr (caller of exprInternal) will call check.recordTypeAndValue // even though check.typ has already called it. This is fine as both // times the same expression and type are recorded. It is also not a // performance issue because we only reach here for composite literal // types, which are comparatively rare. default: panic(fmt.Sprintf("%s: unknown expression type %T", check.fset.Position(e.Pos()), e)) } // everything went well x.expr = e return expression Error: x.mode = invalid x.expr = e return statement // avoid follow-up errors }
// updateExprType updates the type of x to typ and invokes itself // recursively for the operands of x, depending on expression kind. // If typ is still an untyped and not the final type, updateExprType // only updates the recorded untyped type for x and possibly its // operands. Otherwise (i.e., typ is not an untyped type anymore, // or it is the final type for x), the type and value are recorded. // Also, if x is a constant, it must be representable as a value of typ, // and if x is the (formerly untyped) lhs operand of a non-constant // shift, it must be an integer value. // func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) { old, found := check.untyped[x] if !found { return // nothing to do } // update operands of x if necessary switch x := x.(type) { case *ast.BadExpr, *ast.FuncLit, *ast.CompositeLit, *ast.IndexExpr, *ast.SliceExpr, *ast.TypeAssertExpr, *ast.StarExpr, *ast.KeyValueExpr, *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType: // These expression are never untyped - nothing to do. // The respective sub-expressions got their final types // upon assignment or use. if debug { check.dump("%s: found old type(%s): %s (new: %s)", x.Pos(), x, old.typ, typ) unreachable() } return case *ast.CallExpr: // Resulting in an untyped constant (e.g., built-in complex). // The respective calls take care of calling updateExprType // for the arguments if necessary. case *ast.Ident, *ast.BasicLit, *ast.SelectorExpr: // An identifier denoting a constant, a constant literal, // or a qualified identifier (imported untyped constant). // No operands to take care of. case *ast.ParenExpr: check.updateExprType(x.X, typ, final) case *ast.UnaryExpr: // If x is a constant, the operands were constants. // They don't need to be updated since they never // get "materialized" into a typed value; and they // will be processed at the end of the type check. if old.val != nil { break } check.updateExprType(x.X, typ, final) case *ast.BinaryExpr: if old.val != nil { break // see comment for unary expressions } if isComparison(x.Op) { // The result type is independent of operand types // and the operand types must have final types. } else if isShift(x.Op) { // The result type depends only on lhs operand. // The rhs type was updated when checking the shift. check.updateExprType(x.X, typ, final) } else { // The operand types match the result type. check.updateExprType(x.X, typ, final) check.updateExprType(x.Y, typ, final) } default: unreachable() } // If the new type is not final and still untyped, just // update the recorded type. if !final && isUntyped(typ) { old.typ = typ.Underlying().(*Basic) check.untyped[x] = old return } // Otherwise we have the final (typed or untyped type). // Remove it from the map of yet untyped expressions. delete(check.untyped, x) // If x is the lhs of a shift, its final type must be integer. // We already know from the shift check that it is representable // as an integer if it is a constant. if old.isLhs && !isInteger(typ) { check.invalidOp(x.Pos(), "shifted operand %s (type %s) must be integer", x, typ) return } // Everything's fine, record final type and value for x. check.recordTypeAndValue(x, old.mode, typ, old.val) }
// typExprInternal drives type checking of types. // Must only be called by typExpr. // func (check *Checker) typExprInternal(e ast.Expr, def *Named, path []*TypeName) Type { switch e := e.(type) { case *ast.BadExpr: // ignore - error reported before case *ast.Ident: var x operand check.ident(&x, e, def, path) switch x.mode { case typexpr: typ := x.typ def.setUnderlying(typ) return typ case invalid: // ignore - error reported before case novalue: check.errorf(x.pos(), "%s used as type", &x) default: check.errorf(x.pos(), "%s is not a type", &x) } case *ast.SelectorExpr: var x operand check.selector(&x, e) switch x.mode { case typexpr: typ := x.typ def.setUnderlying(typ) return typ case invalid: // ignore - error reported before case novalue: check.errorf(x.pos(), "%s used as type", &x) default: check.errorf(x.pos(), "%s is not a type", &x) } case *ast.ParenExpr: return check.typExpr(e.X, def, path) case *ast.ArrayType: if e.Len != nil { typ := new(Array) def.setUnderlying(typ) typ.len = check.arrayLength(e.Len) typ.elem = check.typExpr(e.Elt, nil, path) return typ } else { typ := new(Slice) def.setUnderlying(typ) typ.elem = check.typ(e.Elt) return typ } case *ast.StructType: typ := new(Struct) def.setUnderlying(typ) check.structType(typ, e, path) return typ case *ast.StarExpr: typ := new(Pointer) def.setUnderlying(typ) typ.base = check.typ(e.X) return typ case *ast.FuncType: typ := new(Signature) def.setUnderlying(typ) check.funcType(typ, nil, e) return typ case *ast.InterfaceType: typ := new(Interface) def.setUnderlying(typ) check.interfaceType(typ, e, def, path) return typ case *ast.MapType: typ := new(Map) def.setUnderlying(typ) typ.key = check.typ(e.Key) typ.elem = check.typ(e.Value) // spec: "The comparison operators == and != must be fully defined // for operands of the key type; thus the key type must not be a // function, map, or slice." // // Delay this check because it requires fully setup types; // it is safe to continue in any case (was issue 6667). check.delay(func() { if !Comparable(typ.key) { check.errorf(e.Key.Pos(), "invalid map key type %s", typ.key) } }) return typ case *ast.ChanType: typ := new(Chan) def.setUnderlying(typ) dir := SendRecv switch e.Dir { case ast.SEND | ast.RECV: // nothing to do case ast.SEND: dir = SendOnly case ast.RECV: dir = RecvOnly default: check.invalidAST(e.Pos(), "unknown channel direction %d", e.Dir) // ok to continue } typ.dir = dir typ.elem = check.typ(e.Value) return typ default: check.errorf(e.Pos(), "%s is not a type", e) } typ := Typ[Invalid] def.setUnderlying(typ) return typ }
func (p *printer) expr1(expr ast.Expr, prec1, depth int) { p.print(expr.Pos()) switch x := expr.(type) { case *ast.BadExpr: p.print("BadExpr") case *ast.Ident: p.print(x) case *ast.BinaryExpr: if depth < 1 { p.internalError("depth < 1:", depth) depth = 1 } p.binaryExpr(x, prec1, cutoff(x, depth), depth) case *ast.KeyValueExpr: p.expr(x.Key) p.print(x.Colon, token.COLON, blank) p.expr(x.Value) case *ast.StarExpr: const prec = token.UnaryPrec if prec < prec1 { // parenthesis needed p.print(token.LPAREN) p.print(token.MUL) p.expr(x.X) p.print(token.RPAREN) } else { // no parenthesis needed p.print(token.MUL) p.expr(x.X) } case *ast.UnaryExpr: const prec = token.UnaryPrec if prec < prec1 { // parenthesis needed p.print(token.LPAREN) p.expr(x) p.print(token.RPAREN) } else { // no parenthesis needed p.print(x.Op) if x.Op == token.RANGE { // TODO(gri) Remove this code if it cannot be reached. p.print(blank) } p.expr1(x.X, prec, depth) } case *ast.BasicLit: p.print(x) case *ast.FuncLit: p.expr(x.Type) p.adjBlock(p.distanceFrom(x.Type.Pos()), blank, x.Body) case *ast.ParenExpr: if _, hasParens := x.X.(*ast.ParenExpr); hasParens { // don't print parentheses around an already parenthesized expression // TODO(gri) consider making this more general and incorporate precedence levels p.expr0(x.X, depth) } else { p.print(token.LPAREN) p.expr0(x.X, reduceDepth(depth)) // parentheses undo one level of depth p.print(x.Rparen, token.RPAREN) } case *ast.SelectorExpr: p.expr1(x.X, token.HighestPrec, depth) p.print(token.PERIOD) if line := p.lineFor(x.Sel.Pos()); p.pos.IsValid() && p.pos.Line < line { p.print(indent, newline, x.Sel.Pos(), x.Sel, unindent) } else { p.print(x.Sel.Pos(), x.Sel) } case *ast.TypeAssertExpr: p.expr1(x.X, token.HighestPrec, depth) p.print(token.PERIOD, x.Lparen, token.LPAREN) if x.Type != nil { p.expr(x.Type) } else { p.print(token.TYPE) } p.print(x.Rparen, token.RPAREN) case *ast.IndexExpr: // TODO(gri): should treat[] like parentheses and undo one level of depth p.expr1(x.X, token.HighestPrec, 1) p.print(x.Lbrack, token.LBRACK) p.expr0(x.Index, depth+1) p.print(x.Rbrack, token.RBRACK) case *ast.SliceExpr: // TODO(gri): should treat[] like parentheses and undo one level of depth p.expr1(x.X, token.HighestPrec, 1) p.print(x.Lbrack, token.LBRACK) indices := []ast.Expr{x.Low, x.High} if x.Max != nil { indices = append(indices, x.Max) } for i, y := range indices { if i > 0 { // blanks around ":" if both sides exist and either side is a binary expression // TODO(gri) once we have committed a variant of a[i:j:k] we may want to fine- // tune the formatting here x := indices[i-1] if depth <= 1 && x != nil && y != nil && (isBinary(x) || isBinary(y)) { p.print(blank, token.COLON, blank) } else { p.print(token.COLON) } } if y != nil { p.expr0(y, depth+1) } } p.print(x.Rbrack, token.RBRACK) case *ast.CallExpr: if len(x.Args) > 1 { depth++ } if _, ok := x.Fun.(*ast.FuncType); ok { // conversions to literal function types require parentheses around the type p.print(token.LPAREN) p.expr1(x.Fun, token.HighestPrec, depth) p.print(token.RPAREN) } else { p.expr1(x.Fun, token.HighestPrec, depth) } p.print(x.Lparen, token.LPAREN) if x.Ellipsis.IsValid() { p.exprList(x.Lparen, x.Args, depth, 0, x.Ellipsis) p.print(x.Ellipsis, token.ELLIPSIS) if x.Rparen.IsValid() && p.lineFor(x.Ellipsis) < p.lineFor(x.Rparen) { p.print(token.COMMA, formfeed) } } else { p.exprList(x.Lparen, x.Args, depth, commaTerm, x.Rparen) } p.print(x.Rparen, token.RPAREN) case *ast.CompositeLit: // composite literal elements that are composite literals themselves may have the type omitted if x.Type != nil { p.expr1(x.Type, token.HighestPrec, depth) } p.print(x.Lbrace, token.LBRACE) p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace) // do not insert extra line break following a /*-style comment // before the closing '}' as it might break the code if there // is no trailing ',' mode := noExtraLinebreak // do not insert extra blank following a /*-style comment // before the closing '}' unless the literal is empty if len(x.Elts) > 0 { mode |= noExtraBlank } p.print(mode, x.Rbrace, token.RBRACE, mode) case *ast.Ellipsis: p.print(token.ELLIPSIS) if x.Elt != nil { p.expr(x.Elt) } case *ast.ArrayType: p.print(token.LBRACK) if x.Len != nil { p.expr(x.Len) } p.print(token.RBRACK) p.expr(x.Elt) case *ast.StructType: p.print(token.STRUCT) p.fieldList(x.Fields, true, x.Incomplete) case *ast.FuncType: p.print(token.FUNC) p.signature(x.Params, x.Results) case *ast.InterfaceType: p.print(token.INTERFACE) p.fieldList(x.Methods, false, x.Incomplete) case *ast.MapType: p.print(token.MAP, token.LBRACK) p.expr(x.Key) p.print(token.RBRACK) p.expr(x.Value) case *ast.ChanType: switch x.Dir { case ast.SEND | ast.RECV: p.print(token.CHAN) case ast.RECV: p.print(token.ARROW, token.CHAN) // x.Arrow and x.Pos() are the same case ast.SEND: p.print(token.CHAN, x.Arrow, token.ARROW) } p.print(blank) p.expr(x.Value) default: panic("unreachable") } return }