github.com/leeprovoost/terraform@v0.6.10-0.20160119085442-96f3f76118e7/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.UnaryArithmetic:
    59  		tc := &typeCheckUnaryArithmetic{n}
    60  		result, err = tc.TypeCheck(v)
    61  	case *ast.Arithmetic:
    62  		tc := &typeCheckArithmetic{n}
    63  		result, err = tc.TypeCheck(v)
    64  	case *ast.Call:
    65  		tc := &typeCheckCall{n}
    66  		result, err = tc.TypeCheck(v)
    67  	case *ast.Concat:
    68  		tc := &typeCheckConcat{n}
    69  		result, err = tc.TypeCheck(v)
    70  	case *ast.LiteralNode:
    71  		tc := &typeCheckLiteral{n}
    72  		result, err = tc.TypeCheck(v)
    73  	case *ast.VariableAccess:
    74  		tc := &typeCheckVariableAccess{n}
    75  		result, err = tc.TypeCheck(v)
    76  	default:
    77  		tc, ok := raw.(TypeCheckNode)
    78  		if !ok {
    79  			err = fmt.Errorf("unknown node for type check: %#v", raw)
    80  			break
    81  		}
    82  
    83  		result, err = tc.TypeCheck(v)
    84  	}
    85  
    86  	if err != nil {
    87  		pos := raw.Pos()
    88  		v.err = fmt.Errorf("At column %d, line %d: %s",
    89  			pos.Column, pos.Line, err)
    90  	}
    91  
    92  	return result
    93  }
    94  
    95  type typeCheckUnaryArithmetic struct {
    96  	n *ast.UnaryArithmetic
    97  }
    98  
    99  func (tc *typeCheckUnaryArithmetic) TypeCheck(v *TypeCheck) (ast.Node, error) {
   100  	// Only support + or - as unary op
   101  	if tc.n.Op != ast.ArithmeticOpAdd && tc.n.Op != ast.ArithmeticOpSub {
   102  		fmt.Printf("%+v\n", tc.n.Op)
   103  		return nil, fmt.Errorf("only + or - supported as unary operator")
   104  	}
   105  	expr := v.StackPop()
   106  
   107  	mathFunc := "__builtin_UnaryIntMath"
   108  	mathType := ast.TypeInt
   109  	switch expr {
   110  	case ast.TypeInt:
   111  		mathFunc = "__builtin_UnaryIntMath"
   112  		mathType = expr
   113  	case ast.TypeFloat:
   114  		mathFunc = "__builtin_UnaryFloatMath"
   115  		mathType = expr
   116  	}
   117  
   118  	// Return type
   119  	v.StackPush(mathType)
   120  
   121  	args := make([]ast.Node, 2)
   122  	args[0] = &ast.LiteralNode{
   123  		Value: tc.n.Op,
   124  		Typex: ast.TypeInt,
   125  		Posx:  tc.n.Pos(),
   126  	}
   127  	args[1] = tc.n.Expr
   128  	// Replace our node with a call to the proper function. This isn't
   129  	// type checked but we already verified types.
   130  	return &ast.Call{
   131  		Func: mathFunc,
   132  		Args: args,
   133  		Posx: tc.n.Pos(),
   134  	}, nil
   135  }
   136  
   137  type typeCheckArithmetic struct {
   138  	n *ast.Arithmetic
   139  }
   140  
   141  func (tc *typeCheckArithmetic) TypeCheck(v *TypeCheck) (ast.Node, error) {
   142  	// The arguments are on the stack in reverse order, so pop them off.
   143  	exprs := make([]ast.Type, len(tc.n.Exprs))
   144  	for i, _ := range tc.n.Exprs {
   145  		exprs[len(tc.n.Exprs)-1-i] = v.StackPop()
   146  	}
   147  
   148  	// Determine the resulting type we want. We do this by going over
   149  	// every expression until we find one with a type we recognize.
   150  	// We do this because the first expr might be a string ("var.foo")
   151  	// and we need to know what to implicit to.
   152  	mathFunc := "__builtin_IntMath"
   153  	mathType := ast.TypeInt
   154  	for _, v := range exprs {
   155  		exit := true
   156  		switch v {
   157  		case ast.TypeInt:
   158  			mathFunc = "__builtin_IntMath"
   159  			mathType = v
   160  		case ast.TypeFloat:
   161  			mathFunc = "__builtin_FloatMath"
   162  			mathType = v
   163  		default:
   164  			exit = false
   165  		}
   166  
   167  		// We found the type, so leave
   168  		if exit {
   169  			break
   170  		}
   171  	}
   172  
   173  	// Verify the args
   174  	for i, arg := range exprs {
   175  		if arg != mathType {
   176  			cn := v.ImplicitConversion(exprs[i], mathType, tc.n.Exprs[i])
   177  			if cn != nil {
   178  				tc.n.Exprs[i] = cn
   179  				continue
   180  			}
   181  
   182  			return nil, fmt.Errorf(
   183  				"operand %d should be %s, got %s",
   184  				i+1, mathType, arg)
   185  		}
   186  	}
   187  
   188  	// Modulo doesn't work for floats
   189  	if mathType == ast.TypeFloat && tc.n.Op == ast.ArithmeticOpMod {
   190  		return nil, fmt.Errorf("modulo cannot be used with floats")
   191  	}
   192  
   193  	// Return type
   194  	v.StackPush(mathType)
   195  
   196  	// Replace our node with a call to the proper function. This isn't
   197  	// type checked but we already verified types.
   198  	args := make([]ast.Node, len(tc.n.Exprs)+1)
   199  	args[0] = &ast.LiteralNode{
   200  		Value: tc.n.Op,
   201  		Typex: ast.TypeInt,
   202  		Posx:  tc.n.Pos(),
   203  	}
   204  	copy(args[1:], tc.n.Exprs)
   205  	return &ast.Call{
   206  		Func: mathFunc,
   207  		Args: args,
   208  		Posx: tc.n.Pos(),
   209  	}, nil
   210  }
   211  
   212  type typeCheckCall struct {
   213  	n *ast.Call
   214  }
   215  
   216  func (tc *typeCheckCall) TypeCheck(v *TypeCheck) (ast.Node, error) {
   217  	// Look up the function in the map
   218  	function, ok := v.Scope.LookupFunc(tc.n.Func)
   219  	if !ok {
   220  		return nil, fmt.Errorf("unknown function called: %s", tc.n.Func)
   221  	}
   222  
   223  	// The arguments are on the stack in reverse order, so pop them off.
   224  	args := make([]ast.Type, len(tc.n.Args))
   225  	for i, _ := range tc.n.Args {
   226  		args[len(tc.n.Args)-1-i] = v.StackPop()
   227  	}
   228  
   229  	// Verify the args
   230  	for i, expected := range function.ArgTypes {
   231  		if expected == ast.TypeAny {
   232  			continue
   233  		}
   234  
   235  		if args[i] != expected {
   236  			cn := v.ImplicitConversion(args[i], expected, tc.n.Args[i])
   237  			if cn != nil {
   238  				tc.n.Args[i] = cn
   239  				continue
   240  			}
   241  
   242  			return nil, fmt.Errorf(
   243  				"%s: argument %d should be %s, got %s",
   244  				tc.n.Func, i+1, expected, args[i])
   245  		}
   246  	}
   247  
   248  	// If we're variadic, then verify the types there
   249  	if function.Variadic && function.VariadicType != ast.TypeAny {
   250  		args = args[len(function.ArgTypes):]
   251  		for i, t := range args {
   252  			if t != function.VariadicType {
   253  				realI := i + len(function.ArgTypes)
   254  				cn := v.ImplicitConversion(
   255  					t, function.VariadicType, tc.n.Args[realI])
   256  				if cn != nil {
   257  					tc.n.Args[realI] = cn
   258  					continue
   259  				}
   260  
   261  				return nil, fmt.Errorf(
   262  					"%s: argument %d should be %s, got %s",
   263  					tc.n.Func, realI,
   264  					function.VariadicType, t)
   265  			}
   266  		}
   267  	}
   268  
   269  	// Return type
   270  	v.StackPush(function.ReturnType)
   271  
   272  	return tc.n, nil
   273  }
   274  
   275  type typeCheckConcat struct {
   276  	n *ast.Concat
   277  }
   278  
   279  func (tc *typeCheckConcat) TypeCheck(v *TypeCheck) (ast.Node, error) {
   280  	n := tc.n
   281  	types := make([]ast.Type, len(n.Exprs))
   282  	for i, _ := range n.Exprs {
   283  		types[len(n.Exprs)-1-i] = v.StackPop()
   284  	}
   285  
   286  	// All concat args must be strings, so validate that
   287  	for i, t := range types {
   288  		if t != ast.TypeString {
   289  			cn := v.ImplicitConversion(t, ast.TypeString, n.Exprs[i])
   290  			if cn != nil {
   291  				n.Exprs[i] = cn
   292  				continue
   293  			}
   294  
   295  			return nil, fmt.Errorf(
   296  				"argument %d must be a string", i+1)
   297  		}
   298  	}
   299  
   300  	// This always results in type string
   301  	v.StackPush(ast.TypeString)
   302  
   303  	return n, nil
   304  }
   305  
   306  type typeCheckLiteral struct {
   307  	n *ast.LiteralNode
   308  }
   309  
   310  func (tc *typeCheckLiteral) TypeCheck(v *TypeCheck) (ast.Node, error) {
   311  	v.StackPush(tc.n.Typex)
   312  	return tc.n, nil
   313  }
   314  
   315  type typeCheckVariableAccess struct {
   316  	n *ast.VariableAccess
   317  }
   318  
   319  func (tc *typeCheckVariableAccess) TypeCheck(v *TypeCheck) (ast.Node, error) {
   320  	// Look up the variable in the map
   321  	variable, ok := v.Scope.LookupVar(tc.n.Name)
   322  	if !ok {
   323  		return nil, fmt.Errorf(
   324  			"unknown variable accessed: %s", tc.n.Name)
   325  	}
   326  
   327  	// Add the type to the stack
   328  	v.StackPush(variable.Type)
   329  
   330  	return tc.n, nil
   331  }
   332  
   333  func (v *TypeCheck) ImplicitConversion(
   334  	actual ast.Type, expected ast.Type, n ast.Node) ast.Node {
   335  	if v.Implicit == nil {
   336  		return nil
   337  	}
   338  
   339  	fromMap, ok := v.Implicit[actual]
   340  	if !ok {
   341  		return nil
   342  	}
   343  
   344  	toFunc, ok := fromMap[expected]
   345  	if !ok {
   346  		return nil
   347  	}
   348  
   349  	return &ast.Call{
   350  		Func: toFunc,
   351  		Args: []ast.Node{n},
   352  		Posx: n.Pos(),
   353  	}
   354  }
   355  
   356  func (v *TypeCheck) reset() {
   357  	v.Stack = nil
   358  	v.err = nil
   359  }
   360  
   361  func (v *TypeCheck) StackPush(t ast.Type) {
   362  	v.Stack = append(v.Stack, t)
   363  }
   364  
   365  func (v *TypeCheck) StackPop() ast.Type {
   366  	var x ast.Type
   367  	x, v.Stack = v.Stack[len(v.Stack)-1], v.Stack[:len(v.Stack)-1]
   368  	return x
   369  }