deepCopy :: Options -> [ActualParameter ()] -> [Program ()]
deepCopy opts [ValueParameter arg1, ValueParameter arg2]
  | arg1 == arg2
  = []

  | ConstExpr ArrayConst{..} <- arg2
  = initArray (Just arg1) (litI32 $ toInteger $ length arrayValues)
    : zipWith (\i c -> Assign (Just $ ArrayElem arg1 (litI32 i)) (ConstExpr c)) [0..] arrayValues

  | NativeArray{} <- typeof arg2
  , l@(ConstExpr (IntConst n _)) <- arrayLength arg2
  = if n < safetyLimit opts
      then initArray (Just arg1) l:map (\i -> Assign (Just $ ArrayElem arg1 (litI32 i)) (ArrayElem arg2 (litI32 i))) [0..(n-1)]
      else error $ unlines ["Internal compiler error: array size (" ++ show n ++ ") too large for deepcopy", show arg1, show arg2]

  | StructType _ fts <- typeof arg2
  = concatMap (deepCopyField . fst) fts

  | not (isArray (typeof arg1))
  = [Assign (Just arg1) arg2]
    where deepCopyField fld = deepCopy opts [ ValueParameter $ StructField arg1 fld
                                            , ValueParameter $ StructField arg2 fld]
deepCopy :: Options -> [ActualParameter ()] -> [Program ()]
deepCopy opts [ValueParameter arg1, ValueParameter arg2]
  | arg1 == arg2
  = []

  | ConstExpr ArrayConst{..} <- arg2
  = initArray (Just arg1) (litI32 $ toInteger $ length arrayValues)
    : zipWith (\i c -> Assign (Just $ ArrayElem arg1 (litI32 i)) (ConstExpr c)) [0..] arrayValues

  | NativeArray{} <- typeof arg2
  , l@(ConstExpr (IntConst n _)) <- arrayLength arg2
  = if n < safetyLimit opts
      then initArray (Just arg1) l:map (\i -> Assign (Just $ ArrayElem arg1 (litI32 i)) (ArrayElem arg2 (litI32 i))) [0..(n-1)]
      else error $ unlines ["Internal compiler error: array size (" ++ show n ++ ") too large for deepcopy", show arg1, show arg2]

  | StructType _ fts <- typeof arg2
  = concatMap (deepCopyField . fst) fts

  | not (isArray (typeof arg1))
  = [Assign (Just arg1) arg2]
    where deepCopyField fld = deepCopy opts [ ValueParameter $ StructField arg1 fld
                                            , ValueParameter $ StructField arg2 fld]
deepCopy opts [ValueParameter arg1, ValueParameter arg2]
  | arg1 == arg2
  = []

  | ConstExpr ArrayConst{..} <- arg2
  = initArray (Just arg1) (litI32 $ toInteger $ length arrayValues)
    : zipWith (\i c -> Assign (Just $ ArrayElem arg1 (litI32 i)) (ConstExpr c)) [0..] arrayValues

  | NativeArray{} <- typeof arg2
  , l@(ConstExpr (IntConst n _)) <- arrayLength arg2
  = if n < safetyLimit opts
      then initArray (Just arg1) l:map (\i -> Assign (Just $ ArrayElem arg1 (litI32 i)) (ArrayElem arg2 (litI32 i))) [0..(n-1)]
      else error $ unlines ["Internal compiler error: array size (" ++ show n ++ ") too large for deepcopy", show arg1, show arg2]

  | StructType _ fts <- typeof arg2
  = concatMap (deepCopyField . fst) fts

  | not (isArray (typeof arg1))
  = [Assign (Just arg1) arg2]
    where deepCopyField fld = deepCopy opts [ ValueParameter $ StructField arg1 fld
                                            , ValueParameter $ StructField arg2 fld]