CommonConversionFactory.java

package edu.udel.cis.vsl.abc.ast.conversion.common;

import edu.udel.cis.vsl.abc.ast.conversion.IF.ArithmeticConversion;
import edu.udel.cis.vsl.abc.ast.conversion.IF.ArrayConversion;
import edu.udel.cis.vsl.abc.ast.conversion.IF.CompatiblePointerConversion;
import edu.udel.cis.vsl.abc.ast.conversion.IF.CompatibleStructureOrUnionConversion;
import edu.udel.cis.vsl.abc.ast.conversion.IF.Conversion;
import edu.udel.cis.vsl.abc.ast.conversion.IF.ConversionFactory;
import edu.udel.cis.vsl.abc.ast.conversion.IF.FunctionConversion;
import edu.udel.cis.vsl.abc.ast.conversion.IF.Integer2PointerConversion;
import edu.udel.cis.vsl.abc.ast.conversion.IF.LvalueConversion;
import edu.udel.cis.vsl.abc.ast.conversion.IF.MemConversion;
import edu.udel.cis.vsl.abc.ast.conversion.IF.NullPointerConversion;
import edu.udel.cis.vsl.abc.ast.conversion.IF.Pointer2IntegerConversion;
import edu.udel.cis.vsl.abc.ast.conversion.IF.PointerBoolConversion;
import edu.udel.cis.vsl.abc.ast.conversion.IF.RegularRangeToDomainConversion;
import edu.udel.cis.vsl.abc.ast.conversion.IF.VoidPointerConversion;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.CastNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.ExpressionNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.IntegerConstantNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.WildcardNode;
import edu.udel.cis.vsl.abc.ast.type.IF.ArithmeticType;
import edu.udel.cis.vsl.abc.ast.type.IF.ArrayType;
import edu.udel.cis.vsl.abc.ast.type.IF.AtomicType;
import edu.udel.cis.vsl.abc.ast.type.IF.DomainType;
import edu.udel.cis.vsl.abc.ast.type.IF.FunctionType;
import edu.udel.cis.vsl.abc.ast.type.IF.IntegerType;
import edu.udel.cis.vsl.abc.ast.type.IF.ObjectType;
import edu.udel.cis.vsl.abc.ast.type.IF.PointerType;
import edu.udel.cis.vsl.abc.ast.type.IF.QualifiedObjectType;
import edu.udel.cis.vsl.abc.ast.type.IF.SetType;
import edu.udel.cis.vsl.abc.ast.type.IF.StandardBasicType;
import edu.udel.cis.vsl.abc.ast.type.IF.StandardBasicType.BasicTypeKind;
import edu.udel.cis.vsl.abc.ast.type.IF.StandardUnsignedIntegerType.UnsignedIntKind;
import edu.udel.cis.vsl.abc.ast.type.IF.StructureOrUnionType;
import edu.udel.cis.vsl.abc.ast.type.IF.Type;
import edu.udel.cis.vsl.abc.ast.type.IF.Type.TypeKind;
import edu.udel.cis.vsl.abc.ast.type.IF.TypeFactory;
import edu.udel.cis.vsl.abc.ast.type.IF.UnqualifiedObjectType;
import edu.udel.cis.vsl.abc.config.IF.Configuration;
import edu.udel.cis.vsl.abc.token.IF.SyntaxException;
import edu.udel.cis.vsl.abc.token.IF.UnsourcedException;

public class CommonConversionFactory implements ConversionFactory {

	private TypeFactory typeFactory;

	public CommonConversionFactory(TypeFactory typeFactory) {
		this.typeFactory = typeFactory;
	}

	private UnsourcedException error(String message) {
		return new UnsourcedException(message);
	}

	@Override
	public TypeFactory getTypeFactory() {
		return typeFactory;
	}

	@Override
	public ArithmeticConversion arithmeticConversion(ArithmeticType oldType,
			ArithmeticType newType) {
		return new CommonArithmeticConversion(oldType, newType);
	}

	@Override
	public CompatibleStructureOrUnionConversion compatibleStructureOrUnionConversion(
			StructureOrUnionType type1, StructureOrUnionType type2) {
		return new CommonCompatibleStructureOrUnionConversion(type1, type2);
	}

	@Override
	public CompatiblePointerConversion compatiblePointerConversion(
			PointerType type1, PointerType type2) {
		return new CommonCompatiblePointerConversion(type1, type2);
	}

	@Override
	public VoidPointerConversion voidPointerConversion(PointerType type1,
			PointerType type2) {
		return new CommonVoidPointerConversion(type1, type2);
	}

	@Override
	public NullPointerConversion nullPointerConversion(ObjectType type1,
			PointerType type2) {
		return new CommonNullPointerConversion(type1, type2);
	}

	@Override
	public PointerBoolConversion pointerBoolConversion(PointerType oldType) {
		return new CommonPointerBoolConversion(oldType,
				typeFactory.unsignedIntegerType(UnsignedIntKind.BOOL));
	}

	@Override
	public LvalueConversion lvalueConversion(ObjectType type) {
		UnqualifiedObjectType result = lvalueConversionType(type);

		if (result.equals(type))
			return null;
		return new CommonLvalueConversion(type, result);
	}

	@Override
	public UnqualifiedObjectType lvalueConversionType(ObjectType type) {
		TypeKind kind = type.kind();

		if (kind == TypeKind.QUALIFIED)
			return lvalueConversionType(
					((QualifiedObjectType) type).getBaseType());
		if (kind == TypeKind.ATOMIC)
			return lvalueConversionType(((AtomicType) type).getBaseType());
		return (UnqualifiedObjectType) type;
	}

	@Override
	public ArrayConversion arrayConversion(ObjectType type) {
		// get rid of $input/$output qualifiers on type.getElementType?
		ArrayType arrayType;

		if (type instanceof QualifiedObjectType)
			arrayType = (ArrayType) ((QualifiedObjectType) type).getBaseType();
		else
			arrayType = (ArrayType) type;
		return new CommonArrayConversion(type,
				typeFactory.pointerType(arrayType.getElementType()));
	}

	@Override
	public FunctionConversion functionConversion(FunctionType type) {
		return new CommonFunctionConversion(type,
				typeFactory.pointerType(type));
	}

	private void checkQualifierConsistency(PointerType type1, PointerType type2,
			boolean checkBaseCompatibility) throws UnsourcedException {
		Type base1 = type1.referencedType();
		Type base2 = type2.referencedType();

		// any qualifier on base1 must also occur in base2...
		if (base1 instanceof QualifiedObjectType) {
			QualifiedObjectType qualified1 = (QualifiedObjectType) base1;
			QualifiedObjectType qualified2;

			if (!(base2 instanceof QualifiedObjectType))
				throw error(
						"Referenced type of left-hand of assignment lacks qualifiers of right-hand side");
			qualified2 = (QualifiedObjectType) base2;
			if (qualified1.isConstQualified() && !qualified2.isConstQualified())
				throw error(
						"Type referenced by pointer on left-hand side of assignment"
								+ " lacks const qualifier occurring on right-hand side");
			if (qualified1.isRestrictQualified()
					&& !qualified2.isRestrictQualified())
				throw error(
						"Type referenced by pointer on left-hand side of assignment"
								+ " lacks restrict qualifier occurring on right-hand side");
			if (qualified1.isVolatileQualified()
					&& !qualified2.isVolatileQualified())
				throw error(
						"Type referenced by pointer on left-hand side of assignment"
								+ " lacks volatile qualifier occurring on right-hand side");
			base1 = qualified1.getBaseType();
			base2 = qualified2.getBaseType();
		}
		if (base1 instanceof AtomicType) {
			if (!(base2 instanceof AtomicType))
				throw error(
						"Type referenced by pointer on left-hand side of assigment "
								+ "lacks atomic qualifier occurring on right-hand side");
			base1 = ((AtomicType) base1).getBaseType();
			base2 = ((AtomicType) base2).getBaseType();
		}
		if (checkBaseCompatibility) {
			if (base1 instanceof QualifiedObjectType)
				base1 = ((QualifiedObjectType) base1).getBaseType();
			if (base2 instanceof QualifiedObjectType)
				base2 = ((QualifiedObjectType) base2).getBaseType();
			if (!base1.compatibleWith(base2)) {
				throw error("Type: " + type1
						+ "\n referenced by pointer on left-hand side of assignment "
						+ "is incompatible with corresponding type " + type2
						+ "\n on right-hand side");
			}
		}
	}

	@Override
	public boolean isNullPointerConstant(ExpressionNode node) {
		if (node instanceof CastNode) {
			CastNode castNode = (CastNode) node;
			Type castType = castNode.getCastType().getType();

			if (castType instanceof PointerType && ((PointerType) castType)
					.referencedType().kind() == TypeKind.VOID)
				node = castNode.getArgument();
			else
				return false;
		}
		if (node instanceof IntegerConstantNode)
			return ((IntegerConstantNode) node).getConstantValue()
					.getIntegerValue().signum() == 0;
		return false;
	}

	@Override
	public boolean isPointerToVoid(PointerType type) {
		Type base = type.referencedType();

		if (base instanceof QualifiedObjectType) {
			base = ((QualifiedObjectType) base).getBaseType();
		}
		if (base instanceof AtomicType) {
			base = ((AtomicType) base).getBaseType();
		}
		return base.kind() == TypeKind.VOID;
	}

	@Override
	public boolean isPointerToObject(PointerType type) {
		return type.referencedType() instanceof ObjectType;
	}

	@Override
	public Conversion assignmentConversion(Configuration config,
			ExpressionNode rhs, Type newType) throws UnsourcedException {
		return assignmentConversion(config, rhs, newType, false);
	}

	@Override
	public Conversion assignmentConversion(Configuration config,
			ExpressionNode rhs, Type newType, boolean ignoreQualifier)
			throws UnsourcedException {
		Type oldType = rhs.getConvertedType();

		if (rhs instanceof WildcardNode) {
			// wildcard node has void type, which means that it can be any type
			return null;
		}
		if (typeFactory.isArrayOfCharType(oldType)
				&& typeFactory.isArrayOfCharType(newType))
			return null;
		if (newType.kind() == TypeKind.SCOPE || newType.equals(oldType))
			return null;
		if (oldType instanceof DomainType && newType instanceof DomainType)
			return null;
		if (oldType instanceof ArithmeticType
				&& newType instanceof ArithmeticType) {
			return arithmeticConversion((ArithmeticType) oldType,
					(ArithmeticType) newType);
		}
		if (oldType instanceof StructureOrUnionType
				&& newType instanceof StructureOrUnionType) {
			StructureOrUnionType type1 = (StructureOrUnionType) oldType;
			StructureOrUnionType type2 = (StructureOrUnionType) newType;

			if (!type1.compatibleWith(type2))
				throw error(
						"Assignment to incompatible structure or union type");
			return new CommonCompatibleStructureOrUnionConversion(type1, type2);
		}
		if (newType instanceof PointerType && isNullPointerConstant(rhs))
			return nullPointerConversion((ObjectType) oldType,
					(PointerType) newType);
		if (oldType instanceof PointerType && newType instanceof PointerType) {
			PointerType type1 = (PointerType) oldType;
			PointerType type2 = (PointerType) newType;

			if (isPointerToObject(type1) && isPointerToVoid(type2)
					|| isPointerToObject(type2) && isPointerToVoid(type1)) {
				if (!ignoreQualifier && (config == null || !config.getSVCOMP()))
					checkQualifierConsistency(type1, type2, false);
				return voidPointerConversion(type1, type2);
			}
			if (!ignoreQualifier && (config == null || !config.getSVCOMP()))
				checkQualifierConsistency(type1, type2, true);
			return new CommonCompatiblePointerConversion(type1, type2);
		}
		if (oldType instanceof PointerType && isBool(newType))
			return pointerBoolConversion((PointerType) oldType);
		if (oldType.kind() == TypeKind.LAMBDA
				&& newType.kind() == TypeKind.LAMBDA)
			return null;
		if (config != null && config.getSVCOMP()) {
			if (oldType instanceof PointerType
					&& newType instanceof IntegerType)
				return this.pointer2IntegerConversion((PointerType) oldType,
						(IntegerType) newType);
			if (oldType instanceof IntegerType
					&& newType instanceof PointerType)
				return this.integer2PointerConversion((IntegerType) oldType,
						(PointerType) newType);
		}
		if (newType.kind() == TypeKind.MEM
				&& rhs.getType().kind() == TypeKind.MEM)
			return null;
		throw error(
				"No conversion from type of right hand side to that of left:\n"
						+ oldType + "\n" + newType);
	}

	@Override
	public MemConversion memConversion(ExpressionNode expr)
			throws UnsourcedException, SyntaxException {
		Type type = expr.getType();
		Type elementType = type;

		if (type.kind() != TypeKind.POINTER) {
			if (type.kind() != TypeKind.SET)
				throw error("No conversion from non set-type " + type
						+ " to $mem type.");
			if (((SetType) type).elementType().kind() != TypeKind.POINTER)
				throw error("No conversion from set of non-pointer type " + type
						+ " to $mem type.");
			elementType = ((SetType) type).elementType();
		}
		MemConversionRestriction.check(expr);
		return new CommonMemoryConversion(type,
				typeFactory.memType((PointerType) elementType));
	}

	private boolean isBool(Type type) {
		return type instanceof StandardBasicType && ((StandardBasicType) type)
				.getBasicTypeKind() == BasicTypeKind.BOOL;
	}

	@Override
	public RegularRangeToDomainConversion regularRangeToDomainConversion(
			ObjectType rangeType, DomainType domainType) {
		return new CommonRegularRangeToDomainConversion(rangeType, domainType);
	}

	@Override
	public Pointer2IntegerConversion pointer2IntegerConversion(
			PointerType oldType, IntegerType newType) {
		return new CommonPointer2IntegerConversion(oldType, newType);
	}

	@Override
	public Integer2PointerConversion integer2PointerConversion(
			IntegerType oldType, PointerType newType) {
		return new CommonInteger2PointerConversion(oldType, newType);
	}

}