github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/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. We do this by going over
   104  	// every expression until we find one with a type we recognize.
   105  	// We do this because the first expr might be a string ("var.foo")
   106  	// and we need to know what to implicit to.
   107  	mathFunc := "__builtin_IntMath"
   108  	mathType := ast.TypeInt
   109  	for _, v := range exprs {
   110  		exit := true
   111  		switch v {
   112  		case ast.TypeInt:
   113  			mathFunc = "__builtin_IntMath"
   114  			mathType = v
   115  		case ast.TypeFloat:
   116  			mathFunc = "__builtin_FloatMath"
   117  			mathType = v
   118  		default:
   119  			exit = false
   120  		}
   121  
   122  		// We found the type, so leave
   123  		if exit {
   124  			break
   125  		}
   126  	}
   127  
   128  	// Verify the args
   129  	for i, arg := range exprs {
   130  		if arg != mathType {
   131  			cn := v.ImplicitConversion(exprs[i], mathType, tc.n.Exprs[i])
   132  			if cn != nil {
   133  				tc.n.Exprs[i] = cn
   134  				continue
   135  			}
   136  
   137  			return nil, fmt.Errorf(
   138  				"operand %d should be %s, got %s",
   139  				i+1, mathType, arg)
   140  		}
   141  	}
   142  
   143  	// Modulo doesn't work for floats
   144  	if mathType == ast.TypeFloat && tc.n.Op == ast.ArithmeticOpMod {
   145  		return nil, fmt.Errorf("modulo cannot be used with floats")
   146  	}
   147  
   148  	// Return type
   149  	v.StackPush(mathType)
   150  
   151  	// Replace our node with a call to the proper function. This isn't
   152  	// type checked but we already verified types.
   153  	args := make([]ast.Node, len(tc.n.Exprs)+1)
   154  	args[0] = &ast.LiteralNode{
   155  		Value: tc.n.Op,
   156  		Typex: ast.TypeInt,
   157  		Posx:  tc.n.Pos(),
   158  	}
   159  	copy(args[1:], tc.n.Exprs)
   160  	return &ast.Call{
   161  		Func: mathFunc,
   162  		Args: args,
   163  		Posx: tc.n.Pos(),
   164  	}, nil
   165  }
   166  
   167  type typeCheckCall struct {
   168  	n *ast.Call
   169  }
   170  
   171  func (tc *typeCheckCall) TypeCheck(v *TypeCheck) (ast.Node, error) {
   172  	// Look up the function in the map
   173  	function, ok := v.Scope.LookupFunc(tc.n.Func)
   174  	if !ok {
   175  		return nil, fmt.Errorf("unknown function called: %s", tc.n.Func)
   176  	}
   177  
   178  	// The arguments are on the stack in reverse order, so pop them off.
   179  	args := make([]ast.Type, len(tc.n.Args))
   180  	for i, _ := range tc.n.Args {
   181  		args[len(tc.n.Args)-1-i] = v.StackPop()
   182  	}
   183  
   184  	// Verify the args
   185  	for i, expected := range function.ArgTypes {
   186  		if expected == ast.TypeAny {
   187  			continue
   188  		}
   189  
   190  		if args[i] != expected {
   191  			cn := v.ImplicitConversion(args[i], expected, tc.n.Args[i])
   192  			if cn != nil {
   193  				tc.n.Args[i] = cn
   194  				continue
   195  			}
   196  
   197  			return nil, fmt.Errorf(
   198  				"%s: argument %d should be %s, got %s",
   199  				tc.n.Func, i+1, expected, args[i])
   200  		}
   201  	}
   202  
   203  	// If we're variadic, then verify the types there
   204  	if function.Variadic && function.VariadicType != ast.TypeAny {
   205  		args = args[len(function.ArgTypes):]
   206  		for i, t := range args {
   207  			if t != function.VariadicType {
   208  				realI := i + len(function.ArgTypes)
   209  				cn := v.ImplicitConversion(
   210  					t, function.VariadicType, tc.n.Args[realI])
   211  				if cn != nil {
   212  					tc.n.Args[realI] = cn
   213  					continue
   214  				}
   215  
   216  				return nil, fmt.Errorf(
   217  					"%s: argument %d should be %s, got %s",
   218  					tc.n.Func, realI,
   219  					function.VariadicType, t)
   220  			}
   221  		}
   222  	}
   223  
   224  	// Return type
   225  	v.StackPush(function.ReturnType)
   226  
   227  	return tc.n, nil
   228  }
   229  
   230  type typeCheckConcat struct {
   231  	n *ast.Concat
   232  }
   233  
   234  func (tc *typeCheckConcat) TypeCheck(v *TypeCheck) (ast.Node, error) {
   235  	n := tc.n
   236  	types := make([]ast.Type, len(n.Exprs))
   237  	for i, _ := range n.Exprs {
   238  		types[len(n.Exprs)-1-i] = v.StackPop()
   239  	}
   240  
   241  	// All concat args must be strings, so validate that
   242  	for i, t := range types {
   243  		if t != ast.TypeString {
   244  			cn := v.ImplicitConversion(t, ast.TypeString, n.Exprs[i])
   245  			if cn != nil {
   246  				n.Exprs[i] = cn
   247  				continue
   248  			}
   249  
   250  			return nil, fmt.Errorf(
   251  				"argument %d must be a string", i+1)
   252  		}
   253  	}
   254  
   255  	// This always results in type string
   256  	v.StackPush(ast.TypeString)
   257  
   258  	return n, nil
   259  }
   260  
   261  type typeCheckLiteral struct {
   262  	n *ast.LiteralNode
   263  }
   264  
   265  func (tc *typeCheckLiteral) TypeCheck(v *TypeCheck) (ast.Node, error) {
   266  	v.StackPush(tc.n.Typex)
   267  	return tc.n, nil
   268  }
   269  
   270  type typeCheckVariableAccess struct {
   271  	n *ast.VariableAccess
   272  }
   273  
   274  func (tc *typeCheckVariableAccess) TypeCheck(v *TypeCheck) (ast.Node, error) {
   275  	// Look up the variable in the map
   276  	variable, ok := v.Scope.LookupVar(tc.n.Name)
   277  	if !ok {
   278  		return nil, fmt.Errorf(
   279  			"unknown variable accessed: %s", tc.n.Name)
   280  	}
   281  
   282  	// Add the type to the stack
   283  	v.StackPush(variable.Type)
   284  
   285  	return tc.n, nil
   286  }
   287  
   288  func (v *TypeCheck) ImplicitConversion(
   289  	actual ast.Type, expected ast.Type, n ast.Node) ast.Node {
   290  	if v.Implicit == nil {
   291  		return nil
   292  	}
   293  
   294  	fromMap, ok := v.Implicit[actual]
   295  	if !ok {
   296  		return nil
   297  	}
   298  
   299  	toFunc, ok := fromMap[expected]
   300  	if !ok {
   301  		return nil
   302  	}
   303  
   304  	return &ast.Call{
   305  		Func: toFunc,
   306  		Args: []ast.Node{n},
   307  		Posx: n.Pos(),
   308  	}
   309  }
   310  
   311  func (v *TypeCheck) reset() {
   312  	v.Stack = nil
   313  	v.err = nil
   314  }
   315  
   316  func (v *TypeCheck) StackPush(t ast.Type) {
   317  	v.Stack = append(v.Stack, t)
   318  }
   319  
   320  func (v *TypeCheck) StackPop() ast.Type {
   321  	var x ast.Type
   322  	x, v.Stack = v.Stack[len(v.Stack)-1], v.Stack[:len(v.Stack)-1]
   323  	return x
   324  }