CommonFunctionType.java
package edu.udel.cis.vsl.abc.ast.type.common;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Map;
import edu.udel.cis.vsl.abc.ast.IF.ASTException;
import edu.udel.cis.vsl.abc.ast.type.IF.FunctionType;
import edu.udel.cis.vsl.abc.ast.type.IF.ObjectType;
import edu.udel.cis.vsl.abc.ast.type.IF.QualifiedObjectType;
import edu.udel.cis.vsl.abc.ast.type.IF.Type;
/**
*
* @author siegel
*
*/
public class CommonFunctionType extends CommonType implements FunctionType {
private static int classCode = CommonFunctionType.class.hashCode();
private ObjectType returnType;
private ArrayList<ObjectType> parameterTypes;
private boolean hasVariableArgs;
private boolean fromIdentifierList;
/**
* Constructs a new instance in the case where no information is known about
* the parameters. In this case, fromIdentifierList must be true.
*
* @param returnType
* the type returned by a call to the function
*/
public CommonFunctionType(ObjectType returnType) {
super(TypeKind.FUNCTION);
this.returnType = returnType;
this.fromIdentifierList = true;
this.parameterTypes = null;
this.hasVariableArgs = false;
}
/**
* Constructs a new instance in the case where the information on the
* parameter types is known.
*
* @param returnType
* the type returned by a call to the function
* @param fromIdentifierList
* was this type generated from a declaration with an identifier
* list (as opposed to a parameter-type list)?
* @param parameterTypes
* the type of each parameter
* @param hasVariableArgs
* does the function have a variable number of arguments
* (indicated by a "..." in the parameter type list)?
*/
public CommonFunctionType(ObjectType returnType, boolean fromIdentifierList,
Iterable<ObjectType> parameterTypes, boolean hasVariableArgs) {
super(TypeKind.FUNCTION);
this.returnType = returnType;
this.fromIdentifierList = fromIdentifierList;
this.parameterTypes = new ArrayList<ObjectType>();
for (ObjectType ot : parameterTypes)
this.parameterTypes.add(ot);
this.hasVariableArgs = hasVariableArgs;
}
@Override
public ObjectType getReturnType() {
return returnType;
}
@Override
public boolean hasVariableArgs() {
return hasVariableArgs;
}
// TODO need to make it an option to turn switch checking
@Override
public int getNumParameters() {
if (parameterTypes == null)
return 0;
// throw new ASTException(
// "The parameters for the function have not been specified."
// +
// "\nNote that a prototype for a function with 0 parameters must have
// the form \"f(void)\"");
return parameterTypes.size();
}
@Override
public ObjectType getParameterType(int index) {
if (parameterTypes == null)
throw new ASTException(
"The parameters for the function have not been specified."
+ "\nNote that a prototype for a function with 0 parameters must have the form \"f(void)\"");
return parameterTypes.get(index);
}
@Override
public Iterable<ObjectType> getParameterTypes() {
if (parameterTypes == null)
throw new ASTException(
"The parameters for the function have not been specified."
+ "\nNote that a prototype for a function with 0 parameters must have the form \"f(void)\"");
return parameterTypes;
}
@Override
public boolean isVariablyModified() {
return returnType.isVariablyModified();
}
@Override
public int hashCode() {
int result = classCode + returnType.hashCode();
if (parameterTypes != null) {
for (ObjectType type : parameterTypes)
if (type != null)
result += type.hashCode();
}
if (hasVariableArgs)
result += 128;
if (fromIdentifierList)
result += 256;
return result;
}
@Override
public boolean equals(Object object) {
if (this == object)
return true;
if (object instanceof CommonFunctionType) {
CommonFunctionType that = (CommonFunctionType) object;
if (!returnType.equals(that.returnType))
return false;
if (parameterTypes == null) {
if (that.parameterTypes != null)
return false;
} else {
if (!parameterTypes.equals(that.parameterTypes))
return false;
}
return hasVariableArgs == that.hasVariableArgs
&& fromIdentifierList == that.fromIdentifierList;
}
return false;
}
private boolean equiv(ArrayList<ObjectType> types1,
ArrayList<ObjectType> types2, Map<TypeKey, Type> seen) {
int length1 = types1.size();
int length2 = types2.size();
if (length1 != length2)
return false;
for (int i = 0; i < length1; i++) {
CommonType x1 = (CommonType) types1.get(i);
CommonType x2 = (CommonType) types2.get(i);
if (x1 == null) {
if (x2 != null)
return false;
} else {
if (x2 == null)
return false;
if (!x1.similar(x2, true, seen))
return false;
}
}
return true;
}
private boolean equivalentTo(Type type, Map<TypeKey, Type> seen) {
if (type instanceof CommonFunctionType) {
CommonFunctionType that = (CommonFunctionType) type;
if (!((CommonType) returnType).similar(that.returnType, true, seen))
return false;
if (parameterTypes == null) {
if (that.parameterTypes != null)
return false;
} else {
if (!equiv(parameterTypes, that.parameterTypes, seen))
return false;
}
return hasVariableArgs == that.hasVariableArgs
&& fromIdentifierList == that.fromIdentifierList;
}
return false;
}
/**
* See C11 Sec. 6.7.6.3(15).
*
* Note: "In the determination of type compatibility and of a composite
* type, each parameter declared with function or array type is taken as
* having the adjusted type and each parameter declared with qualified type
* is taken as having the unqualified version of its declared type."
*
* 6.5.2.2(6): "If the expression that denotes the called function has a
* type that does not include a prototype, the integer promotions are
* performed on each argument, and arguments that have type float are
* promoted to double. These are called the default argument promotions."
*/
private boolean compatibleWith(Type type, Map<TypeKey, Type> seen) {
if (type instanceof CommonFunctionType) {
CommonFunctionType that = (CommonFunctionType) type;
if (!returnType.compatibleWith(that.returnType))
// "For two function types to be compatible, both shall specify
// compatible return types."
return false;
if (!this.fromIdentifierList && !that.fromIdentifierList) {
// "the parameter type lists, if both are present, shall agree
// in the number of parameters and in use of the ellipsis
// terminator; corresponding parameters shall have compatible
// types."
int numParameters = getNumParameters();
if (numParameters != that.getNumParameters())
return false;
for (int i = 0; i < numParameters; i++) {
CommonObjectType parameterType1 = (CommonObjectType) this
.getParameterType(i);
ObjectType parameterType2 = that.getParameterType(i);
// C11 6.7.6.3(15):
// "In the determination of type compatibility and of a
// composite type, ... each parameter declared with
// qualified type is taken as having the unqualified
// version of its declared type."
if (parameterType1 instanceof QualifiedObjectType)
parameterType1 = (CommonObjectType) ((QualifiedObjectType) parameterType1)
.getBaseType();
if (parameterType2 instanceof QualifiedObjectType)
parameterType2 = ((QualifiedObjectType) parameterType2)
.getBaseType();
if (!parameterType1.similar(parameterType2, false, seen))
return false;
}
if (this.hasVariableArgs != that.hasVariableArgs)
return false;
return true;
} else if (!this.fromIdentifierList && !that.parametersKnown()) {
// "If one type has a parameter type list and the other type is
// specified by a function declarator that is not part of a
// function definition and that contains an empty identifier
// list, the parameter list shall not have an ellipsis
// terminator and the type of each parameter shall be compatible
// with the type that results from the application of the
// default argument promotions." The application of the default
// argument promotions to what??? Is this referring to
// what happens when the function is called?
if (this.hasVariableArgs)
return false;
return true;
} else if (!that.fromIdentifierList && !this.parametersKnown()) {
// symmetric situation
return that.compatibleWith(this, seen);
} else if (!this.fromIdentifierList
&& (that.fromIdentifierList && that.parametersKnown())) {
// "If one type has a parameter type list and the other type is
// specified by a function definition that contains a (possibly
// empty) identifier list, both shall agree in the number of
// parameters, and the type of each prototype parameter shall be
// compatible with the type that results from the application of
// the default argument promotions to the type of the
// corresponding identifier." In other words, the "real" type
// of the parameter is the one in the parameter-type list; the
// type specified in the function definition has to be
// compatible with that after it is promoted.
int numParameters = getNumParameters();
if (numParameters != that.getNumParameters())
return false;
for (int i = 0; i < numParameters; i++) {
CommonObjectType parameterType1 = (CommonObjectType) this
.getParameterType(i);
CommonObjectType parameterType2 = (CommonObjectType) that
.getParameterType(i);
// TODO: perform default argument promotion to
// parameterType2
if (!parameterType1.similar(parameterType2, false, seen))
return false;
}
if (this.hasVariableArgs != that.hasVariableArgs)
return false;
return true;
} else if (!that.fromIdentifierList
&& (this.fromIdentifierList && this.parametersKnown())) {
// symmetric situation
return that.compatibleWith(this, seen);
} else {
return true;
}
}
return false;
}
@Override
public boolean fromIdentifierList() {
return fromIdentifierList;
}
@Override
public boolean parametersKnown() {
return parameterTypes != null;
}
@Override
public void print(String prefix, PrintStream out, boolean abbrv) {
out.println("Function");
out.print(prefix + "| returnType = ");
returnType.print(prefix + "| | ", out, true);
if (parameterTypes != null) {
out.println();
out.print(prefix + "| parameterTypes");
for (Type type : parameterTypes) {
out.println();
out.print(prefix + "| | ");
type.print(prefix + "| | ", out, true);
}
}
}
@Override
public boolean isScalar() {
return true;
}
@Override
protected boolean similar(Type other, boolean equivalent,
Map<TypeKey, Type> seen) {
return equivalent ? equivalentTo(other, seen)
: compatibleWith(other, seen);
}
@Override
public String toString() {
String result = "";
boolean isFirst = true;
result += returnType.toString();
result += " (";
if (parameterTypes != null)
for (ObjectType parameterType : parameterTypes) {
if (isFirst)
isFirst = false;
else
result += ", ";
result += parameterType.toString();
}
if (hasVariableArgs) {
if (!isFirst)
result += ", ";
result += "...";
}
result += ")";
return result;
}
}