// zeroConst returns a new "zero" constant of the specified type, // which must not be an array or struct type: the zero values of // aggregates are well-defined but cannot be represented by Const. // func zeroConst(t types.Type) *Const { switch t := t.(type) { case *types.Basic: switch { case t.Info()&types.IsBoolean != 0: return NewConst(exact.MakeBool(false), t) case t.Info()&types.IsNumeric != 0: return NewConst(exact.MakeInt64(0), t) case t.Info()&types.IsString != 0: return NewConst(exact.MakeString(""), t) case t.Kind() == types.UnsafePointer: fallthrough case t.Kind() == types.UntypedNil: return nilConst(t) default: panic(fmt.Sprint("zeroConst for unexpected type:", t)) } case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature: return nilConst(t) case *types.Named: return NewConst(zeroConst(t.Underlying()).Value, t) case *types.Array, *types.Struct, *types.Tuple: panic(fmt.Sprint("zeroConst applied to aggregate:", t)) } panic(fmt.Sprint("zeroConst: unexpected ", t)) }
// number = int_lit [ "p" int_lit ] . // func (p *gcParser) parseNumber() (x operand) { x.mode = constant // mantissa mant := exact.MakeFromLiteral(p.parseInt(), token.INT) assert(mant != nil) if p.lit == "p" { // exponent (base 2) p.next() exp, err := strconv.ParseInt(p.parseInt(), 10, 0) if err != nil { p.error(err) } if exp < 0 { denom := exact.MakeInt64(1) denom = exact.Shift(denom, token.SHL, uint(-exp)) x.typ = Typ[UntypedFloat] x.val = exact.BinaryOp(mant, token.QUO, denom) return } if exp > 0 { mant = exact.Shift(mant, token.SHL, uint(exp)) } x.typ = Typ[UntypedFloat] x.val = mant return } x.typ = Typ[UntypedInt] x.val = mant return }
// number = int_lit [ "p" int_lit ] . // func (p *parser) parseNumber() (typ *types.Basic, val exact.Value) { // mantissa mant := exact.MakeFromLiteral(p.parseInt(), token.INT) if mant == nil { panic("invalid mantissa") } if p.lit == "p" { // exponent (base 2) p.next() exp, err := strconv.ParseInt(p.parseInt(), 10, 0) if err != nil { p.error(err) } if exp < 0 { denom := exact.MakeInt64(1) denom = exact.Shift(denom, token.SHL, uint(-exp)) typ = types.Typ[types.UntypedFloat] val = exact.BinaryOp(mant, token.QUO, denom) return } if exp > 0 { mant = exact.Shift(mant, token.SHL, uint(exp)) } typ = types.Typ[types.UntypedFloat] val = mant return } typ = types.Typ[types.UntypedInt] val = mant return }
func (p *importer) ufloat() exact.Value { exp := p.int() x := exact.MakeFromBytes(p.bytes()) switch { case exp < 0: d := exact.Shift(exact.MakeInt64(1), token.SHL, uint(-exp)) x = exact.BinaryOp(x, token.QUO, d) case exp > 0: x = exact.Shift(x, token.SHL, uint(exp)) } return x }
func (p *importer) fraction() exact.Value { sign := p.int() if sign == 0 { return exact.MakeInt64(0) } x := exact.BinaryOp(p.ufloat(), token.QUO, p.ufloat()) if sign < 0 { x = exact.UnaryOp(token.SUB, x, 0) } return x }
func (p *importer) value() exact.Value { switch kind := exact.Kind(p.int()); kind { case falseTag: return exact.MakeBool(false) case trueTag: return exact.MakeBool(true) case int64Tag: return exact.MakeInt64(p.int64()) case floatTag: return p.float() case fractionTag: return p.fraction() case complexTag: re := p.fraction() im := p.fraction() return exact.BinaryOp(re, token.ADD, exact.MakeImag(im)) case stringTag: return exact.MakeString(p.string()) default: panic(fmt.Sprintf("unexpected value kind %d", kind)) } }
// intConst returns an untyped integer constant that evaluates to i. func intConst(i int64) *Const { return NewConst(exact.MakeInt64(i), types.Typ[types.UntypedInt]) }
func (c *funcContext) newInt(i int, t types.Type) *ast.BasicLit { lit := &ast.BasicLit{Kind: token.INT} c.p.info.Types[lit] = types.TypeAndValue{Type: t, Value: exact.MakeInt64(int64(i))} return lit }
// collectObjects collects all file and package objects and inserts them // into their respective scopes. It also performs imports and associates // methods with receiver base type names. func (check *checker) collectObjects() { pkg := check.pkg importer := check.conf.Import if importer == nil { if DefaultImport == nil { panic(`no Config.Import or DefaultImport (missing import _ "code.google.com/p/go.tools/go/gcimporter"?)`) } importer = DefaultImport } // pkgImports is the set of packages already imported by any package file seen // so far. Used to avoid duplicate entries in pkg.imports. Allocate and populate // it (pkg.imports may not be empty if we are checking test files incrementally). var pkgImports = make(map[*Package]bool) for _, imp := range pkg.imports { pkgImports[imp] = true } for fileNo, file := range check.files { // The package identifier denotes the current package, // but there is no corresponding package object. check.recordDef(file.Name, nil) fileScope := check.fileScopes[fileNo] for _, decl := range file.Decls { switch d := decl.(type) { case *ast.BadDecl: // ignore case *ast.GenDecl: var last *ast.ValueSpec // last ValueSpec with type or init exprs seen for iota, spec := range d.Specs { switch s := spec.(type) { case *ast.ImportSpec: // import package var imp *Package path, err := validatedImportPath(s.Path.Value) if err != nil { check.errorf(s.Path.Pos(), "invalid import path (%s)", err) continue } if path == "C" && check.conf.FakeImportC { // TODO(gri) shouldn't create a new one each time imp = NewPackage("C", "C") imp.fake = true } else { var err error imp, err = importer(check.conf.Packages, path) if imp == nil && err == nil { err = errors.New("Config.Import returned nil but no error") } if err != nil { check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err) continue } } // add package to list of explicit imports // (this functionality is provided as a convenience // for clients; it is not needed for type-checking) if !pkgImports[imp] { pkgImports[imp] = true if imp != Unsafe { pkg.imports = append(pkg.imports, imp) } } // local name overrides imported package name name := imp.name if s.Name != nil { name = s.Name.Name if name == "init" { check.errorf(s.Name.Pos(), "cannot declare init - must be func") continue } } obj := NewPkgName(s.Pos(), imp, name) if s.Name != nil { // in a dot-import, the dot represents the package check.recordDef(s.Name, obj) } else { check.recordImplicit(s, obj) } // add import to file scope if name == "." { // merge imported scope with file scope for _, obj := range imp.scope.elems { // A package scope may contain non-exported objects, // do not import them! if obj.Exported() { check.declare(fileScope, nil, obj) check.recordImplicit(s, obj) } } // add position to set of dot-import positions for this file // (this is only needed for "imported but not used" errors) posSet := check.dotImports[fileNo] if posSet == nil { posSet = make(map[*Package]token.Pos) check.dotImports[fileNo] = posSet } posSet[imp] = s.Pos() } else { // declare imported package object in file scope check.declare(fileScope, nil, obj) } case *ast.ValueSpec: switch d.Tok { case token.CONST: // determine which initialization expressions to use switch { case s.Type != nil || len(s.Values) > 0: last = s case last == nil: last = new(ast.ValueSpec) // make sure last exists } // declare all constants for i, name := range s.Names { obj := NewConst(name.Pos(), pkg, name.Name, nil, exact.MakeInt64(int64(iota))) var init ast.Expr if i < len(last.Values) { init = last.Values[i] } d := &declInfo{file: fileScope, typ: last.Type, init: init} check.declarePkgObj(name, obj, d) } check.arityMatch(s, last) case token.VAR: lhs := make([]*Var, len(s.Names)) // If there's exactly one rhs initializer, use // the same declInfo d1 for all lhs variables // so that each lhs variable depends on the same // rhs initializer (n:1 var declaration). var d1 *declInfo if len(s.Values) == 1 { // The lhs elements are only set up after the for loop below, // but that's ok because declareVar only collects the declInfo // for a later phase. d1 = &declInfo{file: fileScope, lhs: lhs, typ: s.Type, init: s.Values[0]} } // declare all variables for i, name := range s.Names { obj := NewVar(name.Pos(), pkg, name.Name, nil) lhs[i] = obj d := d1 if d == nil { // individual assignments var init ast.Expr if i < len(s.Values) { init = s.Values[i] } d = &declInfo{file: fileScope, typ: s.Type, init: init} } check.declarePkgObj(name, obj, d) } check.arityMatch(s, nil) default: check.invalidAST(s.Pos(), "invalid token %s", d.Tok) } case *ast.TypeSpec: obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil) check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, typ: s.Type}) default: check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s) } } case *ast.FuncDecl: name := d.Name.Name obj := NewFunc(d.Name.Pos(), pkg, name, nil) if d.Recv == nil { // regular function if name == "init" { // don't declare init functions in the package scope - they are invisible obj.parent = pkg.scope check.recordDef(d.Name, obj) // init functions must have a body if d.Body == nil { check.softErrorf(obj.pos, "missing function body") } } else { check.declare(pkg.scope, d.Name, obj) } } else { // method check.recordDef(d.Name, obj) // Associate method with receiver base type name, if possible. // Ignore methods that have an invalid receiver, or a blank _ // receiver name. They will be type-checked later, with regular // functions. if list := d.Recv.List; len(list) > 0 { typ := list[0].Type if ptr, _ := typ.(*ast.StarExpr); ptr != nil { typ = ptr.X } if base, _ := typ.(*ast.Ident); base != nil && base.Name != "_" { check.assocMethod(base.Name, obj) } } } info := &declInfo{file: fileScope, fdecl: d} check.objMap[obj] = info default: check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) } } } // verify that objects in package and file scopes have different names for _, scope := range check.fileScopes { for _, obj := range scope.elems { if alt := pkg.scope.Lookup(obj.Name()); alt != nil { check.errorf(alt.Pos(), "%s already declared in this file through import of package %s", obj.Name(), obj.Pkg().Name()) } } } }
func (c *funcContext) translateStmt(stmt ast.Stmt, label string) { c.Write([]byte{'\b'}) binary.Write(c, binary.BigEndian, uint32(stmt.Pos())) switch s := stmt.(type) { case *ast.BlockStmt: c.translateStmtList(s.List) case *ast.IfStmt: c.printLabel(label) if s.Init != nil { c.translateStmt(s.Init, "") } var caseClauses []ast.Stmt ifStmt := s for { caseClauses = append(caseClauses, &ast.CaseClause{List: []ast.Expr{ifStmt.Cond}, Body: ifStmt.Body.List}) switch elseStmt := ifStmt.Else.(type) { case *ast.IfStmt: if elseStmt.Init != nil { caseClauses = append(caseClauses, &ast.CaseClause{List: nil, Body: []ast.Stmt{elseStmt}}) break } ifStmt = elseStmt continue case *ast.BlockStmt: caseClauses = append(caseClauses, &ast.CaseClause{List: nil, Body: elseStmt.List}) case *ast.EmptyStmt, nil: // no else clause default: panic(fmt.Sprintf("Unhandled else: %T\n", elseStmt)) } break } c.translateBranchingStmt(caseClauses, false, c.translateExpr, nil, "", c.hasGoto[s]) case *ast.SwitchStmt: if s.Init != nil { c.translateStmt(s.Init, "") } translateCond := func(cond ast.Expr) *expression { return c.translateExpr(cond) } if s.Tag != nil { refVar := c.newVariable("_ref") c.Printf("%s = %s;", refVar, c.translateExpr(s.Tag)) translateCond = func(cond ast.Expr) *expression { refIdent := c.newIdent(refVar, c.p.info.Types[s.Tag].Type) return c.translateExpr(&ast.BinaryExpr{ X: refIdent, Op: token.EQL, Y: cond, }) } } c.translateBranchingStmt(s.Body.List, true, translateCond, nil, label, c.hasGoto[s]) case *ast.TypeSwitchStmt: if s.Init != nil { c.translateStmt(s.Init, "") } var expr ast.Expr var typeSwitchVar string switch a := s.Assign.(type) { case *ast.AssignStmt: expr = a.Rhs[0].(*ast.TypeAssertExpr).X typeSwitchVar = c.newVariable(a.Lhs[0].(*ast.Ident).Name) for _, caseClause := range s.Body.List { c.p.objectVars[c.p.info.Implicits[caseClause]] = typeSwitchVar } case *ast.ExprStmt: expr = a.X.(*ast.TypeAssertExpr).X } refVar := c.newVariable("_ref") typeVar := c.newVariable("_type") c.Printf("%s = %s;", refVar, c.translateExpr(expr)) c.Printf("%s = %s !== null ? %s.constructor : null;", typeVar, refVar, refVar) translateCond := func(cond ast.Expr) *expression { return c.formatExpr("%s", c.typeCheck(typeVar, c.p.info.Types[cond].Type)) } printCaseBodyPrefix := func(conds []ast.Expr) { if typeSwitchVar == "" { return } value := refVar if len(conds) == 1 { t := c.p.info.Types[conds[0]].Type if _, isInterface := t.Underlying().(*types.Interface); !isInterface && !types.Identical(t, types.Typ[types.UntypedNil]) { value += ".go$val" } } c.Printf("%s = %s;", typeSwitchVar, value) } c.translateBranchingStmt(s.Body.List, true, translateCond, printCaseBodyPrefix, label, c.hasGoto[s]) case *ast.ForStmt: if s.Init != nil { c.translateStmt(s.Init, "") } cond := "true" if s.Cond != nil { cond = c.translateExpr(s.Cond).String() } c.translateLoopingStmt(cond, s.Body, nil, func() { if s.Post != nil { c.translateStmt(s.Post, "") } }, label, c.hasGoto[s]) case *ast.RangeStmt: refVar := c.newVariable("_ref") c.Printf("%s = %s;", refVar, c.translateExpr(s.X)) iVar := c.newVariable("_i") c.Printf("%s = 0;", iVar) switch t := c.p.info.Types[s.X].Type.Underlying().(type) { case *types.Basic: runeVar := c.newVariable("_rune") c.translateLoopingStmt(iVar+" < "+refVar+".length", s.Body, func() { c.Printf("%s = go$decodeRune(%s, %s);", runeVar, refVar, iVar) if !isBlank(s.Value) { c.Printf("%s", c.translateAssign(s.Value, runeVar+"[0]")) } if !isBlank(s.Key) { c.Printf("%s", c.translateAssign(s.Key, iVar)) } }, func() { c.Printf("%s += %s[1];", iVar, runeVar) }, label, c.hasGoto[s]) case *types.Map: keysVar := c.newVariable("_keys") c.Printf("%s = go$keys(%s);", keysVar, refVar) c.translateLoopingStmt(iVar+" < "+keysVar+".length", s.Body, func() { entryVar := c.newVariable("_entry") c.Printf("%s = %s[%s[%s]];", entryVar, refVar, keysVar, iVar) if !isBlank(s.Value) { c.Printf("%s", c.translateAssign(s.Value, entryVar+".v")) } if !isBlank(s.Key) { c.Printf("%s", c.translateAssign(s.Key, entryVar+".k")) } }, func() { c.Printf("%s++;", iVar) }, label, c.hasGoto[s]) case *types.Array, *types.Pointer, *types.Slice: var length string switch t2 := t.(type) { case *types.Array: length = fmt.Sprintf("%d", t2.Len()) case *types.Pointer: length = fmt.Sprintf("%d", t2.Elem().Underlying().(*types.Array).Len()) case *types.Slice: length = refVar + ".length" } c.translateLoopingStmt(iVar+" < "+length, s.Body, func() { if !isBlank(s.Value) { indexExpr := &ast.IndexExpr{ X: c.newIdent(refVar, t), Index: c.newIdent(iVar, types.Typ[types.Int]), } et := elemType(t) c.p.info.Types[indexExpr] = types.TypeAndValue{Type: et} c.Printf("%s", c.translateAssign(s.Value, c.translateImplicitConversion(indexExpr, et).String())) } if !isBlank(s.Key) { c.Printf("%s", c.translateAssign(s.Key, iVar)) } }, func() { c.Printf("%s++;", iVar) }, label, c.hasGoto[s]) case *types.Chan: c.printLabel(label) // skip default: panic("") } case *ast.BranchStmt: c.printLabel(label) labelSuffix := "" data := c.flowDatas[""] if s.Label != nil { labelSuffix = " " + s.Label.Name data = c.flowDatas[s.Label.Name] } switch s.Tok { case token.BREAK: c.PrintCond(data.endCase == 0, fmt.Sprintf("break%s;", labelSuffix), fmt.Sprintf("go$s = %d; continue;", data.endCase)) case token.CONTINUE: data.postStmt() c.PrintCond(data.beginCase == 0, fmt.Sprintf("continue%s;", labelSuffix), fmt.Sprintf("go$s = %d; continue;", data.beginCase)) case token.GOTO: c.PrintCond(false, "goto "+s.Label.Name, fmt.Sprintf("go$s = %d; continue;", c.labelCases[s.Label.Name])) case token.FALLTHROUGH: // handled in CaseClause default: panic("Unhandled branch statment: " + s.Tok.String()) } case *ast.ReturnStmt: c.printLabel(label) results := s.Results if c.resultNames != nil { if len(s.Results) != 0 { c.translateStmt(&ast.AssignStmt{ Lhs: c.resultNames, Tok: token.ASSIGN, Rhs: s.Results, }, "") } results = c.resultNames } switch len(results) { case 0: c.Printf("return;") case 1: if c.sig.Results().Len() > 1 { c.Printf("return %s;", c.translateExpr(results[0])) break } v := c.translateImplicitConversion(results[0], c.sig.Results().At(0).Type()) c.delayedOutput = nil c.Printf("return %s;", v) default: values := make([]string, len(results)) for i, result := range results { values[i] = c.translateImplicitConversion(result, c.sig.Results().At(i).Type()).String() } c.delayedOutput = nil c.Printf("return [%s];", strings.Join(values, ", ")) } case *ast.DeferStmt: c.printLabel(label) if ident, isIdent := s.Call.Fun.(*ast.Ident); isIdent { if builtin, isBuiltin := c.p.info.Uses[ident].(*types.Builtin); isBuiltin { if builtin.Name() == "recover" { c.Printf("go$deferred.push({ fun: go$recover, args: [] });") return } args := make([]ast.Expr, len(s.Call.Args)) for i, arg := range s.Call.Args { args[i] = c.newIdent(c.newVariable("_arg"), c.p.info.Types[arg].Type) } call := c.translateExpr(&ast.CallExpr{ Fun: s.Call.Fun, Args: args, Ellipsis: s.Call.Ellipsis, }) c.Printf("go$deferred.push({ fun: function(%s) { %s; }, args: [%s] });", strings.Join(c.translateExprSlice(args, nil), ", "), call, strings.Join(c.translateExprSlice(s.Call.Args, nil), ", ")) return } } sig := c.p.info.Types[s.Call.Fun].Type.Underlying().(*types.Signature) args := strings.Join(c.translateArgs(sig, s.Call.Args, s.Call.Ellipsis.IsValid()), ", ") if sel, isSelector := s.Call.Fun.(*ast.SelectorExpr); isSelector { obj := c.p.info.Selections[sel].Obj() if !obj.Exported() { c.p.dependencies[obj] = true } c.Printf(`go$deferred.push({ recv: %s, method: "%s", args: [%s] });`, c.translateExpr(sel.X), sel.Sel.Name, args) return } c.Printf("go$deferred.push({ fun: %s, args: [%s] });", c.translateExpr(s.Call.Fun), args) case *ast.AssignStmt: c.printLabel(label) if s.Tok != token.ASSIGN && s.Tok != token.DEFINE { var op token.Token switch s.Tok { case token.ADD_ASSIGN: op = token.ADD case token.SUB_ASSIGN: op = token.SUB case token.MUL_ASSIGN: op = token.MUL case token.QUO_ASSIGN: op = token.QUO case token.REM_ASSIGN: op = token.REM case token.AND_ASSIGN: op = token.AND case token.OR_ASSIGN: op = token.OR case token.XOR_ASSIGN: op = token.XOR case token.SHL_ASSIGN: op = token.SHL case token.SHR_ASSIGN: op = token.SHR case token.AND_NOT_ASSIGN: op = token.AND_NOT default: panic(s.Tok) } var parts []string lhs := s.Lhs[0] switch l := lhs.(type) { case *ast.IndexExpr: lhsVar := c.newVariable("_lhs") indexVar := c.newVariable("_index") parts = append(parts, lhsVar+" = "+c.translateExpr(l.X).String()+";") parts = append(parts, indexVar+" = "+c.translateExpr(l.Index).String()+";") lhs = &ast.IndexExpr{ X: c.newIdent(lhsVar, c.p.info.Types[l.X].Type), Index: c.newIdent(indexVar, c.p.info.Types[l.Index].Type), } c.p.info.Types[lhs] = c.p.info.Types[l] case *ast.StarExpr: lhsVar := c.newVariable("_lhs") parts = append(parts, lhsVar+" = "+c.translateExpr(l.X).String()+";") lhs = &ast.StarExpr{ X: c.newIdent(lhsVar, c.p.info.Types[l.X].Type), } c.p.info.Types[lhs] = c.p.info.Types[l] case *ast.SelectorExpr: v := hasCallVisitor{c.p.info, false} ast.Walk(&v, l.X) if v.hasCall { lhsVar := c.newVariable("_lhs") parts = append(parts, lhsVar+" = "+c.translateExpr(l.X).String()+";") lhs = &ast.SelectorExpr{ X: c.newIdent(lhsVar, c.p.info.Types[l.X].Type), Sel: l.Sel, } c.p.info.Types[lhs] = c.p.info.Types[l] c.p.info.Selections[lhs.(*ast.SelectorExpr)] = c.p.info.Selections[l] } } parenExpr := &ast.ParenExpr{X: s.Rhs[0]} c.p.info.Types[parenExpr] = c.p.info.Types[s.Rhs[0]] binaryExpr := &ast.BinaryExpr{ X: lhs, Op: op, Y: parenExpr, } c.p.info.Types[binaryExpr] = c.p.info.Types[s.Lhs[0]] parts = append(parts, c.translateAssign(lhs, c.translateExpr(binaryExpr).String())) c.Printf("%s", strings.Join(parts, " ")) return } if s.Tok == token.DEFINE { for _, lhs := range s.Lhs { if !isBlank(lhs) { c.p.info.Types[lhs] = types.TypeAndValue{Type: c.p.info.Defs[lhs.(*ast.Ident)].Type()} } } } removeParens := func(e ast.Expr) ast.Expr { for { if p, isParen := e.(*ast.ParenExpr); isParen { e = p.X continue } break } return e } switch { case len(s.Lhs) == 1 && len(s.Rhs) == 1: lhs := removeParens(s.Lhs[0]) if isBlank(lhs) { v := hasCallVisitor{c.p.info, false} ast.Walk(&v, s.Rhs[0]) if v.hasCall { c.Printf("%s;", c.translateExpr(s.Rhs[0]).String()) } return } c.Printf("%s", c.translateAssign(lhs, c.translateImplicitConversion(s.Rhs[0], c.p.info.Types[s.Lhs[0]].Type).String())) case len(s.Lhs) > 1 && len(s.Rhs) == 1: tupleVar := c.newVariable("_tuple") out := tupleVar + " = " + c.translateExpr(s.Rhs[0]).String() + ";" tuple := c.p.info.Types[s.Rhs[0]].Type.(*types.Tuple) for i, lhs := range s.Lhs { lhs = removeParens(lhs) if !isBlank(lhs) { out += " " + c.translateAssign(lhs, c.translateImplicitConversion(c.newIdent(fmt.Sprintf("%s[%d]", tupleVar, i), tuple.At(i).Type()), c.p.info.Types[s.Lhs[i]].Type).String()) } } c.Printf("%s", out) case len(s.Lhs) == len(s.Rhs): parts := make([]string, len(s.Rhs)) for i, rhs := range s.Rhs { parts[i] = c.translateImplicitConversion(rhs, c.p.info.Types[s.Lhs[i]].Type).String() } tupleVar := c.newVariable("_tuple") out := tupleVar + " = [" + strings.Join(parts, ", ") + "];" for i, lhs := range s.Lhs { lhs = removeParens(lhs) if !isBlank(lhs) { out += " " + c.translateAssign(lhs, fmt.Sprintf("%s[%d]", tupleVar, i)) } } c.Printf("%s", out) default: panic("Invalid arity of AssignStmt.") } case *ast.IncDecStmt: t := c.p.info.Types[s.X].Type if iExpr, isIExpr := s.X.(*ast.IndexExpr); isIExpr { switch u := c.p.info.Types[iExpr.X].Type.Underlying().(type) { case *types.Array: t = u.Elem() case *types.Slice: t = u.Elem() case *types.Map: t = u.Elem() } } tok := token.ADD_ASSIGN if s.Tok == token.DEC { tok = token.SUB_ASSIGN } one := &ast.BasicLit{ Kind: token.INT, Value: "1", } c.p.info.Types[one] = types.TypeAndValue{Type: t, Value: exact.MakeInt64(1)} c.translateStmt(&ast.AssignStmt{ Lhs: []ast.Expr{s.X}, Tok: tok, Rhs: []ast.Expr{one}, }, label) case *ast.ExprStmt: c.printLabel(label) c.Printf("%s;", c.translateExpr(s.X).String()) case *ast.DeclStmt: c.printLabel(label) decl := s.Decl.(*ast.GenDecl) switch decl.Tok { case token.VAR: for _, spec := range s.Decl.(*ast.GenDecl).Specs { valueSpec := spec.(*ast.ValueSpec) lhs := make([]ast.Expr, len(valueSpec.Names)) for i, name := range valueSpec.Names { lhs[i] = name } rhs := valueSpec.Values isTuple := false if len(rhs) == 1 { _, isTuple = c.p.info.Types[rhs[0]].Type.(*types.Tuple) } for len(rhs) < len(lhs) && !isTuple { rhs = append(rhs, nil) } c.translateStmt(&ast.AssignStmt{ Lhs: lhs, Tok: token.DEFINE, Rhs: rhs, }, "") } case token.TYPE: for _, spec := range decl.Specs { o := c.p.info.Defs[spec.(*ast.TypeSpec).Name].(*types.TypeName) c.translateType(o, false) c.initType(o) } case token.CONST: // skip, constants are inlined } case *ast.LabeledStmt: c.printLabel(label) c.translateStmt(s.Stmt, s.Label.Name) case *ast.SelectStmt: c.printLabel(label) c.Printf(`go$notSupported("select");`) case *ast.GoStmt: c.printLabel(label) c.Printf(`go$notSupported("go");`) case *ast.SendStmt: c.printLabel(label) c.Printf(`go$notSupported("send");`) case *ast.EmptyStmt: // skip default: panic(fmt.Sprintf("Unhandled statement: %T\n", s)) } }
// builtin typechecks a call to a built-in and returns the result via x. // If the call has type errors, the returned x is marked as invalid. // func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) { args := call.Args // declare before goto's var arg0 ast.Expr // first argument, if present // check argument count n := len(args) msg := "" bin := predeclaredFuncs[id] if n < bin.nargs { msg = "not enough" } else if !bin.variadic && n > bin.nargs { msg = "too many" } if msg != "" { check.invalidOp(call.Pos(), msg+" arguments for %s (expected %d, found %d)", call, bin.nargs, n) goto Error } // common case: evaluate first argument if present; // if it is an expression, x has the expression value if n > 0 { arg0 = args[0] switch id { case _Make, _New, _Print, _Println, _Offsetof, _Trace: // respective cases below do the work default: // argument must be an expression check.expr(x, arg0) if x.mode == invalid { goto Error } } } switch id { case _Append: if _, ok := x.typ.Underlying().(*Slice); !ok { check.invalidArg(x.pos(), "%s is not a typed slice", x) goto Error } resultTyp := x.typ for _, arg := range args[1:] { check.expr(x, arg) if x.mode == invalid { goto Error } // TODO(gri) check assignability } x.mode = value x.typ = resultTyp case _Cap, _Len: mode := invalid var val exact.Value switch typ := implicitArrayDeref(x.typ.Underlying()).(type) { case *Basic: if isString(typ) && id == _Len { if x.mode == constant { mode = constant val = exact.MakeInt64(int64(len(exact.StringVal(x.val)))) } else { mode = value } } case *Array: mode = value // spec: "The expressions len(s) and cap(s) are constants // if the type of s is an array or pointer to an array and // the expression s does not contain channel receives or // function calls; in this case s is not evaluated." if !check.containsCallsOrReceives(arg0) { mode = constant val = exact.MakeInt64(typ.len) } case *Slice, *Chan: mode = value case *Map: if id == _Len { mode = value } } if mode == invalid { check.invalidArg(x.pos(), "%s for %s", x, bin.name) goto Error } x.mode = mode x.typ = Typ[Int] x.val = val case _Close: ch, ok := x.typ.Underlying().(*Chan) if !ok { check.invalidArg(x.pos(), "%s is not a channel", x) goto Error } if ch.dir&ast.SEND == 0 { check.invalidArg(x.pos(), "%s must not be a receive-only channel", x) goto Error } x.mode = novalue case _Complex: if !check.complexArg(x) { goto Error } var y operand check.expr(&y, args[1]) if y.mode == invalid { goto Error } if !check.complexArg(&y) { goto Error } check.convertUntyped(x, y.typ) if x.mode == invalid { goto Error } check.convertUntyped(&y, x.typ) if y.mode == invalid { goto Error } if !IsIdentical(x.typ, y.typ) { check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ) goto Error } typ := x.typ.Underlying().(*Basic) if x.mode == constant && y.mode == constant { x.val = exact.BinaryOp(x.val, token.ADD, exact.MakeImag(y.val)) } else { x.mode = value } switch typ.kind { case Float32: x.typ = Typ[Complex64] case Float64: x.typ = Typ[Complex128] case UntypedInt, UntypedRune, UntypedFloat: if x.mode == constant { typ = defaultType(typ).(*Basic) x.typ = Typ[UntypedComplex] } else { // untyped but not constant; probably because one // operand is a non-constant shift of untyped lhs typ = Typ[Float64] x.typ = Typ[Complex128] } default: check.invalidArg(x.pos(), "float32 or float64 arguments expected") goto Error } if x.mode != constant { // The arguments have now their final types, which at run- // time will be materialized. Update the expression trees. // If the current types are untyped, the materialized type // is the respective default type. // (If the result is constant, the arguments are never // materialized and there is nothing to do.) check.updateExprType(args[0], typ, true) check.updateExprType(args[1], typ, true) } case _Copy: var y operand check.expr(&y, args[1]) if y.mode == invalid { goto Error } var dst, src Type if t, ok := x.typ.Underlying().(*Slice); ok { dst = t.elt } switch t := y.typ.Underlying().(type) { case *Basic: if isString(y.typ) { src = Typ[Byte] } case *Slice: src = t.elt } if dst == nil || src == nil { check.invalidArg(x.pos(), "copy expects slice arguments; found %s and %s", x, &y) goto Error } if !IsIdentical(dst, src) { check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src) goto Error } x.mode = value x.typ = Typ[Int] case _Delete: m, ok := x.typ.Underlying().(*Map) if !ok { check.invalidArg(x.pos(), "%s is not a map", x) goto Error } check.expr(x, args[1]) if x.mode == invalid { goto Error } if !x.isAssignableTo(check.conf, m.key) { check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key) goto Error } x.mode = novalue case _Imag, _Real: if !isComplex(x.typ) { check.invalidArg(x.pos(), "%s must be a complex number", x) goto Error } if x.mode == constant { if id == _Real { x.val = exact.Real(x.val) } else { x.val = exact.Imag(x.val) } } else { x.mode = value } k := Invalid switch x.typ.Underlying().(*Basic).kind { case Complex64: k = Float32 case Complex128: k = Float64 case UntypedComplex: k = UntypedFloat default: unreachable() } x.typ = Typ[k] case _Make: resultTyp := check.typ(arg0, nil, false) if resultTyp == Typ[Invalid] { goto Error } var min int // minimum number of arguments switch resultTyp.Underlying().(type) { case *Slice: min = 2 case *Map, *Chan: min = 1 default: check.invalidArg(arg0.Pos(), "cannot make %s; type must be slice, map, or channel", arg0) goto Error } if n := len(args); n < min || min+1 < n { check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, n) goto Error } var sizes []int64 // constant integer arguments, if any for _, arg := range args[1:] { if s, ok := check.index(arg, -1); ok && s >= 0 { sizes = append(sizes, s) } } if len(sizes) == 2 && sizes[0] > sizes[1] { check.invalidArg(args[1].Pos(), "length and capacity swapped") // safe to continue } x.mode = variable x.typ = resultTyp case _New: resultTyp := check.typ(arg0, nil, false) if resultTyp == Typ[Invalid] { goto Error } x.mode = variable x.typ = &Pointer{base: resultTyp} case _Panic: x.mode = novalue case _Print, _Println: for _, arg := range args { check.expr(x, arg) if x.mode == invalid { goto Error } } x.mode = novalue case _Recover: x.mode = value x.typ = new(Interface) case _Alignof: x.mode = constant x.val = exact.MakeInt64(check.conf.alignof(x.typ)) x.typ = Typ[Uintptr] case _Offsetof: arg, ok := unparen(arg0).(*ast.SelectorExpr) if !ok { check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0) goto Error } check.expr(x, arg.X) if x.mode == invalid { goto Error } base := derefStructPtr(x.typ) sel := arg.Sel.Name obj, index, indirect := LookupFieldOrMethod(base, check.pkg, arg.Sel.Name) switch obj.(type) { case nil: check.invalidArg(x.pos(), "%s has no single field %s", base, sel) goto Error case *Func: check.invalidArg(arg0.Pos(), "%s is a method value", arg0) goto Error } if indirect { check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, base) goto Error } // TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)? check.recordSelection(arg, FieldVal, base, obj, index, false) offs := check.conf.offsetof(base, index) x.mode = constant x.val = exact.MakeInt64(offs) x.typ = Typ[Uintptr] case _Sizeof: x.mode = constant x.val = exact.MakeInt64(check.conf.sizeof(x.typ)) x.typ = Typ[Uintptr] case _Assert: // assert(pred) causes a typechecker error if pred is false. // The result of assert is the value of pred if there is no error. // Note: assert is only available in self-test mode. if x.mode != constant || !isBoolean(x.typ) { check.invalidArg(x.pos(), "%s is not a boolean constant", x) goto Error } if x.val.Kind() != exact.Bool { check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x) goto Error } if !exact.BoolVal(x.val) { check.errorf(call.Pos(), "%s failed", call) // compile-time assertion failure - safe to continue } case _Trace: // trace(x, y, z, ...) dumps the positions, expressions, and // values of its arguments. The result of trace is the value // of the first argument. // Note: trace is only available in self-test mode. if len(args) == 0 { check.dump("%s: trace() without arguments", call.Pos()) x.mode = novalue x.expr = call return } var t operand x1 := x for _, arg := range args { check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T)) check.dump("%s: %s", x1.pos(), x1) x1 = &t // use incoming x only for first argument } default: unreachable() } x.expr = call return Error: x.mode = invalid x.expr = call }
func (check *checker) resolveFiles(files []*ast.File) { pkg := check.pkg // Phase 1: Pre-declare all package-level objects so that they can be found // independent of source order. Associate methods with receiver // base type names. var ( fileScope *Scope // current file scope objList []Object // list of package-level objects to type-check objMap = make(map[Object]declInfo) // declaration info for each package-level object ) // declare declares obj in the package scope, records its ident -> obj mapping, // and updates objList and objMap. The object must not be a function or method. declare := func(ident *ast.Ident, obj Object, typ, init ast.Expr) { assert(ident.Name == obj.Name()) // spec: "A package-scope or file-scope identifier with name init // may only be declared to be a function with this (func()) signature." if ident.Name == "init" { check.errorf(ident.Pos(), "cannot declare init - must be func") return } check.declareObj(pkg.scope, ident, obj) objList = append(objList, obj) objMap[obj] = declInfo{fileScope, typ, init, nil} } importer := check.conf.Import if importer == nil { importer = GcImport } var ( seenPkgs = make(map[*Package]bool) // imported packages that have been seen already fileScopes []*Scope // file scope for each file dotImports []map[*Package]token.Pos // positions of dot-imports for each file ) for _, file := range files { // The package identifier denotes the current package, // but there is no corresponding package object. check.recordObject(file.Name, nil) fileScope = NewScope(pkg.scope) check.recordScope(file, fileScope) fileScopes = append(fileScopes, fileScope) dotImports = append(dotImports, nil) // element (map) is lazily allocated for _, decl := range file.Decls { switch d := decl.(type) { case *ast.BadDecl: // ignore case *ast.GenDecl: var last *ast.ValueSpec // last ValueSpec with type or init exprs seen for iota, spec := range d.Specs { switch s := spec.(type) { case *ast.ImportSpec: // import package var imp *Package path, _ := strconv.Unquote(s.Path.Value) if path == "C" && check.conf.FakeImportC { // TODO(gri) shouldn't create a new one each time imp = NewPackage("C", "C", NewScope(nil)) imp.fake = true } else { var err error imp, err = importer(check.conf.Packages, path) if imp == nil && err == nil { err = errors.New("Config.Import returned nil but no error") } if err != nil { check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err) continue } } // add package to list of explicit imports // (this functionality is provided as a convenience // for clients; it is not needed for type-checking) if !seenPkgs[imp] { seenPkgs[imp] = true if imp != Unsafe { pkg.imports = append(pkg.imports, imp) } } // local name overrides imported package name name := imp.name if s.Name != nil { name = s.Name.Name if name == "init" { check.errorf(s.Name.Pos(), "cannot declare init - must be func") continue } } obj := NewPkgName(s.Pos(), imp, name) if s.Name != nil { // in a dot-import, the dot represents the package check.recordObject(s.Name, obj) } else { check.recordImplicit(s, obj) } // add import to file scope if name == "." { // merge imported scope with file scope for _, obj := range imp.scope.elems { // A package scope may contain non-exported objects, // do not import them! if obj.IsExported() { // Note: This will change each imported object's scope! // May be an issue for type aliases. check.declareObj(fileScope, nil, obj) check.recordImplicit(s, obj) } } // add position to set of dot-import positions for this file // (this is only needed for "imported but not used" errors) posSet := dotImports[len(dotImports)-1] if posSet == nil { posSet = make(map[*Package]token.Pos) dotImports[len(dotImports)-1] = posSet } posSet[imp] = s.Pos() } else { // declare imported package object in file scope check.declareObj(fileScope, nil, obj) } case *ast.ValueSpec: switch d.Tok { case token.CONST: // determine which initialization expressions to use switch { case s.Type != nil || len(s.Values) > 0: last = s case last == nil: last = new(ast.ValueSpec) // make sure last exists } // declare all constants for i, name := range s.Names { obj := NewConst(name.Pos(), pkg, name.Name, nil, exact.MakeInt64(int64(iota))) var init ast.Expr if i < len(last.Values) { init = last.Values[i] } declare(name, obj, last.Type, init) } check.arityMatch(s, last) case token.VAR: // declare all variables lhs := make([]*Var, len(s.Names)) for i, name := range s.Names { obj := NewVar(name.Pos(), pkg, name.Name, nil) lhs[i] = obj var init ast.Expr switch len(s.Values) { case len(s.Names): // lhs and rhs match init = s.Values[i] case 1: // rhs must be a multi-valued expression // (lhs may not be fully set up yet, but // that's fine because declare simply collects // the information for later processing.) init = &multiExpr{lhs, s.Values, nil} default: if i < len(s.Values) { init = s.Values[i] } } declare(name, obj, s.Type, init) } check.arityMatch(s, nil) default: check.invalidAST(s.Pos(), "invalid token %s", d.Tok) } case *ast.TypeSpec: obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil) declare(s.Name, obj, s.Type, nil) default: check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s) } } case *ast.FuncDecl: name := d.Name.Name obj := NewFunc(d.Name.Pos(), pkg, name, nil) if d.Recv == nil { // regular function if name == "init" { // don't declare init functions in the package scope - they are invisible obj.parent = pkg.scope check.recordObject(d.Name, obj) // init functions must have a body if d.Body == nil { check.errorf(obj.pos, "missing function body") // ok to continue } } else { check.declareObj(pkg.scope, d.Name, obj) } } else { // Associate method with receiver base type name, if possible. // Ignore methods that have an invalid receiver, or a blank _ // receiver name. They will be type-checked later, with regular // functions. if list := d.Recv.List; len(list) > 0 { typ := list[0].Type if ptr, _ := typ.(*ast.StarExpr); ptr != nil { typ = ptr.X } if base, _ := typ.(*ast.Ident); base != nil && base.Name != "_" { check.methods[base.Name] = append(check.methods[base.Name], obj) } } } objList = append(objList, obj) objMap[obj] = declInfo{fileScope, nil, nil, d} default: check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) } } } // Phase 2: Verify that objects in package and file scopes have different names. for _, scope := range fileScopes { for _, obj := range scope.elems { if alt := pkg.scope.Lookup(obj.Name()); alt != nil { check.errorf(alt.Pos(), "%s already declared in this file through import of package %s", obj.Name(), obj.Pkg().Name()) } } } // Phase 3: Typecheck all objects in objList, but not function bodies. check.objMap = objMap // indicate that we are checking global declarations (objects may not have a type yet) for _, obj := range objList { if obj.Type() == nil { check.objDecl(obj, nil, false) } } // done with package-level declarations check.objMap = nil objList = nil // At this point we may have a non-empty check.methods map; this means that not all // entries were deleted at the end of typeDecl because the respective receiver base // types were not declared. In that case, an error was reported when declaring those // methods. We can now safely discard this map. check.methods = nil // Phase 4: Typecheck all functions bodies. // Note: funcList may grow while iterating through it - cannot use range clause. for i := 0; i < len(check.funcList); i++ { f := check.funcList[i] if trace { s := "<function literal>" if f.obj != nil { s = f.obj.name } fmt.Println("---", s) } check.topScope = f.sig.scope // open function scope check.funcSig = f.sig check.labels = nil // lazily allocated check.stmtList(f.body.List, false) if f.sig.results.Len() > 0 && !check.isTerminating(f.body, "") { check.errorf(f.body.Rbrace, "missing return") } // spec: "It is illegal to define a label that is never used." if check.labels != nil { for _, obj := range check.labels.elems { if l := obj.(*Label); !l.used { check.errorf(l.pos, "%s defined but not used", l.name) } } } } // Phase 5: Check for declared but not used packages and variables. // Note: must happen after checking all functions because closures may affect outer scopes // spec: "It is illegal (...) to directly import a package without referring to // any of its exported identifiers. To import a package solely for its side-effects // (initialization), use the blank identifier as explicit package name." for i, scope := range fileScopes { var usedDotImports map[*Package]bool // lazily allocated for _, obj := range scope.elems { switch obj := obj.(type) { case *PkgName: // Unused "blank imports" are automatically ignored // since _ identifiers are not entered into scopes. if !obj.used { check.errorf(obj.pos, "%q imported but not used", obj.pkg.path) } default: // All other objects in the file scope must be dot- // imported. If an object was used, mark its package // as used. if obj.isUsed() { if usedDotImports == nil { usedDotImports = make(map[*Package]bool) } usedDotImports[obj.Pkg()] = true } } } // Iterate through all dot-imports for this file and // check if the corresponding package was used. for pkg, pos := range dotImports[i] { if !usedDotImports[pkg] { check.errorf(pos, "%q imported but not used", pkg.path) } } } // Each set of implicitly declared lhs variables in a type switch acts collectively // as a single lhs variable. If any one was 'used', all of them are 'used'. Handle // them before the general analysis. for _, vars := range check.lhsVarsList { // len(vars) > 0 var used bool for _, v := range vars { if v.used { used = true } v.used = true // avoid later error } if !used { v := vars[0] check.errorf(v.pos, "%s declared but not used", v.name) } } // spec: "Implementation restriction: A compiler may make it illegal to // declare a variable inside a function body if the variable is never used." for _, f := range check.funcList { check.usage(f.sig.scope) } }
// ConstDecl = "const" ExportedName [ Type ] "=" Literal . // Literal = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit . // bool_lit = "true" | "false" . // complex_lit = "(" float_lit "+" float_lit "i" ")" . // rune_lit = "(" int_lit "+" int_lit ")" . // string_lit = `"` { unicode_char } `"` . // func (p *gcParser) parseConstDecl() { p.expectKeyword("const") pkg, name := p.parseExportedName() obj := declConst(pkg, name) var x operand if p.tok != '=' { obj.typ = p.parseType() } p.expect('=') switch p.tok { case scanner.Ident: // bool_lit if p.lit != "true" && p.lit != "false" { p.error("expected true or false") } x.typ = Typ[UntypedBool] x.val = exact.MakeBool(p.lit == "true") p.next() case '-', scanner.Int: // int_lit x = p.parseNumber() case '(': // complex_lit or rune_lit p.next() if p.tok == scanner.Char { p.next() p.expect('+') x = p.parseNumber() x.typ = Typ[UntypedRune] p.expect(')') break } re := p.parseNumber() p.expect('+') im := p.parseNumber() p.expectKeyword("i") p.expect(')') x.typ = Typ[UntypedComplex] // TODO(gri) fix this _, _ = re, im x.val = exact.MakeInt64(0) case scanner.Char: // rune_lit x.setConst(token.CHAR, p.lit) p.next() case scanner.String: // string_lit x.setConst(token.STRING, p.lit) p.next() default: p.errorf("expected literal got %s", scanner.TokenString(p.tok)) } if obj.typ == nil { obj.typ = x.typ } assert(x.val != nil) obj.val = x.val }
// intConst returns an 'int' constant that evaluates to i. // (i is an int64 in case the host is narrower than the target.) func intConst(i int64) *Const { return NewConst(exact.MakeInt64(i), tInt) }
func (check *Checker) declStmt(decl ast.Decl) { pkg := check.pkg switch d := decl.(type) { case *ast.BadDecl: // ignore case *ast.GenDecl: var last *ast.ValueSpec // last ValueSpec with type or init exprs seen for iota, spec := range d.Specs { switch s := spec.(type) { case *ast.ValueSpec: switch d.Tok { case token.CONST: // determine which init exprs to use switch { case s.Type != nil || len(s.Values) > 0: last = s case last == nil: last = new(ast.ValueSpec) // make sure last exists } // declare all constants lhs := make([]*Const, len(s.Names)) for i, name := range s.Names { obj := NewConst(name.Pos(), pkg, name.Name, nil, exact.MakeInt64(int64(iota))) lhs[i] = obj var init ast.Expr if i < len(last.Values) { init = last.Values[i] } check.constDecl(obj, last.Type, init) } check.arityMatch(s, last) for i, name := range s.Names { check.declare(check.scope, name, lhs[i]) } case token.VAR: lhs0 := make([]*Var, len(s.Names)) for i, name := range s.Names { lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil) } // initialize all variables for i, obj := range lhs0 { var lhs []*Var var init ast.Expr switch len(s.Values) { case len(s.Names): // lhs and rhs match init = s.Values[i] case 1: // rhs is expected to be a multi-valued expression lhs = lhs0 init = s.Values[0] default: if i < len(s.Values) { init = s.Values[i] } } check.varDecl(obj, lhs, s.Type, init) if len(s.Values) == 1 { // If we have a single lhs variable we are done either way. // If we have a single rhs expression, it must be a multi- // valued expression, in which case handling the first lhs // variable will cause all lhs variables to have a type // assigned, and we are done as well. if debug { for _, obj := range lhs0 { assert(obj.typ != nil) } } break } } check.arityMatch(s, nil) // declare all variables // (only at this point are the variable scopes (parents) set) for i, name := range s.Names { check.declare(check.scope, name, lhs0[i]) } default: check.invalidAST(s.Pos(), "invalid token %s", d.Tok) } case *ast.TypeSpec: obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil) check.declare(check.scope, s.Name, obj) check.typeDecl(obj, s.Type, nil, nil) default: check.invalidAST(s.Pos(), "const, type, or var declaration expected") } } default: check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) } }
// builtin type-checks a call to the built-in specified by id and // returns true if the call is valid, with *x holding the result; // but x.expr is not set. If the call is invalid, the result is // false, and *x is undefined. // func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ bool) { // append is the only built-in that permits the use of ... for the last argument bin := predeclaredFuncs[id] if call.Ellipsis.IsValid() && id != _Append { check.invalidOp(call.Ellipsis, "invalid use of ... with built-in %s", bin.name) check.use(call.Args...) return } // For len(x) and cap(x) we need to know if x contains any function calls or // receive operations. Save/restore current setting and set hasCallOrRecv to // false for the evaluation of x so that we can check it afterwards. // Note: We must do this _before_ calling unpack because unpack evaluates the // first argument before we even call arg(x, 0)! if id == _Len || id == _Cap { defer func(b bool) { check.hasCallOrRecv = b }(check.hasCallOrRecv) check.hasCallOrRecv = false } // determine actual arguments var arg getter nargs := len(call.Args) switch id { default: // make argument getter arg, nargs, _ = unpack(func(x *operand, i int) { check.expr(x, call.Args[i]) }, nargs, false) // evaluate first argument, if present if nargs > 0 { arg(x, 0) if x.mode == invalid { return } } case _Make, _New, _Offsetof, _Trace: // arguments require special handling } // check argument count { msg := "" if nargs < bin.nargs { msg = "not enough" } else if !bin.variadic && nargs > bin.nargs { msg = "too many" } if msg != "" { check.invalidOp(call.Rparen, "%s arguments for %s (expected %d, found %d)", msg, call, bin.nargs, nargs) return } } switch id { case _Append: // append(s S, x ...T) S, where T is the element type of S // spec: "The variadic function append appends zero or more values x to s of type // S, which must be a slice type, and returns the resulting slice, also of type S. // The values x are passed to a parameter of type ...T where T is the element type // of S and the respective parameter passing rules apply." S := x.typ var T Type if s, _ := S.Underlying().(*Slice); s != nil { T = s.elem } else { check.invalidArg(x.pos(), "%s is not a slice", x) return } // remember arguments that have been evaluated already alist := []operand{*x} // spec: "As a special case, append also accepts a first argument assignable // to type []byte with a second argument of string type followed by ... . // This form appends the bytes of the string. if nargs == 2 && call.Ellipsis.IsValid() && x.assignableTo(check.conf, NewSlice(UniverseByte)) { arg(x, 1) if x.mode == invalid { return } if isString(x.typ) { if check.Types != nil { sig := makeSig(S, S, x.typ) sig.variadic = true check.recordBuiltinType(call.Fun, sig) } x.mode = value x.typ = S break } alist = append(alist, *x) // fallthrough } // check general case by creating custom signature sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature sig.variadic = true check.arguments(x, call, sig, func(x *operand, i int) { // only evaluate arguments that have not been evaluated before if i < len(alist) { *x = alist[i] return } arg(x, i) }, nargs) // ok to continue even if check.arguments reported errors x.mode = value x.typ = S if check.Types != nil { check.recordBuiltinType(call.Fun, sig) } case _Cap, _Len: // cap(x) // len(x) mode := invalid var typ Type var val exact.Value switch typ = implicitArrayDeref(x.typ.Underlying()); t := typ.(type) { case *Basic: if isString(t) && id == _Len { if x.mode == constant { mode = constant val = exact.MakeInt64(int64(len(exact.StringVal(x.val)))) } else { mode = value } } case *Array: mode = value // spec: "The expressions len(s) and cap(s) are constants // if the type of s is an array or pointer to an array and // the expression s does not contain channel receives or // function calls; in this case s is not evaluated." if !check.hasCallOrRecv { mode = constant val = exact.MakeInt64(t.len) } case *Slice, *Chan: mode = value case *Map: if id == _Len { mode = value } } if mode == invalid { check.invalidArg(x.pos(), "%s for %s", x, bin.name) return } x.mode = mode x.typ = Typ[Int] x.val = val if check.Types != nil && mode != constant { check.recordBuiltinType(call.Fun, makeSig(x.typ, typ)) } case _Close: // close(c) c, _ := x.typ.Underlying().(*Chan) if c == nil { check.invalidArg(x.pos(), "%s is not a channel", x) return } if c.dir == RecvOnly { check.invalidArg(x.pos(), "%s must not be a receive-only channel", x) return } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, c)) } case _Complex: // complex(x, y realT) complexT if !check.complexArg(x) { return } var y operand arg(&y, 1) if y.mode == invalid { return } if !check.complexArg(&y) { return } check.convertUntyped(x, y.typ) if x.mode == invalid { return } check.convertUntyped(&y, x.typ) if y.mode == invalid { return } if !Identical(x.typ, y.typ) { check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ) return } if x.mode == constant && y.mode == constant { x.val = exact.BinaryOp(x.val, token.ADD, exact.MakeImag(y.val)) } else { x.mode = value } realT := x.typ complexT := Typ[Invalid] switch realT.Underlying().(*Basic).kind { case Float32: complexT = Typ[Complex64] case Float64: complexT = Typ[Complex128] case UntypedInt, UntypedRune, UntypedFloat: if x.mode == constant { realT = defaultType(realT).(*Basic) complexT = Typ[UntypedComplex] } else { // untyped but not constant; probably because one // operand is a non-constant shift of untyped lhs realT = Typ[Float64] complexT = Typ[Complex128] } default: check.invalidArg(x.pos(), "float32 or float64 arguments expected") return } x.typ = complexT if check.Types != nil && x.mode != constant { check.recordBuiltinType(call.Fun, makeSig(complexT, realT, realT)) } if x.mode != constant { // The arguments have now their final types, which at run- // time will be materialized. Update the expression trees. // If the current types are untyped, the materialized type // is the respective default type. // (If the result is constant, the arguments are never // materialized and there is nothing to do.) check.updateExprType(x.expr, realT, true) check.updateExprType(y.expr, realT, true) } case _Copy: // copy(x, y []T) int var dst Type if t, _ := x.typ.Underlying().(*Slice); t != nil { dst = t.elem } var y operand arg(&y, 1) if y.mode == invalid { return } var src Type switch t := y.typ.Underlying().(type) { case *Basic: if isString(y.typ) { src = UniverseByte } case *Slice: src = t.elem } if dst == nil || src == nil { check.invalidArg(x.pos(), "copy expects slice arguments; found %s and %s", x, &y) return } if !Identical(dst, src) { check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src) return } if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ, y.typ)) } x.mode = value x.typ = Typ[Int] case _Delete: // delete(m, k) m, _ := x.typ.Underlying().(*Map) if m == nil { check.invalidArg(x.pos(), "%s is not a map", x) return } arg(x, 1) // k if x.mode == invalid { return } if !x.assignableTo(check.conf, m.key) { check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key) return } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, m, m.key)) } case _Imag, _Real: // imag(complexT) realT // real(complexT) realT if !isComplex(x.typ) { check.invalidArg(x.pos(), "%s must be a complex number", x) return } if x.mode == constant { if id == _Real { x.val = exact.Real(x.val) } else { x.val = exact.Imag(x.val) } } else { x.mode = value } var k BasicKind switch x.typ.Underlying().(*Basic).kind { case Complex64: k = Float32 case Complex128: k = Float64 case UntypedComplex: k = UntypedFloat default: unreachable() } if check.Types != nil && x.mode != constant { check.recordBuiltinType(call.Fun, makeSig(Typ[k], x.typ)) } x.typ = Typ[k] case _Make: // make(T, n) // make(T, n, m) // (no argument evaluated yet) arg0 := call.Args[0] T := check.typ(arg0) if T == Typ[Invalid] { return } var min int // minimum number of arguments switch T.Underlying().(type) { case *Slice: min = 2 case *Map, *Chan: min = 1 default: check.invalidArg(arg0.Pos(), "cannot make %s; type must be slice, map, or channel", arg0) return } if nargs < min || min+1 < nargs { check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, nargs) return } var sizes []int64 // constant integer arguments, if any for _, arg := range call.Args[1:] { if s, ok := check.index(arg, -1); ok && s >= 0 { sizes = append(sizes, s) } } if len(sizes) == 2 && sizes[0] > sizes[1] { check.invalidArg(call.Args[1].Pos(), "length and capacity swapped") // safe to continue } x.mode = value x.typ = T if check.Types != nil { params := [...]Type{T, Typ[Int], Typ[Int]} check.recordBuiltinType(call.Fun, makeSig(x.typ, params[:1+len(sizes)]...)) } case _New: // new(T) // (no argument evaluated yet) T := check.typ(call.Args[0]) if T == Typ[Invalid] { return } x.mode = value x.typ = &Pointer{base: T} if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(x.typ, T)) } case _Panic: // panic(x) T := new(Interface) if !check.assignment(x, T) { assert(x.mode == invalid) return } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, T)) } case _Print, _Println: // print(x, y, ...) // println(x, y, ...) var params []Type if nargs > 0 { params = make([]Type, nargs) for i := 0; i < nargs; i++ { if i > 0 { arg(x, i) // first argument already evaluated } if !check.assignment(x, nil) { assert(x.mode == invalid) return } params[i] = x.typ } } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, params...)) } case _Recover: // recover() interface{} x.mode = value x.typ = new(Interface) if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(x.typ)) } case _Alignof: // unsafe.Alignof(x T) uintptr if !check.assignment(x, nil) { assert(x.mode == invalid) return } x.mode = constant x.val = exact.MakeInt64(check.conf.alignof(x.typ)) x.typ = Typ[Uintptr] // result is constant - no need to record signature case _Offsetof: // unsafe.Offsetof(x T) uintptr, where x must be a selector // (no argument evaluated yet) arg0 := call.Args[0] selx, _ := unparen(arg0).(*ast.SelectorExpr) if selx == nil { check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0) check.use(arg0) return } check.expr(x, selx.X) if x.mode == invalid { return } base := derefStructPtr(x.typ) sel := selx.Sel.Name obj, index, indirect := LookupFieldOrMethod(base, false, check.pkg, sel) switch obj.(type) { case nil: check.invalidArg(x.pos(), "%s has no single field %s", base, sel) return case *Func: // TODO(gri) Using derefStructPtr may result in methods being found // that don't actually exist. An error either way, but the error // message is confusing. See: http://play.golang.org/p/al75v23kUy , // but go/types reports: "invalid argument: x.m is a method value". check.invalidArg(arg0.Pos(), "%s is a method value", arg0) return } if indirect { check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, base) return } // TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)? check.recordSelection(selx, FieldVal, base, obj, index, false) offs := check.conf.offsetof(base, index) x.mode = constant x.val = exact.MakeInt64(offs) x.typ = Typ[Uintptr] // result is constant - no need to record signature case _Sizeof: // unsafe.Sizeof(x T) uintptr if !check.assignment(x, nil) { assert(x.mode == invalid) return } x.mode = constant x.val = exact.MakeInt64(check.conf.sizeof(x.typ)) x.typ = Typ[Uintptr] // result is constant - no need to record signature case _Assert: // assert(pred) causes a typechecker error if pred is false. // The result of assert is the value of pred if there is no error. // Note: assert is only available in self-test mode. if x.mode != constant || !isBoolean(x.typ) { check.invalidArg(x.pos(), "%s is not a boolean constant", x) return } if x.val.Kind() != exact.Bool { check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x) return } if !exact.BoolVal(x.val) { check.errorf(call.Pos(), "%s failed", call) // compile-time assertion failure - safe to continue } // result is constant - no need to record signature case _Trace: // trace(x, y, z, ...) dumps the positions, expressions, and // values of its arguments. The result of trace is the value // of the first argument. // Note: trace is only available in self-test mode. // (no argument evaluated yet) if nargs == 0 { check.dump("%s: trace() without arguments", call.Pos()) x.mode = novalue break } var t operand x1 := x for _, arg := range call.Args { check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T)) check.dump("%s: %s", x1.pos(), x1) x1 = &t // use incoming x only for first argument } // trace is only available in test mode - no need to record signature default: unreachable() } return true }
res := NewVar(token.NoPos, nil, "", Typ[String]) sig := &Signature{results: NewTuple(res)} err := NewFunc(token.NoPos, nil, "Error", sig) typ := &Named{underlying: NewInterface([]*Func{err}, nil), complete: true} sig.recv = NewVar(token.NoPos, nil, "", typ) def(NewTypeName(token.NoPos, nil, "error", typ)) } var predeclaredConsts = [...]struct { name string kind BasicKind val exact.Value }{ {"true", UntypedBool, exact.MakeBool(true)}, {"false", UntypedBool, exact.MakeBool(false)}, {"iota", UntypedInt, exact.MakeInt64(0)}, } func defPredeclaredConsts() { for _, c := range predeclaredConsts { def(NewConst(token.NoPos, nil, c.name, Typ[c.kind], c.val)) } } func defPredeclaredNil() { def(&Nil{object{name: "nil", typ: Typ[UntypedNil]}}) } // A builtinId is the id of a builtin function. type builtinId int
// intLiteral returns an untyped integer literal that evaluates to i. func intLiteral(i int64) *Literal { return newLiteral(exact.MakeInt64(i), types.Typ[types.UntypedInt]) }
func (check *checker) declStmt(decl ast.Decl) { pkg := check.pkg switch d := decl.(type) { case *ast.BadDecl: // ignore case *ast.GenDecl: var last *ast.ValueSpec // last ValueSpec with type or init exprs seen for iota, spec := range d.Specs { switch s := spec.(type) { case *ast.ValueSpec: switch d.Tok { case token.CONST: // determine which init exprs to use switch { case s.Type != nil || len(s.Values) > 0: last = s case last == nil: last = new(ast.ValueSpec) // make sure last exists } // declare all constants lhs := make([]*Const, len(s.Names)) for i, name := range s.Names { obj := NewConst(name.Pos(), pkg, name.Name, nil, exact.MakeInt64(int64(iota))) lhs[i] = obj var init ast.Expr if i < len(last.Values) { init = last.Values[i] } check.constDecl(obj, last.Type, init) } check.arityMatch(s, last) for i, name := range s.Names { check.declareObj(check.topScope, name, lhs[i]) } case token.VAR: // For varDecl called with a multiExpr we need the fully // initialized lhs. Compute it in a separate pre-pass. lhs := make([]*Var, len(s.Names)) for i, name := range s.Names { lhs[i] = NewVar(name.Pos(), pkg, name.Name, nil) } // declare all variables for i, obj := range lhs { var init ast.Expr switch len(s.Values) { case len(s.Names): // lhs and rhs match init = s.Values[i] case 1: // rhs is expected to be a multi-valued expression init = &multiExpr{lhs, s.Values, nil} default: if i < len(s.Values) { init = s.Values[i] } } check.varDecl(obj, s.Type, init) } check.arityMatch(s, nil) for i, name := range s.Names { check.declareObj(check.topScope, name, lhs[i]) } default: check.invalidAST(s.Pos(), "invalid token %s", d.Tok) } case *ast.TypeSpec: obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil) check.declareObj(check.topScope, s.Name, obj) check.typeDecl(obj, s.Type, nil, false) default: check.invalidAST(s.Pos(), "const, type, or var declaration expected") } } default: check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) } }
func (c *PkgContext) translateSimpleStmt(stmt ast.Stmt) string { switch s := stmt.(type) { case *ast.AssignStmt: if s.Tok != token.ASSIGN && s.Tok != token.DEFINE { var op token.Token switch s.Tok { case token.ADD_ASSIGN: op = token.ADD case token.SUB_ASSIGN: op = token.SUB case token.MUL_ASSIGN: op = token.MUL case token.QUO_ASSIGN: op = token.QUO case token.REM_ASSIGN: op = token.REM case token.AND_ASSIGN: op = token.AND case token.OR_ASSIGN: op = token.OR case token.XOR_ASSIGN: op = token.XOR case token.SHL_ASSIGN: op = token.SHL case token.SHR_ASSIGN: op = token.SHR case token.AND_NOT_ASSIGN: op = token.AND_NOT default: panic(s.Tok) } var parts []string var lhs ast.Expr switch l := s.Lhs[0].(type) { case *ast.IndexExpr: lhsVar := c.newVariable("lhs") indexVar := c.newVariable("index") parts = append(parts, lhsVar+" = "+c.translateExpr(l.X)) parts = append(parts, indexVar+" = "+c.translateExpr(l.Index)) lhs = &ast.IndexExpr{ X: c.newIdent(lhsVar, c.info.Types[l.X]), Index: c.newIdent(indexVar, c.info.Types[l.Index]), } c.info.Types[lhs] = c.info.Types[l] case *ast.StarExpr: lhsVar := c.newVariable("lhs") parts = append(parts, lhsVar+" = "+c.translateExpr(l.X)) lhs = &ast.StarExpr{ X: c.newIdent(lhsVar, c.info.Types[l.X]), } c.info.Types[lhs] = c.info.Types[l] default: lhs = l } parenExpr := &ast.ParenExpr{ X: s.Rhs[0], } c.info.Types[parenExpr] = c.info.Types[s.Rhs[0]] binaryExpr := &ast.BinaryExpr{ X: lhs, Op: op, Y: parenExpr, } c.info.Types[binaryExpr] = c.info.Types[s.Lhs[0]] parts = append(parts, c.translateAssign(lhs, c.translateExpr(binaryExpr))) return strings.Join(parts, ", ") } if s.Tok == token.DEFINE { for _, lhs := range s.Lhs { if !isBlank(lhs) { c.info.Types[lhs] = c.info.Objects[lhs.(*ast.Ident)].Type() } } } switch { case len(s.Lhs) == 1 && len(s.Rhs) == 1: return c.translateAssign(s.Lhs[0], c.translateExprToType(s.Rhs[0], c.info.Types[s.Lhs[0]])) case len(s.Lhs) > 1 && len(s.Rhs) == 1: out := "Go$tuple = " + c.translateExpr(s.Rhs[0]) tuple := c.info.Types[s.Rhs[0]].(*types.Tuple) for i, lhs := range s.Lhs { if !isBlank(lhs) { out += ", " + c.translateAssign(lhs, c.translateExprToType(c.newIdent(fmt.Sprintf("Go$tuple[%d]", i), tuple.At(i).Type()), c.info.Types[s.Lhs[i]])) } } return out case len(s.Lhs) == len(s.Rhs): parts := make([]string, len(s.Rhs)) for i, rhs := range s.Rhs { parts[i] = c.translateExprToType(rhs, c.info.Types[s.Lhs[i]]) } out := "Go$tuple = [" + strings.Join(parts, ", ") + "]" for i, lhs := range s.Lhs { if !isBlank(lhs) { out += ", " + c.translateAssign(lhs, fmt.Sprintf("Go$tuple[%d]", i)) } } return out default: panic("Invalid arity of AssignStmt.") } case *ast.IncDecStmt: t := c.info.Types[s.X] if iExpr, isIExpr := s.X.(*ast.IndexExpr); isIExpr { switch u := c.info.Types[iExpr.X].Underlying().(type) { case *types.Array: t = u.Elem() case *types.Slice: t = u.Elem() case *types.Map: t = u.Elem() } } tok := token.ADD_ASSIGN if s.Tok == token.DEC { tok = token.SUB_ASSIGN } one := &ast.BasicLit{ Kind: token.INT, Value: "1", } c.info.Types[one] = t c.info.Values[one] = exact.MakeInt64(1) return c.translateSimpleStmt(&ast.AssignStmt{ Lhs: []ast.Expr{s.X}, Tok: tok, Rhs: []ast.Expr{one}, }) case *ast.ExprStmt: return c.translateExpr(s.X) case *ast.DeclStmt: var parts []string for _, spec := range s.Decl.(*ast.GenDecl).Specs { for _, singleSpec := range c.splitValueSpec(spec.(*ast.ValueSpec)) { lhs := make([]ast.Expr, len(singleSpec.Names)) for i, name := range singleSpec.Names { lhs[i] = name } parts = append(parts, c.translateSimpleStmt(&ast.AssignStmt{ Lhs: lhs, Tok: token.DEFINE, Rhs: singleSpec.Values, })) } } return strings.Join(parts, ", ") case *ast.SendStmt: return `throw new Go$Panic("Statement not supported: send")` default: panic(fmt.Sprintf("Unhandled statement: %T\n", s)) } }