 # Declaring a type associates a type with the variable in the current
 # scope. Destructuring is allowed, but each destructured variable must get a
 # simple type, i.e. not a **TypeArr** or a **TypeObj**.
-declare = (scope, v, ty, node) ->
+declare = (scope, v, ty, variables) ->
   if v instanceof Literal
     name = v.value
-    error node, "cannot redeclare typed variable `#{name}'" if scope.check name
+    error v, "cannot redeclare typed variable `#{name}'" if scope.check name
     return unless ty
     if ty instanceof TypeArr or ty instanceof TypeObj
-      error node, "cannot type non-destructuring variable with destructuring type `#{ty}'"
+      error v, "cannot type non-destructuring variable with destructuring type `#{ty}'"
     bind = new Binding ty
     scope.add name, bind
-    node.variables.push { name, type: ty, binding: bind }
+    variables.push { name, type: ty, binding: bind }
     putOnStack scope, name if ty instanceof StructType
     return
   if v instanceof Value
-    error node, "cannot type properties" if v.hasProperties()
+    error v, "cannot type properties" if v.hasProperties()
     v = v.base
   if v instanceof Arr
     els = v.objects
     unless ty instanceof TypeArr
-      error node, "cannot type destructuring array with non-destructuring array type `#{ty}'"
+      error v, "cannot type destructuring array with non-destructuring array type `#{ty}'"
     for el, i in els
       if el.this
-        error node, "cannot type @-names"
+        error v, "cannot type @-names"
       else
-        declare scope, el.base, ty.elements[i], node
+        declare scope, el.base, ty.elements[i], variables
     return
   if v instanceof Obj
     objs = v.objects
     unless ty instanceof TypeObj
-      error node, "cannot type destructuring object with non-destructuring object type `#{ty}'"
+      error v, "cannot type destructuring object with non-destructuring object type `#{ty}'"
     for obj in objs
       if obj instanceof Assign
         name = obj.variable.base
-        declare scope, name, ty.names[name.value]?.type, node
+        declare scope, obj.value, ty.names[name.value]?.type, variables
       else if obj.this
-        error node, "cannot type @-names"
+        error v, "cannot type @-names"
       else
         name = obj.base
-        declare scope, name, ty.names[name.value]?.type, node
+        declare scope, name, ty.names[name.value]?.type, variables
     return
-  error node, "trying to type non-typeable"
+  error v, "trying to type non-typeable"
 
 DeclareType::declareType = (r, o) ->
-  type  = @type.lint o.types
   scope = o.scope
-  @variables = []
-  declare scope, v, type, this for v in @typeables
+  declare scope, v, @type.lint(o.types), (@variables = []) for v in @typeables
   return
 
 Param::declareType = (r, o) ->
   # Lint just in case the user manually declared the parameter types and
   # thus the types were not linted before this point.
-  type  = @type.lint o.types
   scope = o.scope
-  @variables = []
-  declare scope, this.name, type, this
+  declare scope, this.name, @type.lint(o.types), (@variables = [])
   return
 
 #### AST Transformation

    # If we're assigning, return the left type, else return the wider primitive.
    if lty.size isnt rty.size
      printWarn "conversion from `#{rty}' to `#{lty}' may alter its value"
      return if assign then lty else if lty.size > rty.size then lty else rty
    # Return the sign of the left type if they're the same size. This is just
    # to break the tie arbitrarily and to behave as expected for assignments.
    if lty.signed isnt rty.signed

  @computedType = @type.lint o.types

# Assignments do asymmetric type unification.
assign = (o, lval, rval, context) ->
  if lval instanceof Value
    if lval.isArray()
      rval = if rval.isArray() then rval.base else null
    else if lval.isObject()
      rval = if rval.isObject() then rval.base else null
    else if lval.this
      return
    lval = lval.base
  if lval instanceof Literal and lty = lval.computedType
    # As a convenience if the left-hand side is a pointer and the right-hand
    # side is 0, replace the right-hand side with null, which has the type *any
    # to suppress int-to-pointer warnings.
    if rval?
      value = rval.unwrapAll()
      if lty instanceof PointerType and value instanceof Literal and value.value is '0'
        value.computedType = anyPtrTy
      # `context` can only be passed in from `Assign::computeType'. This is okay
      # since compound assignments only have **SimpleAssignable**s as lvals.
      rty = if context
        op = context.substr 0, context.indexOf '='
        binopType o, op, lty, value.computedType, lval
      else
        value.computedType
    else
      rty = null
    unless unify o.types, lty, rty, true
      msg = "incompatible types: assigning `#{tystr(rty)}' to `#{lty}'"
      if context
        error lval, msg + " using #{context}"
      else
        error lval, msg
  else if lval instanceof Arr
    robjects = rval?.objects
    assign o, lel, robjects?[i] for lel, i in lval.objects
  else if lval instanceof Obj
    # Build property-name mapping.
    props = {}
    if rval?
      for prop in rval.properties
        prop = prop.variable if prop.isComplex()
        if prop?
          propName = prop.unwrapAll().value.toString()
          props[propName] = prop
    for obj in lval.objects
      if obj instanceof Assign
        assign o, obj.value, props[obj.variable.unwrapAll().value]
      else if obj instanceof Value
        assign o, obj.base, props[obj.base.unwrapAll().value]

Assign::computeType = (r, o) ->
  if @context is 'object'
    delete @variable.computedType
    return
  return @computedType if @typeCached
  @typeCached = true
  assign o, (variable = @variable), @value, @context
  # Complex left-hand sides (destructuring assignments) always have dynamic type.
  @computedType = variable.computedType unless variable.isComplex()

# Accesses on struct types may be typed. If we arrived at this case it's
# because `unwrapAll()` didn't manage to unwrap value, i.e. this is a properties
