/** * Copyright (c) 2014 committers of YAKINDU and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * Contributors: * committers of YAKINDU - initial API and implementation * */ package org.yakindu.base.expressions.inferrer; import static org.yakindu.base.types.typesystem.ITypeSystem.ANY; import static org.yakindu.base.types.typesystem.ITypeSystem.BOOLEAN; import static org.yakindu.base.types.typesystem.ITypeSystem.INTEGER; import static org.yakindu.base.types.typesystem.ITypeSystem.NULL; import static org.yakindu.base.types.typesystem.ITypeSystem.REAL; import static org.yakindu.base.types.typesystem.ITypeSystem.STRING; import static org.yakindu.base.types.typesystem.ITypeSystem.VOID; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.eclipse.emf.common.util.EList; import org.eclipse.xtext.EcoreUtil2; import org.yakindu.base.expressions.expressions.ArgumentExpression; import org.yakindu.base.expressions.expressions.AssignmentExpression; import org.yakindu.base.expressions.expressions.BitwiseAndExpression; import org.yakindu.base.expressions.expressions.BitwiseOrExpression; import org.yakindu.base.expressions.expressions.BitwiseXorExpression; import org.yakindu.base.expressions.expressions.BoolLiteral; import org.yakindu.base.expressions.expressions.ConditionalExpression; import org.yakindu.base.expressions.expressions.DoubleLiteral; import org.yakindu.base.expressions.expressions.ElementReferenceExpression; import org.yakindu.base.expressions.expressions.Expression; import org.yakindu.base.expressions.expressions.FeatureCall; import org.yakindu.base.expressions.expressions.FloatLiteral; import org.yakindu.base.expressions.expressions.HexLiteral; import org.yakindu.base.expressions.expressions.IntLiteral; import org.yakindu.base.expressions.expressions.LogicalAndExpression; import org.yakindu.base.expressions.expressions.LogicalNotExpression; import org.yakindu.base.expressions.expressions.LogicalOrExpression; import org.yakindu.base.expressions.expressions.LogicalRelationExpression; import org.yakindu.base.expressions.expressions.NullLiteral; import org.yakindu.base.expressions.expressions.NumericalAddSubtractExpression; import org.yakindu.base.expressions.expressions.NumericalMultiplyDivideExpression; import org.yakindu.base.expressions.expressions.NumericalUnaryExpression; import org.yakindu.base.expressions.expressions.ParenthesizedExpression; import org.yakindu.base.expressions.expressions.PrimitiveValueExpression; import org.yakindu.base.expressions.expressions.ShiftExpression; import org.yakindu.base.expressions.expressions.StringLiteral; import org.yakindu.base.expressions.expressions.TypeCastExpression; import org.yakindu.base.expressions.expressions.UnaryOperator; import org.yakindu.base.types.EnumerationType; import org.yakindu.base.types.Enumerator; import org.yakindu.base.types.GenericElement; import org.yakindu.base.types.Operation; import org.yakindu.base.types.Parameter; import org.yakindu.base.types.Property; import org.yakindu.base.types.Type; import org.yakindu.base.types.TypeAlias; import org.yakindu.base.types.TypeParameter; import org.yakindu.base.types.TypeSpecifier; import org.yakindu.base.types.inferrer.AbstractTypeSystemInferrer; import org.yakindu.base.types.validation.IValidationIssueAcceptor; import com.google.common.collect.Maps; import com.google.inject.Inject; /** * @author andreas muelder - Initial contribution and API * */ public class ExpressionsTypeInferrer extends AbstractTypeSystemInferrer implements ExpressionsTypeInferrerMessages { @Inject protected TypeParameterInferrer typeParameterInferrer; public InferenceResult doInfer(AssignmentExpression e) { InferenceResult result1 = inferTypeDispatch(e.getVarRef()); InferenceResult result2 = inferTypeDispatch(e.getExpression()); assertAssignable(result1, result2, String.format(ASSIGNMENT_OPERATOR, e.getOperator(), result1, result2)); return inferTypeDispatch(e.getVarRef()); } public InferenceResult doInfer(ConditionalExpression e) { InferenceResult result1 = inferTypeDispatch(e.getTrueCase()); InferenceResult result2 = inferTypeDispatch(e.getFalseCase()); assertCompatible(result1, result2, String.format(COMMON_TYPE, result1, result2)); assertIsSubType(inferTypeDispatch(e.getCondition()), getResultFor(BOOLEAN), CONDITIONAL_BOOLEAN); return getCommonType(result1, result2); } public InferenceResult doInfer(LogicalOrExpression e) { InferenceResult result1 = inferTypeDispatch(e.getLeftOperand()); InferenceResult result2 = inferTypeDispatch(e.getRightOperand()); assertIsSubType(result1, getResultFor(BOOLEAN), String.format(LOGICAL_OPERATORS, "||", result1, result2)); assertIsSubType(result2, getResultFor(BOOLEAN), String.format(LOGICAL_OPERATORS, "||", result1, result2)); return getResultFor(BOOLEAN); } public InferenceResult doInfer(LogicalAndExpression e) { InferenceResult result1 = inferTypeDispatch(e.getLeftOperand()); InferenceResult result2 = inferTypeDispatch(e.getRightOperand()); assertIsSubType(result1, getResultFor(BOOLEAN), String.format(LOGICAL_OPERATORS, "&&", result1, result2)); assertIsSubType(result2, getResultFor(BOOLEAN), String.format(LOGICAL_OPERATORS, "&&", result1, result2)); return getResultFor(BOOLEAN); } public InferenceResult doInfer(LogicalNotExpression e) { InferenceResult type = inferTypeDispatch(e.getOperand()); assertIsSubType(type, getResultFor(BOOLEAN), String.format(LOGICAL_OPERATOR, "!", type)); return getResultFor(BOOLEAN); } public InferenceResult doInfer(BitwiseXorExpression e) { InferenceResult result1 = inferTypeDispatch(e.getLeftOperand()); InferenceResult result2 = inferTypeDispatch(e.getRightOperand()); assertIsSubType(result1, getResultFor(INTEGER), String.format(BITWISE_OPERATORS, "^", result1, result2)); assertIsSubType(result2, getResultFor(INTEGER), String.format(BITWISE_OPERATORS, "^", result1, result2)); return getResultFor(INTEGER); } public InferenceResult doInfer(BitwiseOrExpression e) { InferenceResult result1 = inferTypeDispatch(e.getLeftOperand()); InferenceResult result2 = inferTypeDispatch(e.getRightOperand()); assertIsSubType(result1, getResultFor(INTEGER), String.format(BITWISE_OPERATORS, "|", result1, result2)); assertIsSubType(result2, getResultFor(INTEGER), String.format(BITWISE_OPERATORS, "|", result1, result2)); return getResultFor(INTEGER); } public InferenceResult doInfer(BitwiseAndExpression e) { InferenceResult result1 = inferTypeDispatch(e.getLeftOperand()); InferenceResult result2 = inferTypeDispatch(e.getRightOperand()); assertIsSubType(result1, getResultFor(INTEGER), String.format(BITWISE_OPERATORS, "&", result1, result2)); assertIsSubType(result2, getResultFor(INTEGER), String.format(BITWISE_OPERATORS, "&", result1, result2)); return getResultFor(INTEGER); } public InferenceResult doInfer(ShiftExpression e) { InferenceResult result1 = inferTypeDispatch(e.getLeftOperand()); InferenceResult result2 = inferTypeDispatch(e.getRightOperand()); assertIsSubType(result1, getResultFor(INTEGER), String.format(BITWISE_OPERATORS, e.getOperator(), result1, result2)); assertIsSubType(result2, getResultFor(INTEGER), String.format(BITWISE_OPERATORS, e.getOperator(), result1, result2)); return getResultFor(INTEGER); } public InferenceResult doInfer(LogicalRelationExpression e) { InferenceResult result1 = inferTypeDispatch(e.getLeftOperand()); InferenceResult result2 = inferTypeDispatch(e.getRightOperand()); assertCompatible(result1, result2, String.format(COMPARSION_OPERATOR, e.getOperator(), result1, result2)); InferenceResult result = getResultFor(BOOLEAN); return result; } public InferenceResult doInfer(NumericalAddSubtractExpression e) { InferenceResult result1 = inferTypeDispatch(e.getLeftOperand()); InferenceResult result2 = inferTypeDispatch(e.getRightOperand()); assertCompatible(result1, result2, String.format(ARITHMETIC_OPERATORS, e.getOperator(), result1, result2)); assertIsSubType(result1, getResultFor(REAL), String.format(ARITHMETIC_OPERATORS, e.getOperator(), result1, result2)); return getCommonType(inferTypeDispatch(e.getLeftOperand()), inferTypeDispatch(e.getRightOperand())); } public InferenceResult doInfer(NumericalMultiplyDivideExpression e) { InferenceResult result1 = inferTypeDispatch(e.getLeftOperand()); InferenceResult result2 = inferTypeDispatch(e.getRightOperand()); assertCompatible(result1, result2, String.format(ARITHMETIC_OPERATORS, e.getOperator(), result1, result2)); assertIsSubType(result1, getResultFor(REAL), String.format(ARITHMETIC_OPERATORS, e.getOperator(), result1, result2)); return getCommonType(result1, result2); } public InferenceResult doInfer(NumericalUnaryExpression e) { InferenceResult result1 = inferTypeDispatch(e.getOperand()); if (e.getOperator() == UnaryOperator.COMPLEMENT) assertIsSubType(result1, getResultFor(INTEGER), String.format(BITWISE_OPERATOR, '~', result1)); else { assertIsSubType(result1, getResultFor(REAL), String.format(ARITHMETIC_OPERATOR, e.getOperator(), result1)); } return result1; } public InferenceResult doInfer(TypeCastExpression e) { InferenceResult result1 = inferTypeDispatch(e.getOperand()); InferenceResult result2 = inferTypeDispatch(e.getType()); assertCompatible(result1, result2, String.format(CAST_OPERATORS, result1, result2)); return inferTypeDispatch(e.getType()); } public InferenceResult doInfer(EnumerationType enumType) { return InferenceResult.from(enumType); } public InferenceResult doInfer(Enumerator enumerator) { return InferenceResult.from(EcoreUtil2.getContainerOfType(enumerator, Type.class)); } public InferenceResult doInfer(Type type) { return InferenceResult.from(type.getOriginType()); } /** * The type of a type alias is its (recursively inferred) base type, i.e. * type aliases are assignable if their inferred base types are assignable. */ public InferenceResult doInfer(TypeAlias typeAlias) { return inferTypeDispatch(typeAlias.getTypeSpecifier()); } public InferenceResult doInfer(FeatureCall e) { // map to hold inference results for type parameters Map<TypeParameter, InferenceResult> inferredTypeParameterTypes = Maps.newHashMap(); typeParameterInferrer.inferTypeParametersFromOwner(inferTypeDispatch(e.getOwner()), inferredTypeParameterTypes); if (e.isOperationCall()) { if (!e.getFeature().eIsProxy()) { return inferOperation(e, (Operation) e.getFeature(), inferredTypeParameterTypes); } else { return getAnyType(); } } InferenceResult result = inferTypeDispatch(e.getFeature()); if (result != null) { result = typeParameterInferrer.buildInferenceResult(result, inferredTypeParameterTypes, acceptor); } if (result == null) { return getAnyType(); } return result; } public InferenceResult doInfer(ElementReferenceExpression e) { if (e.isOperationCall()) { if (!e.getReference().eIsProxy()) { return inferOperation(e, (Operation) e.getReference(), Maps.<TypeParameter, InferenceResult> newHashMap()); } else { return getAnyType(); } } return inferTypeDispatch(e.getReference()); } protected InferenceResult inferOperation(ArgumentExpression e, Operation op, Map<TypeParameter, InferenceResult> typeParameterMapping) { // resolve type parameter from operation call List<InferenceResult> argumentTypes = getArgumentTypes(getOperationArguments(e)); List<Parameter> parameters = op.getParameters(); typeParameterInferrer.inferTypeParametersFromOperationArguments(parameters, argumentTypes, typeParameterMapping, acceptor); validateParameters(typeParameterMapping, op, getOperationArguments(e), acceptor); return inferReturnType(op, typeParameterMapping); } /** * Can be extended to e.g. add operation caller to argument list for * extension methods */ protected List<Expression> getOperationArguments(ArgumentExpression e) { return e.getArgs(); } protected List<InferenceResult> getArgumentTypes(List<Expression> args) { List<InferenceResult> argumentTypes = new ArrayList<>(); for (Expression arg : args) { argumentTypes.add(inferTypeDispatch(arg)); } return argumentTypes; } protected InferenceResult inferReturnType(Operation operation, Map<TypeParameter, InferenceResult> inferredTypeParameterTypes) { InferenceResult returnType = inferTypeDispatch(operation); returnType = typeParameterInferrer.buildInferenceResult(returnType, inferredTypeParameterTypes, acceptor); if(returnType == null) { return getAnyType(); } return returnType; } private InferenceResult getAnyType() { return InferenceResult.from(registry.getType(ANY)); } /** * Takes the operation parameter type and performs a lookup for all * contained type parameters by using the given type parameter inference * map.<br> * The parameter types are validated against the operation call's argument * types. * * @throws TypeParameterInferrenceException */ protected Map<TypeParameter, InferenceResult> validateParameters( Map<TypeParameter, InferenceResult> typeParameterMapping, Operation operation, List<Expression> args, IValidationIssueAcceptor acceptor) { List<Parameter> parameters = operation.getParameters(); if (parameters.size() <= args.size()) { for (int i = 0; i < parameters.size(); i++) { Parameter parameter = parameters.get(i); Expression argument = args.get(i); InferenceResult parameterType = inferTypeDispatch(parameter); InferenceResult argumentType = inferTypeDispatch(argument); parameterType = typeParameterInferrer.buildInferenceResult(parameterType, typeParameterMapping, acceptor); assertCompatible(argumentType, parameterType, String.format(INCOMPATIBLE_TYPES, argumentType, parameterType)); } } if (operation.isVariadic() && args.size() - 1 >= operation.getVarArgIndex()) { Parameter parameter = operation.getParameters().get(operation.getVarArgIndex()); List<Expression> varArgs = args.subList(operation.getVarArgIndex(), args.size() - 1); for (Expression expression : varArgs) { // TODO: handle op(T...) assertArgumentIsCompatible(parameter, expression); } } return typeParameterMapping; } protected void assertArgumentIsCompatible(Parameter parameter, Expression argument) { InferenceResult result1 = inferTypeDispatch(parameter); InferenceResult result2 = inferTypeDispatch(argument); assertCompatible(result2, result1, String.format(INCOMPATIBLE_TYPES, result2, result1)); } public InferenceResult doInfer(ParenthesizedExpression e) { return inferTypeDispatch(e.getExpression()); } public InferenceResult doInfer(PrimitiveValueExpression e) { return inferTypeDispatch(e.getValue()); } public InferenceResult doInfer(BoolLiteral literal) { return getResultFor(BOOLEAN); } public InferenceResult doInfer(IntLiteral literal) { return getResultFor(INTEGER); } public InferenceResult doInfer(HexLiteral literal) { return getResultFor(INTEGER); } public InferenceResult doInfer(DoubleLiteral literal) { return getResultFor(REAL); } public InferenceResult doInfer(FloatLiteral literal) { return getResultFor(REAL); } public InferenceResult doInfer(StringLiteral literal) { return getResultFor(STRING); } public InferenceResult doInfer(NullLiteral literal) { return getResultFor(NULL); } public InferenceResult doInfer(Property p) { InferenceResult type = inferTypeDispatch(p.getTypeSpecifier()); assertNotType(type, VARIABLE_VOID_TYPE, getResultFor(VOID)); return type; } public InferenceResult doInfer(Operation e) { return e.getTypeSpecifier() == null ? getResultFor(VOID) : inferTypeDispatch(e.getTypeSpecifier()); } public InferenceResult doInfer(Parameter e) { return inferTypeDispatch(e.getTypeSpecifier()); } public InferenceResult doInfer(TypeSpecifier specifier) { if (specifier.getType() instanceof GenericElement && ((GenericElement) specifier.getType()).getTypeParameters().size() > 0) { List<InferenceResult> bindings = new ArrayList<>(); EList<TypeSpecifier> arguments = specifier.getTypeArguments(); for (TypeSpecifier typeSpecifier : arguments) { InferenceResult binding = inferTypeDispatch(typeSpecifier); if (binding != null) { bindings.add(binding); } } Type type = inferTypeDispatch(specifier.getType()).getType(); return InferenceResult.from(type, bindings); } return inferTypeDispatch(specifier.getType()); } }