Example #1
0
// 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))
}
Example #2
0
// 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
}
Example #4
0
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
}
Example #5
0
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
}
Example #6
0
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))
	}
}
Example #7
0
// intConst returns an untyped integer constant that evaluates to i.
func intConst(i int64) *Const {
	return NewConst(exact.MakeInt64(i), types.Typ[types.UntypedInt])
}
Example #8
0
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
}
Example #9
0
// 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())
			}
		}
	}
}
Example #10
0
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))

	}
}
Example #11
0
// 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
}
Example #12
0
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)
	}
}
Example #13
0
// 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
}
Example #14
0
File: const.go Project: 4honor/obdi
// 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)
}
Example #15
0
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)
	}
}
Example #16
0
// 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
}
Example #17
0
	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
Example #18
0
// intLiteral returns an untyped integer literal that evaluates to i.
func intLiteral(i int64) *Literal {
	return newLiteral(exact.MakeInt64(i), types.Typ[types.UntypedInt])
}
Example #19
0
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)
	}
}
Example #20
0
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))

	}
}