package exm.stc.ic.aliases;

import java.util.Collections;
import java.util.List;

import exm.stc.common.exceptions.STCRuntimeError;
import exm.stc.common.exceptions.TypeMismatchException;
import exm.stc.common.lang.Arg;
import exm.stc.common.lang.Types;
import exm.stc.common.lang.Types.StructType;
import exm.stc.common.lang.Types.Type;
import exm.stc.common.lang.Types.Typed;
import exm.stc.common.lang.Var;

/**
 * Data class to store alias info about an alias that was created
 */
public class Alias {

  public static final List<Alias> NONE = Collections.emptyList();

  public final Var parent;
  public final List<String> fieldPath;
  public final AliasTransform transform;
  public final Var child;

  public Alias(Var parent, List<String> fieldPath,
               AliasTransform transform, Var child) {
    super();
    this.parent = parent;
    this.fieldPath = fieldPath;
    this.transform = transform;
    this.child = child;
  }

  public List<Alias> asList() {
    return Collections.singletonList(this);
  }

  public static enum AliasTransform {
    IDENTITY, // Just a plain alias
    RETRIEVE, // Retrieved value of field
    COPY, // Copied field
  }
  
  /**
   * Helper to build appropriate alias for struct
   * 
   * @param struct
   * @param fieldPath
   * @param val
   * @param derefed
   *          if val is dereferenced type for struct field
   * @return
   */
  public static List<Alias> makeStructAliases(Var struct, List<String> fieldPath,
      Var val, AliasTransform transform) {
    if (transform == AliasTransform.RETRIEVE ||
        transform == AliasTransform.COPY) {
      // Value or copy of field - only relevant if field is a reference
      if (fieldIsRef(struct, fieldPath)) {
        return new Alias(struct, fieldPath, transform, val).asList();
      } else {
        // Value of field - not an alias
        return Alias.NONE;
      }
    } else {
      // Straightforward alias of field
      return new Alias(struct, fieldPath, transform, val).asList();
    }
  }

  public static List<Alias> makeStructAliases2(Var struct, List<Arg> fieldPath,
      Var val, AliasTransform transform) {
    return makeStructAliases(struct, Arg.extractStrings(fieldPath), val,
                             transform);
  }

  /**
   * Return true if field of struct is a reference
   * 
   * @param struct
   * @param fieldPath
   * @return
   */
  public static boolean fieldIsRef(Typed struct, List<String> fieldPath) {
    StructType type = (StructType) struct.type().getImplType();
    Type fieldType;
    try {
      fieldType = type.getFieldTypeByPath(fieldPath);
    } catch (TypeMismatchException e) {
      // Should only happen if we allowed bad IR to get generated
      throw new STCRuntimeError(e.getMessage());
    }
    assert (fieldType != null);
    return Types.isRef(fieldType);
  }

}
@@ -2447,24 +2447,176 @@
 ieldPath) %7B%0A
+    assert(Types.isStruct(struct) %7C%7C Types.isStructRef(struct));%0A    if (Types.isStructRef(struct)) %7B%0A      struct = Types.retrievedType(struct);%0A    %7D%0A
     StructTy

Support struct ref aliases