github.com/jgadling/terraform@v0.3.8-0.20150227214559-abd68c2c87bc/config/lang/check_types.go (about)

     1  package lang
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"github.com/hashicorp/terraform/config/lang/ast"
     8  )
     9  
    10  // TypeCheck implements ast.Visitor for type checking an AST tree.
    11  // It requires some configuration to look up the type of nodes.
    12  //
    13  // It also optionally will not type error and will insert an implicit
    14  // type conversions for specific types if specified by the Implicit
    15  // field. Note that this is kind of organizationally weird to put into
    16  // this structure but we'd rather do that than duplicate the type checking
    17  // logic multiple times.
    18  type TypeCheck struct {
    19  	Scope ast.Scope
    20  
    21  	// Implicit is a map of implicit type conversions that we can do,
    22  	// and that shouldn't error. The key of the first map is the from type,
    23  	// the key of the second map is the to type, and the final string
    24  	// value is the function to call (which must be registered in the Scope).
    25  	Implicit map[ast.Type]map[ast.Type]string
    26  
    27  	// Stack of types. This shouldn't be used directly except by implementations
    28  	// of TypeCheckNode.
    29  	Stack []ast.Type
    30  
    31  	err  error
    32  	lock sync.Mutex
    33  }
    34  
    35  // TypeCheckNode is the interface that must be implemented by any
    36  // ast.Node that wants to support type-checking. If the type checker
    37  // encounters a node that doesn't implement this, it will error.
    38  type TypeCheckNode interface {
    39  	TypeCheck(*TypeCheck) (ast.Node, error)
    40  }
    41  
    42  func (v *TypeCheck) Visit(root ast.Node) error {
    43  	v.lock.Lock()
    44  	defer v.lock.Unlock()
    45  	defer v.reset()
    46  	root.Accept(v.visit)
    47  	return v.err
    48  }
    49  
    50  func (v *TypeCheck) visit(raw ast.Node) ast.Node {
    51  	if v.err != nil {
    52  		return raw
    53  	}
    54  
    55  	var result ast.Node
    56  	var err error
    57  	switch n := raw.(type) {
    58  	case *ast.Arithmetic:
    59  		tc := &typeCheckArithmetic{n}
    60  		result, err = tc.TypeCheck(v)
    61  	case *ast.Call:
    62  		tc := &typeCheckCall{n}
    63  		result, err = tc.TypeCheck(v)
    64  	case *ast.Concat:
    65  		tc := &typeCheckConcat{n}
    66  		result, err = tc.TypeCheck(v)
    67  	case *ast.LiteralNode:
    68  		tc := &typeCheckLiteral{n}
    69  		result, err = tc.TypeCheck(v)
    70  	case *ast.VariableAccess:
    71  		tc := &typeCheckVariableAccess{n}
    72  		result, err = tc.TypeCheck(v)
    73  	default:
    74  		tc, ok := raw.(TypeCheckNode)
    75  		if !ok {
    76  			err = fmt.Errorf("unknown node for type check: %#v", raw)
    77  			break
    78  		}
    79  
    80  		result, err = tc.TypeCheck(v)
    81  	}
    82  
    83  	if err != nil {
    84  		pos := raw.Pos()
    85  		v.err = fmt.Errorf("At column %d, line %d: %s",
    86  			pos.Column, pos.Line, err)
    87  	}
    88  
    89  	return result
    90  }
    91  
    92  type typeCheckArithmetic struct {
    93  	n *ast.Arithmetic
    94  }
    95  
    96  func (tc *typeCheckArithmetic) TypeCheck(v *TypeCheck) (ast.Node, error) {
    97  	// The arguments are on the stack in reverse order, so pop them off.
    98  	exprs := make([]ast.Type, len(tc.n.Exprs))
    99  	for i, _ := range tc.n.Exprs {
   100  		exprs[len(tc.n.Exprs)-1-i] = v.StackPop()
   101  	}
   102  
   103  	// Determine the resulting type we want
   104  	mathFunc := "__builtin_IntMath"
   105  	mathType := ast.TypeInt
   106  	switch v := exprs[0]; v {
   107  	case ast.TypeInt:
   108  		mathFunc = "__builtin_IntMath"
   109  		mathType = v
   110  	case ast.TypeFloat:
   111  		mathFunc = "__builtin_FloatMath"
   112  		mathType = v
   113  	default:
   114  		return nil, fmt.Errorf(
   115  			"Math operations can only be done with ints and floats, got %s",
   116  			v)
   117  	}
   118  
   119  	// Verify the args
   120  	for i, arg := range exprs {
   121  		if arg != mathType {
   122  			cn := v.ImplicitConversion(exprs[i], mathType, tc.n.Exprs[i])
   123  			if cn != nil {
   124  				tc.n.Exprs[i] = cn
   125  				continue
   126  			}
   127  
   128  			return nil, fmt.Errorf(
   129  				"operand %d should be %s, got %s",
   130  				i+1, mathType, arg)
   131  		}
   132  	}
   133  
   134  	// Modulo doesn't work for floats
   135  	if mathType == ast.TypeFloat && tc.n.Op == ast.ArithmeticOpMod {
   136  		return nil, fmt.Errorf("modulo cannot be used with floats")
   137  	}
   138  
   139  	// Return type
   140  	v.StackPush(mathType)
   141  
   142  	// Replace our node with a call to the proper function. This isn't
   143  	// type checked but we already verified types.
   144  	args := make([]ast.Node, len(tc.n.Exprs)+1)
   145  	args[0] = &ast.LiteralNode{
   146  		Value: tc.n.Op,
   147  		Typex: ast.TypeInt,
   148  		Posx:  tc.n.Pos(),
   149  	}
   150  	copy(args[1:], tc.n.Exprs)
   151  	return &ast.Call{
   152  		Func: mathFunc,
   153  		Args: args,
   154  		Posx: tc.n.Pos(),
   155  	}, nil
   156  }
   157  
   158  type typeCheckCall struct {
   159  	n *ast.Call
   160  }
   161  
   162  func (tc *typeCheckCall) TypeCheck(v *TypeCheck) (ast.Node, error) {
   163  	// Look up the function in the map
   164  	function, ok := v.Scope.LookupFunc(tc.n.Func)
   165  	if !ok {
   166  		return nil, fmt.Errorf("unknown function called: %s", tc.n.Func)
   167  	}
   168  
   169  	// The arguments are on the stack in reverse order, so pop them off.
   170  	args := make([]ast.Type, len(tc.n.Args))
   171  	for i, _ := range tc.n.Args {
   172  		args[len(tc.n.Args)-1-i] = v.StackPop()
   173  	}
   174  
   175  	// Verify the args
   176  	for i, expected := range function.ArgTypes {
   177  		if args[i] != expected {
   178  			cn := v.ImplicitConversion(args[i], expected, tc.n.Args[i])
   179  			if cn != nil {
   180  				tc.n.Args[i] = cn
   181  				continue
   182  			}
   183  
   184  			return nil, fmt.Errorf(
   185  				"%s: argument %d should be %s, got %s",
   186  				tc.n.Func, i+1, expected, args[i])
   187  		}
   188  	}
   189  
   190  	// If we're variadic, then verify the types there
   191  	if function.Variadic {
   192  		args = args[len(function.ArgTypes):]
   193  		for i, t := range args {
   194  			if t != function.VariadicType {
   195  				realI := i + len(function.ArgTypes)
   196  				cn := v.ImplicitConversion(
   197  					t, function.VariadicType, tc.n.Args[realI])
   198  				if cn != nil {
   199  					tc.n.Args[realI] = cn
   200  					continue
   201  				}
   202  
   203  				return nil, fmt.Errorf(
   204  					"%s: argument %d should be %s, got %s",
   205  					tc.n.Func, realI,
   206  					function.VariadicType, t)
   207  			}
   208  		}
   209  	}
   210  
   211  	// Return type
   212  	v.StackPush(function.ReturnType)
   213  
   214  	return tc.n, nil
   215  }
   216  
   217  type typeCheckConcat struct {
   218  	n *ast.Concat
   219  }
   220  
   221  func (tc *typeCheckConcat) TypeCheck(v *TypeCheck) (ast.Node, error) {
   222  	n := tc.n
   223  	types := make([]ast.Type, len(n.Exprs))
   224  	for i, _ := range n.Exprs {
   225  		types[len(n.Exprs)-1-i] = v.StackPop()
   226  	}
   227  
   228  	// All concat args must be strings, so validate that
   229  	for i, t := range types {
   230  		if t != ast.TypeString {
   231  			cn := v.ImplicitConversion(t, ast.TypeString, n.Exprs[i])
   232  			if cn != nil {
   233  				n.Exprs[i] = cn
   234  				continue
   235  			}
   236  
   237  			return nil, fmt.Errorf(
   238  				"argument %d must be a string", i+1)
   239  		}
   240  	}
   241  
   242  	// This always results in type string
   243  	v.StackPush(ast.TypeString)
   244  
   245  	return n, nil
   246  }
   247  
   248  type typeCheckLiteral struct {
   249  	n *ast.LiteralNode
   250  }
   251  
   252  func (tc *typeCheckLiteral) TypeCheck(v *TypeCheck) (ast.Node, error) {
   253  	v.StackPush(tc.n.Typex)
   254  	return tc.n, nil
   255  }
   256  
   257  type typeCheckVariableAccess struct {
   258  	n *ast.VariableAccess
   259  }
   260  
   261  func (tc *typeCheckVariableAccess) TypeCheck(v *TypeCheck) (ast.Node, error) {
   262  	// Look up the variable in the map
   263  	variable, ok := v.Scope.LookupVar(tc.n.Name)
   264  	if !ok {
   265  		return nil, fmt.Errorf(
   266  			"unknown variable accessed: %s", tc.n.Name)
   267  	}
   268  
   269  	// Add the type to the stack
   270  	v.StackPush(variable.Type)
   271  
   272  	return tc.n, nil
   273  }
   274  
   275  func (v *TypeCheck) ImplicitConversion(
   276  	actual ast.Type, expected ast.Type, n ast.Node) ast.Node {
   277  	if v.Implicit == nil {
   278  		return nil
   279  	}
   280  
   281  	fromMap, ok := v.Implicit[actual]
   282  	if !ok {
   283  		return nil
   284  	}
   285  
   286  	toFunc, ok := fromMap[expected]
   287  	if !ok {
   288  		return nil
   289  	}
   290  
   291  	return &ast.Call{
   292  		Func: toFunc,
   293  		Args: []ast.Node{n},
   294  		Posx: n.Pos(),
   295  	}
   296  }
   297  
   298  func (v *TypeCheck) reset() {
   299  	v.Stack = nil
   300  	v.err = nil
   301  }
   302  
   303  func (v *TypeCheck) StackPush(t ast.Type) {
   304  	v.Stack = append(v.Stack, t)
   305  }
   306  
   307  func (v *TypeCheck) StackPop() ast.Type {
   308  	var x ast.Type
   309  	x, v.Stack = v.Stack[len(v.Stack)-1], v.Stack[:len(v.Stack)-1]
   310  	return x
   311  }