Beispiel #1
0
// labels checks correct label use in body.
func (check *Checker) labels(body *ast.BlockStmt) {
	// set of all labels in this body
	all := NewScope(nil, body.Pos(), body.End(), "label")

	fwdJumps := check.blockBranches(all, nil, nil, body.List)

	// If there are any forward jumps left, no label was found for
	// the corresponding goto statements. Either those labels were
	// never defined, or they are inside blocks and not reachable
	// for the respective gotos.
	for _, jmp := range fwdJumps {
		var msg string
		name := jmp.Label.Name
		if alt := all.Lookup(name); alt != nil {
			msg = "goto %s jumps into block"
			alt.(*Label).used = true // avoid another error
		} else {
			msg = "label %s not declared"
		}
		check.errorf(jmp.Label.Pos(), msg, name)
	}

	// spec: "It is illegal to define a label that is never used."
	for _, obj := range all.elems {
		if lbl := obj.(*Label); !lbl.used {
			check.softErrorf(lbl.pos, "label %s declared but not used", lbl.name)
		}
	}
}
Beispiel #2
0
// lastComment returns the last comment inside the provided block.
func lastComment(b *ast.BlockStmt, c []*ast.CommentGroup) (i int, last *ast.CommentGroup) {
	pos, end := b.Pos(), b.End()
	for j, cg := range c {
		if cg.Pos() < pos {
			continue
		}
		if cg.End() > end {
			break
		}
		i, last = j, cg
	}
	return
}
Beispiel #3
0
// New returns a new control-flow graph for the specified function body,
// which must be non-nil.
//
// The CFG builder calls mayReturn to determine whether a given function
// call may return.  For example, calls to panic, os.Exit, and log.Fatal
// do not return, so the builder can remove infeasible graph edges
// following such calls.  The builder calls mayReturn only for a
// CallExpr beneath an ExprStmt.
func New(body *ast.BlockStmt, mayReturn func(*ast.CallExpr) bool) *CFG {
	b := builder{
		mayReturn: mayReturn,
		cfg:       new(CFG),
	}
	b.current = b.newBlock("entry")
	b.stmt(body)

	// Does control fall off the end of the function's body?
	// Make implicit return explicit.
	if b.current != nil && !b.current.unreachable {
		b.add(&ast.ReturnStmt{
			Return: body.End() - 1,
		})
	}

	return b.cfg
}
Beispiel #4
0
func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt) {
	if trace {
		if name == "" {
			name = "<function literal>"
		}
		fmt.Printf("--- %s: %s {\n", name, sig)
		defer fmt.Println("--- <end>")
	}

	// set function scope extent
	sig.scope.pos = body.Pos()
	sig.scope.end = body.End()

	// save/restore current context and setup function context
	// (and use 0 indentation at function start)
	defer func(ctxt context, indent int) {
		check.context = ctxt
		check.indent = indent
	}(check.context, check.indent)
	check.context = context{
		decl:  decl,
		scope: sig.scope,
		sig:   sig,
	}
	check.indent = 0

	check.stmtList(0, body.List)

	if check.hasLabel {
		check.labels(body)
	}

	if sig.results.Len() > 0 && !check.isTerminating(body, "") {
		check.error(body.Rbrace, "missing return")
	}

	// spec: "Implementation restriction: A compiler may make it illegal to
	// declare a variable inside a function body if the variable is never used."
	// (One could check each scope after use, but that distributes this check
	// over several places because CloseScope is not always called explicitly.)
	check.usage(sig.scope)
}
Beispiel #5
0
// playExample synthesizes a new *ast.File based on the provided
// file with the provided function body as the body of main.
func playExample(file *ast.File, body *ast.BlockStmt) *ast.File {
	if !strings.HasSuffix(file.Name.Name, "_test") {
		// We don't support examples that are part of the
		// greater package (yet).
		return nil
	}

	// Find top-level declarations in the file.
	topDecls := make(map[*ast.Object]bool)
	for _, decl := range file.Decls {
		switch d := decl.(type) {
		case *ast.FuncDecl:
			topDecls[d.Name.Obj] = true
		case *ast.GenDecl:
			for _, spec := range d.Specs {
				switch s := spec.(type) {
				case *ast.TypeSpec:
					topDecls[s.Name.Obj] = true
				case *ast.ValueSpec:
					for _, id := range s.Names {
						topDecls[id.Obj] = true
					}
				}
			}
		}
	}

	// Find unresolved identifiers and uses of top-level declarations.
	unresolved := make(map[string]bool)
	usesTopDecl := false
	var inspectFunc func(ast.Node) bool
	inspectFunc = func(n ast.Node) bool {
		// For selector expressions, only inspect the left hand side.
		// (For an expression like fmt.Println, only add "fmt" to the
		// set of unresolved names, not "Println".)
		if e, ok := n.(*ast.SelectorExpr); ok {
			ast.Inspect(e.X, inspectFunc)
			return false
		}
		// For key value expressions, only inspect the value
		// as the key should be resolved by the type of the
		// composite literal.
		if e, ok := n.(*ast.KeyValueExpr); ok {
			ast.Inspect(e.Value, inspectFunc)
			return false
		}
		if id, ok := n.(*ast.Ident); ok {
			if id.Obj == nil {
				unresolved[id.Name] = true
			} else if topDecls[id.Obj] {
				usesTopDecl = true
			}
		}
		return true
	}
	ast.Inspect(body, inspectFunc)
	if usesTopDecl {
		// We don't support examples that are not self-contained (yet).
		return nil
	}

	// Remove predeclared identifiers from unresolved list.
	for n := range unresolved {
		if predeclaredTypes[n] || predeclaredConstants[n] || predeclaredFuncs[n] {
			delete(unresolved, n)
		}
	}

	// Use unresolved identifiers to determine the imports used by this
	// example. The heuristic assumes package names match base import
	// paths for imports w/o renames (should be good enough most of the time).
	namedImports := make(map[string]string) // [name]path
	var blankImports []ast.Spec             // _ imports
	for _, s := range file.Imports {
		p, err := strconv.Unquote(s.Path.Value)
		if err != nil {
			continue
		}
		n := path.Base(p)
		if s.Name != nil {
			n = s.Name.Name
			switch n {
			case "_":
				blankImports = append(blankImports, s)
				continue
			case ".":
				// We can't resolve dot imports (yet).
				return nil
			}
		}
		if unresolved[n] {
			namedImports[n] = p
			delete(unresolved, n)
		}
	}

	// If there are other unresolved identifiers, give up because this
	// synthesized file is not going to build.
	if len(unresolved) > 0 {
		return nil
	}

	// Include documentation belonging to blank imports.
	var comments []*ast.CommentGroup
	for _, s := range blankImports {
		if c := s.(*ast.ImportSpec).Doc; c != nil {
			comments = append(comments, c)
		}
	}

	// Include comments that are inside the function body.
	for _, c := range file.Comments {
		if body.Pos() <= c.Pos() && c.End() <= body.End() {
			comments = append(comments, c)
		}
	}

	// Strip "Output:" commment and adjust body end position.
	body, comments = stripOutputComment(body, comments)

	// Synthesize import declaration.
	importDecl := &ast.GenDecl{
		Tok:    token.IMPORT,
		Lparen: 1, // Need non-zero Lparen and Rparen so that printer
		Rparen: 1, // treats this as a factored import.
	}
	for n, p := range namedImports {
		s := &ast.ImportSpec{Path: &ast.BasicLit{Value: strconv.Quote(p)}}
		if path.Base(p) != n {
			s.Name = ast.NewIdent(n)
		}
		importDecl.Specs = append(importDecl.Specs, s)
	}
	importDecl.Specs = append(importDecl.Specs, blankImports...)

	// Synthesize main function.
	funcDecl := &ast.FuncDecl{
		Name: ast.NewIdent("main"),
		Type: &ast.FuncType{},
		Body: body,
	}

	// Synthesize file.
	return &ast.File{
		Name:     ast.NewIdent("main"),
		Decls:    []ast.Decl{importDecl, funcDecl},
		Comments: comments,
	}
}
Beispiel #6
0
// playExample synthesizes a new *ast.File based on the provided
// file with the provided function body as the body of main.
func playExample(file *ast.File, body *ast.BlockStmt) *ast.File {
	if !strings.HasSuffix(file.Name.Name, "_test") {
		// We don't support examples that are part of the
		// greater package (yet).
		return nil
	}

	// Find unresolved identifiers
	unresolved := make(map[string]bool)
	ast.Inspect(body, func(n ast.Node) bool {
		// For an expression like fmt.Println, only add "fmt" to the
		// set of unresolved names.
		if e, ok := n.(*ast.SelectorExpr); ok {
			if id, ok := e.X.(*ast.Ident); ok && id.Obj == nil {
				unresolved[id.Name] = true
			}
			return false
		}
		if id, ok := n.(*ast.Ident); ok && id.Obj == nil {
			unresolved[id.Name] = true
		}
		return true
	})

	// Remove predeclared identifiers from unresolved list.
	for n := range unresolved {
		if predeclaredTypes[n] || predeclaredConstants[n] || predeclaredFuncs[n] {
			delete(unresolved, n)
		}
	}

	// Use unresolved identifiers to determine the imports used by this
	// example. The heuristic assumes package names match base import
	// paths. (Should be good enough most of the time.)
	imports := make(map[string]string) // [name]path
	for _, s := range file.Imports {
		p, err := strconv.Unquote(s.Path.Value)
		if err != nil {
			continue
		}
		n := path.Base(p)
		if s.Name != nil {
			if s.Name.Name == "." {
				// We can't resolve dot imports (yet).
				return nil
			}
			n = s.Name.Name
		}
		if unresolved[n] {
			imports[n] = p
			delete(unresolved, n)
		}
	}

	// If there are other unresolved identifiers, give up because this
	// synthesized file is not going to build.
	if len(unresolved) > 0 {
		return nil
	}

	// Filter out comments that are outside the function body.
	var comments []*ast.CommentGroup
	for _, c := range file.Comments {
		if c.Pos() < body.Pos() || c.Pos() >= body.End() {
			continue
		}
		comments = append(comments, c)
	}

	// Strip "Output:" commment and adjust body end position.
	body, comments = stripOutputComment(body, comments)

	// Synthesize import declaration.
	importDecl := &ast.GenDecl{
		Tok:    token.IMPORT,
		Lparen: 1, // Need non-zero Lparen and Rparen so that printer
		Rparen: 1, // treats this as a factored import.
	}
	for n, p := range imports {
		s := &ast.ImportSpec{Path: &ast.BasicLit{Value: strconv.Quote(p)}}
		if path.Base(p) != n {
			s.Name = ast.NewIdent(n)
		}
		importDecl.Specs = append(importDecl.Specs, s)
	}

	// Synthesize main function.
	funcDecl := &ast.FuncDecl{
		Name: ast.NewIdent("main"),
		Type: &ast.FuncType{},
		Body: body,
	}

	// Synthesize file.
	return &ast.File{
		Name:     ast.NewIdent("main"),
		Decls:    []ast.Decl{importDecl, funcDecl},
		Comments: comments,
	}
}
Beispiel #7
0
// buildFunction takes a function Value, a list of parameters, and a body,
// and generates code for the function.
func (c *compiler) buildFunction(f *LLVMValue, context, params, results *types.Tuple, body *ast.BlockStmt) {
	if currblock := c.builder.GetInsertBlock(); !currblock.IsNil() {
		defer c.builder.SetInsertPointAtEnd(currblock)
	}
	llvm_fn := llvm.ConstExtractValue(f.LLVMValue(), []uint32{0})
	entry := llvm.AddBasicBlock(llvm_fn, "entry")
	c.builder.SetInsertPointAtEnd(entry)

	// For closures, context is the captured context values.
	var paramoffset int
	if context != nil {
		paramoffset++

		// Store the existing values. We're going to temporarily
		// replace the values with offsets into the context param.
		oldvalues := make([]*LLVMValue, context.Len())
		for i := range oldvalues {
			v := context.At(i)
			oldvalues[i] = c.objectdata[v].Value
		}
		defer func() {
			for i := range oldvalues {
				v := context.At(i)
				c.objectdata[v].Value = oldvalues[i]
			}
		}()

		// The context parameter is a pointer to a struct
		// whose elements are pointers to captured values.
		arg0 := llvm_fn.Param(0)
		for i := range oldvalues {
			v := context.At(i)
			argptr := c.builder.CreateStructGEP(arg0, i, "")
			argptr = c.builder.CreateLoad(argptr, "")
			ptrtyp := oldvalues[i].pointer.Type()
			newvalue := c.NewValue(argptr, ptrtyp)
			c.objectdata[v].Value = newvalue.makePointee()
		}
	}

	// Bind receiver, arguments and return values to their
	// identifiers/objects. We'll store each parameter on the stack so
	// they're addressable.
	nparams := int(params.Len())
	for i := 0; i < nparams; i++ {
		v := params.At(i)
		name := v.Name()
		if !isBlank(name) {
			value := llvm_fn.Param(i + paramoffset)
			c.newArgStackVar(i+1, f, v, value, name)
		}
	}

	funcstate := &function{LLVMValue: f, results: results}
	c.functions.push(funcstate)
	hasdefer := hasDefer(funcstate, body)

	// Allocate space on the stack for named results.
	for i := 0; i < results.Len(); i++ {
		v := results.At(i)
		name := v.Name()
		allocstack := !isBlank(name)
		if !allocstack && hasdefer {
			c.objectdata[v] = &ObjectData{}
			allocstack = true
		}
		if allocstack {
			typ := v.Type()
			llvmtyp := c.types.ToLLVM(typ)
			c.newStackVar(f, v, llvm.ConstNull(llvmtyp), name)
		}
	}

	// Create the function body.
	if hasdefer {
		c.makeDeferBlock(funcstate, body)
	}
	c.VisitBlockStmt(body, false)
	c.functions.pop()

	c.setDebugLine(body.End())

	// If the last instruction in the function is not a terminator, then
	// we either have unreachable code or a missing optional return statement
	// (the latter case is allowable only for functions without results).
	//
	// Use GetInsertBlock rather than LastBasicBlock, since the
	// last basic block might actually be a "defer" block.
	last := c.builder.GetInsertBlock()
	if in := last.LastInstruction(); in.IsNil() || in.IsATerminatorInst().IsNil() {
		c.builder.SetInsertPointAtEnd(last)
		if results.Len() == 0 {
			if funcstate.deferblock.IsNil() {
				c.builder.CreateRetVoid()
			} else {
				c.builder.CreateBr(funcstate.deferblock)
			}
		} else {
			c.builder.CreateUnreachable()
		}
	}
}