/* * #%~ * VDM Code Generator * %% * Copyright (C) 2008 - 2014 Overture * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #~% */ package org.overture.codegen.trans.uniontypes; import java.util.LinkedList; import java.util.List; import org.overture.ast.types.PType; import org.overture.ast.types.SMapType; import org.overture.ast.types.SSeqType; import org.overture.codegen.assistant.TypeAssistantIR; import org.overture.codegen.ir.INode; import org.overture.codegen.ir.SExpIR; import org.overture.codegen.ir.SStmIR; import org.overture.codegen.ir.STypeIR; import org.overture.codegen.ir.SourceNode; import org.overture.codegen.ir.analysis.AnalysisException; import org.overture.codegen.ir.analysis.DepthFirstAnalysisAdaptor; import org.overture.codegen.ir.declarations.ADefaultClassDeclIR; import org.overture.codegen.ir.declarations.AFieldDeclIR; import org.overture.codegen.ir.declarations.AMethodDeclIR; import org.overture.codegen.ir.declarations.ARecordDeclIR; import org.overture.codegen.ir.declarations.AVarDeclIR; import org.overture.codegen.ir.declarations.SClassDeclIR; import org.overture.codegen.ir.expressions.AApplyExpIR; import org.overture.codegen.ir.expressions.ACardUnaryExpIR; import org.overture.codegen.ir.expressions.ACastUnaryExpIR; import org.overture.codegen.ir.expressions.AElemsUnaryExpIR; import org.overture.codegen.ir.expressions.AFieldExpIR; import org.overture.codegen.ir.expressions.AFieldNumberExpIR; import org.overture.codegen.ir.expressions.AIdentifierVarExpIR; import org.overture.codegen.ir.expressions.AInstanceofExpIR; import org.overture.codegen.ir.expressions.AIntDivNumericBinaryExpIR; import org.overture.codegen.ir.expressions.ALenUnaryExpIR; import org.overture.codegen.ir.expressions.AMapDomainUnaryExpIR; import org.overture.codegen.ir.expressions.AMissingMemberRuntimeErrorExpIR; import org.overture.codegen.ir.expressions.AModNumericBinaryExpIR; import org.overture.codegen.ir.expressions.ANewExpIR; import org.overture.codegen.ir.expressions.ANotUnaryExpIR; import org.overture.codegen.ir.expressions.ANullExpIR; import org.overture.codegen.ir.expressions.ARemNumericBinaryExpIR; import org.overture.codegen.ir.expressions.ASeqConcatBinaryExpIR; import org.overture.codegen.ir.expressions.AUndefinedExpIR; import org.overture.codegen.ir.expressions.SNumericBinaryExpIR; import org.overture.codegen.ir.expressions.SUnaryExpIR; import org.overture.codegen.ir.expressions.SVarExpBase; import org.overture.codegen.ir.expressions.SVarExpIR; import org.overture.codegen.ir.patterns.AIdentifierPatternIR; import org.overture.codegen.ir.statements.AAssignToExpStmIR; import org.overture.codegen.ir.statements.ABlockStmIR; import org.overture.codegen.ir.statements.ACallObjectExpStmIR; import org.overture.codegen.ir.statements.AElseIfStmIR; import org.overture.codegen.ir.statements.AIfStmIR; import org.overture.codegen.ir.statements.APlainCallStmIR; import org.overture.codegen.ir.statements.ARaiseErrorStmIR; import org.overture.codegen.ir.statements.AReturnStmIR; import org.overture.codegen.ir.statements.ASuperCallStmIR; import org.overture.codegen.ir.statements.SCallStmIR; import org.overture.codegen.ir.types.ABoolBasicTypeIR; import org.overture.codegen.ir.types.AClassTypeIR; import org.overture.codegen.ir.types.AErrorTypeIR; import org.overture.codegen.ir.types.AIntNumericBasicTypeIR; import org.overture.codegen.ir.types.AMethodTypeIR; import org.overture.codegen.ir.types.ARealNumericBasicTypeIR; import org.overture.codegen.ir.types.ARecordTypeIR; import org.overture.codegen.ir.types.ATupleTypeIR; import org.overture.codegen.ir.types.AUnionTypeIR; import org.overture.codegen.ir.types.AUnknownTypeIR; import org.overture.codegen.ir.types.SMapTypeIR; import org.overture.codegen.ir.types.SSeqTypeIR; import org.overture.codegen.trans.assistants.TransAssistantIR; public class UnionTypeTrans extends DepthFirstAnalysisAdaptor { public static final String MISSING_OP_MEMBER = "Missing operation member: "; public static final String MISSING_MEMBER = "Missing member: "; private TransAssistantIR transAssistant; private UnionTypeVarPrefixes unionTypePrefixes; private List<INode> cloneFreeNodes; public UnionTypeTrans(TransAssistantIR transAssistant, UnionTypeVarPrefixes unionTypePrefixes, List<INode> cloneFreeNodes) { this.transAssistant = transAssistant; this.unionTypePrefixes = unionTypePrefixes; this.cloneFreeNodes = cloneFreeNodes; } private interface TypeFinder<T extends STypeIR> { public T findType(PType type) throws org.overture.ast.analysis.AnalysisException; } public <T extends STypeIR> T searchType(SExpIR exp, TypeFinder<T> typeFinder) { if (exp == null || exp.getType() == null) { return null; } SourceNode sourceNode = exp.getType().getSourceNode(); if (sourceNode == null) { return null; } org.overture.ast.node.INode vdmTypeNode = sourceNode.getVdmNode(); if (vdmTypeNode instanceof PType) { try { PType vdmType = (PType) vdmTypeNode; return typeFinder.findType(vdmType); } catch (org.overture.ast.analysis.AnalysisException e) { } } return null; } private SExpIR correctTypes(SExpIR exp, STypeIR castedType) throws AnalysisException { if ((exp.getType() instanceof AUnknownTypeIR || exp.getType() instanceof AUnionTypeIR) && !(exp instanceof ACastUnaryExpIR)) { ACastUnaryExpIR casted = new ACastUnaryExpIR(); casted.setType(castedType.clone()); casted.setExp(exp.clone()); transAssistant.replaceNodeWith(exp, casted); return casted; } return exp; } private boolean correctArgTypes(List<SExpIR> args, List<STypeIR> paramTypes) throws AnalysisException { if (transAssistant.getInfo().getAssistantManager().getTypeAssistant().checkArgTypes(transAssistant.getInfo(), args, paramTypes)) { for (int k = 0; k < paramTypes.size(); k++) { SExpIR arg = args.get(k); if (!(arg instanceof ANullExpIR)) { correctTypes(arg, paramTypes.get(k)); } } return true; } return false; } private boolean handleUnaryExp(SUnaryExpIR exp) throws AnalysisException { STypeIR type = exp.getExp().getType(); if (type instanceof AUnionTypeIR) { org.overture.ast.node.INode vdmNode = type.getSourceNode().getVdmNode(); if (vdmNode instanceof PType) { return true; } } return false; } private AInstanceofExpIR consInstanceCheck(SExpIR copy, STypeIR type) { AInstanceofExpIR check = new AInstanceofExpIR(); check.setType(new ABoolBasicTypeIR()); check.setCheckedType(type.clone()); check.setExp(copy.clone()); return check; } @Override public void defaultInSNumericBinaryExpIR(SNumericBinaryExpIR node) throws AnalysisException { STypeIR expectedType; if (transAssistant.getInfo().getTypeAssistant().isNumericType(node.getType())) { expectedType = node.getType(); } else { expectedType = getExpectedOperandType(node); } correctTypes(node.getLeft(), expectedType); correctTypes(node.getRight(), expectedType); } public STypeIR getExpectedOperandType(SNumericBinaryExpIR node) { if (node instanceof AIntDivNumericBinaryExpIR || node instanceof AModNumericBinaryExpIR || node instanceof ARemNumericBinaryExpIR) { return new AIntNumericBasicTypeIR(); } else { return new ARealNumericBasicTypeIR(); } } @Override public void caseAFieldDeclIR(AFieldDeclIR node) throws AnalysisException { if (node.getInitial() != null) { if (node.getInitial().getType() instanceof AUnionTypeIR) { correctTypes(node.getInitial(), node.getType()); } node.getInitial().apply(this); } } @Override public void caseACardUnaryExpIR(ACardUnaryExpIR node) throws AnalysisException { STypeIR type = node.getExp().getType(); if (type instanceof AUnionTypeIR) { STypeIR expectedType = transAssistant.getInfo().getTypeAssistant().getSetType((AUnionTypeIR) type); correctTypes(node.getExp(), expectedType); } node.getExp().apply(this); node.getType().apply(this); } @Override public void caseALenUnaryExpIR(ALenUnaryExpIR node) throws AnalysisException { STypeIR type = node.getExp().getType(); if (type instanceof AUnionTypeIR) { STypeIR expectedType = transAssistant.getInfo().getTypeAssistant().getSeqType((AUnionTypeIR) type); correctTypes(node.getExp(), expectedType); } node.getExp().apply(this); node.getType().apply(this); } @Override public void caseASeqConcatBinaryExpIR(ASeqConcatBinaryExpIR node) throws AnalysisException { node.getLeft().apply(this); node.getRight().apply(this); node.getType().apply(this); if (!transAssistant.getInfo().getTypeAssistant().usesUnionType(node)) { return; } STypeIR leftType = node.getLeft().getType(); if (leftType instanceof AUnionTypeIR) { STypeIR expectedType = transAssistant.getInfo().getTypeAssistant().getSeqType((AUnionTypeIR) leftType); correctTypes(node.getLeft(), expectedType); } STypeIR rightType = node.getRight().getType(); if (rightType instanceof AUnionTypeIR) { STypeIR expectedType = transAssistant.getInfo().getTypeAssistant().getSeqType((AUnionTypeIR) rightType); correctTypes(node.getRight(), expectedType); } } @Override public void caseAFieldNumberExpIR(AFieldNumberExpIR node) throws AnalysisException { SExpIR tuple = node.getTuple(); STypeIR tupleType = tuple.getType(); if (!(tupleType instanceof AUnionTypeIR)) { tuple.apply(this); return; } handleFieldExp(node, "field number " + node.getField(), tuple, tupleType, node.getType().clone()); } @Override public void caseAFieldExpIR(AFieldExpIR node) throws AnalysisException { SExpIR object = node.getObject(); STypeIR objectType = object.getType(); if (!(objectType instanceof AUnionTypeIR)) { object.apply(this); return; } STypeIR resultType = getResultType(node, node.parent(), objectType, transAssistant.getInfo().getTypeAssistant()); handleFieldExp(node, node.getMemberName(), object, objectType, resultType); } private void handleFieldExp(SExpIR node, String memberName, SExpIR subject, STypeIR fieldObjType, STypeIR resultType) throws AnalysisException { INode parent = node.parent(); TypeAssistantIR typeAssistant = transAssistant.getInfo().getAssistantManager().getTypeAssistant(); SStmIR enclosingStatement = transAssistant.getEnclosingStm(node, "field expression"); String applyResultName = transAssistant.getInfo().getTempVarNameGen().nextVarName(unionTypePrefixes.applyExp()); AIdentifierPatternIR id = new AIdentifierPatternIR(); id.setName(applyResultName); AVarDeclIR resultDecl = transAssistant.getInfo().getDeclAssistant().consLocalVarDecl(node.getSourceNode().getVdmNode(), resultType, id, transAssistant.getInfo().getExpAssistant().consUndefinedExp()); AIdentifierVarExpIR resultVar = new AIdentifierVarExpIR(); resultVar.setSourceNode(node.getSourceNode()); resultVar.setIsLambda(false); resultVar.setIsLocal(true); resultVar.setName(applyResultName); resultVar.setType(resultDecl.getType().clone()); ABlockStmIR replacementBlock = new ABlockStmIR(); SExpIR obj = null; if (!(subject instanceof SVarExpBase)) { String objName = transAssistant.getInfo().getTempVarNameGen().nextVarName(unionTypePrefixes.objExp()); AIdentifierPatternIR objId = new AIdentifierPatternIR(); objId.setName(objName); AVarDeclIR objectDecl = transAssistant.getInfo().getDeclAssistant().consLocalVarDecl(subject.getType().clone(), objId, subject.clone()); replacementBlock.getLocalDefs().add(objectDecl); AIdentifierVarExpIR objectVar = new AIdentifierVarExpIR(); objectVar.setIsLambda(false); objectVar.setIsLocal(true); objectVar.setName(objName); objectVar.setType(objectDecl.getType().clone()); obj = objectVar; } else { obj = subject.clone(); } List<STypeIR> possibleTypes = ((AUnionTypeIR) fieldObjType).getTypes(); possibleTypes = typeAssistant.clearDuplicates(possibleTypes); AIfStmIR ifChecks = new AIfStmIR(); int handledTypes = 0; for (int i = 0; i < possibleTypes.size(); i++) { SExpIR fieldExp = (SExpIR) node.clone(); STypeIR currentType = possibleTypes.get(i); if (currentType instanceof AUnknownTypeIR) { // If we are accessing an element of (say) the sequence [new A(), new B(), nil] of type A | B | [?] // then the current IR type will be the unknown type at some point. This case is simply skipped. continue; } if (!(currentType instanceof AClassTypeIR) && !(currentType instanceof ATupleTypeIR) && !(currentType instanceof ARecordTypeIR)) { // If the field cannot possibly exist then continue continue; } boolean memberExists = false; memberExists = memberExists(memberName, parent, typeAssistant, fieldExp, currentType); if (!memberExists) { // If the member does not exist then the case should not be treated continue; } ACastUnaryExpIR castedFieldExp = new ACastUnaryExpIR(); castedFieldExp.setType(currentType.clone()); castedFieldExp.setExp(obj.clone()); setSubject(fieldExp, castedFieldExp); AAssignToExpStmIR assignment = new AAssignToExpStmIR(); cloneFreeNodes.add(assignment); assignment.setTarget(resultVar.clone()); assignment.setExp(getAssignmentExp(node, fieldExp)); if (handledTypes == 0) { ifChecks.setIfExp(consInstanceCheck(obj, currentType)); ifChecks.setThenStm(assignment); } else { AElseIfStmIR elseIf = new AElseIfStmIR(); elseIf.setElseIf(consInstanceCheck(obj, currentType)); elseIf.setThenStm(assignment); ifChecks.getElseIf().add(elseIf); } handledTypes++; } if (handledTypes == 0) { return; } ARaiseErrorStmIR raise = consRaiseStm(MISSING_MEMBER, memberName); ifChecks.setElseStm(raise); if (parent instanceof AApplyExpIR && ((AApplyExpIR) parent).getRoot() == node) { transAssistant.replaceNodeWith(parent, resultVar); } else { transAssistant.replaceNodeWith(node, resultVar); } replacementBlock.getLocalDefs().add(resultDecl); replacementBlock.getStatements().add(ifChecks); transAssistant.replaceNodeWith(enclosingStatement, replacementBlock); replacementBlock.getStatements().add(enclosingStatement); ifChecks.apply(this); } private void setSubject(SExpIR fieldExp, ACastUnaryExpIR castedFieldExp) { if (fieldExp instanceof AFieldExpIR) { ((AFieldExpIR) fieldExp).setObject(castedFieldExp); } else if (fieldExp instanceof AFieldNumberExpIR) { ((AFieldNumberExpIR) fieldExp).setTuple(castedFieldExp); } } private boolean memberExists(String memberName, INode parent, TypeAssistantIR typeAssistant, SExpIR fieldExp, STypeIR currentType) throws AnalysisException { if (fieldExp instanceof AFieldExpIR) { if (currentType instanceof AClassTypeIR) { String className = ((AClassTypeIR) currentType).getName(); return memberExists(parent, typeAssistant, className, memberName); } else if (currentType instanceof ARecordTypeIR) { ARecordTypeIR recordType = (ARecordTypeIR) currentType; return transAssistant.getInfo().getDeclAssistant().getFieldDecl(transAssistant.getInfo().getClasses(), recordType, memberName) != null; } } else if (fieldExp instanceof AFieldNumberExpIR && currentType instanceof ATupleTypeIR) { return true; // Could possibly be strengthened // AFieldNumberExpIR fieldNumberExp = (AFieldNumberExpIR) fieldExp; // return fieldNumberExp.getField() <= ((ATupleTypeIR) currentType).getTypes().size(); } return false; } private boolean memberExists(INode parent, TypeAssistantIR typeAssistant, String className, String memberName) throws AnalysisException { if (typeAssistant.getFieldType(transAssistant.getInfo().getClasses(), className, memberName) != null) { return true; } List<SExpIR> args = ((AApplyExpIR) parent).getArgs(); return typeAssistant.getMethodType(transAssistant.getInfo(), className, memberName, args) != null; } @Override public void caseAApplyExpIR(AApplyExpIR node) throws AnalysisException { for (SExpIR arg : node.getArgs()) { arg.apply(this); } SExpIR root = node.getRoot(); root.apply(this); if (root.getType() instanceof AUnionTypeIR) { STypeIR colType = searchType(root, new TypeFinder<SMapTypeIR>() { @Override public SMapTypeIR findType(PType type) throws org.overture.ast.analysis.AnalysisException { SMapType mapType = transAssistant.getInfo().getTcFactory().createPTypeAssistant().getMap(type); return mapType != null ? (SMapTypeIR) mapType.apply(transAssistant.getInfo().getTypeVisitor(), transAssistant.getInfo()) : null; } }); if (colType == null) { colType = searchType(root, new TypeFinder<SSeqTypeIR>() { @Override public SSeqTypeIR findType(PType type) throws org.overture.ast.analysis.AnalysisException { SSeqType seqType = transAssistant.getInfo().getTcFactory().createPTypeAssistant().getSeq(type); return seqType != null ? (SSeqTypeIR) seqType.apply(transAssistant.getInfo().getTypeVisitor(), transAssistant.getInfo()) : null; } }); } if (colType != null && node.getArgs().size() == 1) { correctTypes(root, colType); return; } } else if (root.getType() instanceof AMethodTypeIR) { AMethodTypeIR methodType = (AMethodTypeIR) root.getType(); LinkedList<STypeIR> paramTypes = methodType.getParams(); LinkedList<SExpIR> args = node.getArgs(); correctArgTypes(args, paramTypes); } } @Override public void inANotUnaryExpIR(ANotUnaryExpIR node) throws AnalysisException { correctTypes(node.getExp(), new ABoolBasicTypeIR()); } @Override public void inANewExpIR(ANewExpIR node) throws AnalysisException { LinkedList<SExpIR> args = node.getArgs(); boolean hasUnionTypes = false; for (SExpIR arg : args) { if (arg.getType() instanceof AUnionTypeIR) { hasUnionTypes = true; break; } } if (!hasUnionTypes) { return; } STypeIR type = node.getType(); if (type instanceof AClassTypeIR) { for (SClassDeclIR classCg : transAssistant.getInfo().getClasses()) { for (AMethodDeclIR method : classCg.getMethods()) { if (!method.getIsConstructor()) { continue; } if (correctArgTypes(args, method.getMethodType().getParams())) { return; } } } } else if (type instanceof ARecordTypeIR) { ARecordTypeIR recordType = (ARecordTypeIR) type; String definingClassName = recordType.getName().getDefiningClass(); String recordName = recordType.getName().getName(); SClassDeclIR classDecl = transAssistant.getInfo().getAssistantManager().getDeclAssistant().findClass(transAssistant.getInfo().getClasses(), definingClassName); ARecordDeclIR record = transAssistant.getInfo().getAssistantManager().getDeclAssistant().findRecord(classDecl, recordName); List<STypeIR> fieldTypes = transAssistant.getInfo().getAssistantManager().getTypeAssistant().getFieldTypes(record); if (correctArgTypes(args, fieldTypes)) { return; } } } @Override public void inAIfStmIR(AIfStmIR node) throws AnalysisException { ABoolBasicTypeIR expectedType = new ABoolBasicTypeIR(); correctTypes(node.getIfExp(), expectedType); LinkedList<AElseIfStmIR> elseIfs = node.getElseIf(); for (AElseIfStmIR currentElseIf : elseIfs) { correctTypes(currentElseIf.getElseIf(), expectedType); } } @Override public void caseAPlainCallStmIR(APlainCallStmIR node) throws AnalysisException { STypeIR classType = node.getClassType(); String className = classType instanceof AClassTypeIR ? ((AClassTypeIR) classType).getName() : node.getAncestor(ADefaultClassDeclIR.class).getName(); handleCallStm(node, className); } @Override public void caseASuperCallStmIR(ASuperCallStmIR node) throws AnalysisException { handleCallStm(node, transAssistant.getInfo().getStmAssistant().getSuperClassName(node)); } private void handleCallStm(SCallStmIR node, String className) throws AnalysisException { for (SExpIR arg : node.getArgs()) { arg.apply(this); } String fieldName = node.getName(); LinkedList<SExpIR> args = node.getArgs(); TypeAssistantIR typeAssistant = transAssistant.getInfo().getAssistantManager().getTypeAssistant(); AMethodTypeIR methodType = typeAssistant.getMethodType(transAssistant.getInfo(), className, fieldName, args); if (methodType != null) { correctArgTypes(args, methodType.getParams()); } } @SuppressWarnings("unchecked") @Override public void inACallObjectExpStmIR(ACallObjectExpStmIR node) throws AnalysisException { for (SExpIR arg : node.getArgs()) { arg.apply(this); } SExpIR objExp = node.getObj(); STypeIR objType = objExp.getType(); if (!(objType instanceof AUnionTypeIR)) { return; } STypeIR type = node.getType(); LinkedList<SExpIR> args = node.getArgs(); // String className = node.getClassName(); String fieldName = node.getFieldName(); SourceNode sourceNode = node.getSourceNode(); ACallObjectExpStmIR call = new ACallObjectExpStmIR(); call.setObj(objExp); call.setType(type.clone()); call.setArgs((List<? extends SExpIR>) args.clone()); // call.setClassName(className); call.setFieldName(fieldName); call.setSourceNode(sourceNode); ABlockStmIR replacementBlock = new ABlockStmIR(); if (!(objExp instanceof SVarExpIR)) { String callStmObjName = transAssistant.getInfo().getTempVarNameGen().nextVarName(unionTypePrefixes.callStmObj()); AIdentifierPatternIR id = new AIdentifierPatternIR(); id.setName(callStmObjName); AVarDeclIR objDecl = transAssistant.getInfo().getDeclAssistant().consLocalVarDecl(node.getSourceNode().getVdmNode(), objType.clone(), id, objExp.clone()); AIdentifierVarExpIR objVar = new AIdentifierVarExpIR(); objVar.setSourceNode(node.getSourceNode()); objVar.setIsLambda(false); objVar.setIsLocal(true); objVar.setName(callStmObjName); objVar.setType(objDecl.getType().clone()); objExp = objVar; replacementBlock.getLocalDefs().add(objDecl); } TypeAssistantIR typeAssistant = transAssistant.getInfo().getAssistantManager().getTypeAssistant(); LinkedList<STypeIR> possibleTypes = ((AUnionTypeIR) objType).getTypes(); AIfStmIR ifChecks = new AIfStmIR(); int handledTypes = 0; for (int i = 0; i < possibleTypes.size(); i++) { ACallObjectExpStmIR callCopy = call.clone(); AClassTypeIR currentType = (AClassTypeIR) possibleTypes.get(i); AMethodTypeIR methodType = typeAssistant.getMethodType(transAssistant.getInfo(), currentType.getName(), fieldName, args); if (methodType != null) { correctArgTypes(callCopy.getArgs(), methodType.getParams()); } else { // It's possible (due to the way union types work) that the method type for the // field in the object type does not exist. Let's say we are trying to invoke the // operation 'op' for an object type that is either A or B but it might be the // case that only 'A' has the operation 'op' defined. continue; } ACastUnaryExpIR castedVarExp = new ACastUnaryExpIR(); castedVarExp.setType(currentType.clone()); castedVarExp.setExp(objExp.clone()); callCopy.setObj(castedVarExp); if (handledTypes == 0) { ifChecks.setIfExp(consInstanceCheck(objExp, currentType)); ifChecks.setThenStm(callCopy); } else { AElseIfStmIR elseIf = new AElseIfStmIR(); elseIf.setElseIf(consInstanceCheck(objExp, currentType)); elseIf.setThenStm(callCopy); ifChecks.getElseIf().add(elseIf); } handledTypes++; } if (handledTypes == 0) { return; } ARaiseErrorStmIR raiseStm = consRaiseStm(MISSING_OP_MEMBER, fieldName); ifChecks.setElseStm(raiseStm); replacementBlock.getStatements().add(ifChecks); transAssistant.replaceNodeWith(node, replacementBlock); ifChecks.apply(this); } private ARaiseErrorStmIR consRaiseStm(String prefix, String fieldName) { AMissingMemberRuntimeErrorExpIR missingMember = new AMissingMemberRuntimeErrorExpIR(); missingMember.setType(new AErrorTypeIR()); missingMember.setMessage(prefix + fieldName); ARaiseErrorStmIR raise = new ARaiseErrorStmIR(); raise.setError(missingMember); return raise; } @Override public void inAVarDeclIR(AVarDeclIR node) throws AnalysisException { SExpIR exp = node.getExp(); if (exp != null) { exp.apply(this); } STypeIR type = node.getType(); if (castNotNeeded(exp, type)) { return; } if (!(type instanceof AUnionTypeIR)) { correctTypes(exp, type); } } @Override public void caseAAssignToExpStmIR(AAssignToExpStmIR node) throws AnalysisException { handAssignRighHandSide(node); handleAssignTarget(node); } public void handleAssignTarget(AAssignToExpStmIR node) throws AnalysisException { if (node.getTarget() instanceof AFieldExpIR) { AFieldExpIR field = (AFieldExpIR) node.getTarget(); if (field.getObject().getType() instanceof AUnionTypeIR) { LinkedList<STypeIR> types = ((AUnionTypeIR) field.getObject().getType()).getTypes(); AIfStmIR ifChecks = new AIfStmIR(); for (int i = 0; i < types.size(); i++) { STypeIR currentType = types.get(i); AInstanceofExpIR cond = consInstanceCheck(field.getObject(), currentType); AAssignToExpStmIR castFieldObj = castFieldObj(node, field, currentType); if (i == 0) { ifChecks.setIfExp(cond); ifChecks.setThenStm(castFieldObj); } else { AElseIfStmIR elseIf = new AElseIfStmIR(); elseIf.setElseIf(cond); elseIf.setThenStm(castFieldObj); ifChecks.getElseIf().add(elseIf); } } ifChecks.setElseStm(consRaiseStm(MISSING_MEMBER, field.getMemberName())); transAssistant.replaceNodeWith(node, ifChecks); ifChecks.apply(this); } } } public void handAssignRighHandSide(AAssignToExpStmIR node) throws AnalysisException { if (node.getExp() != null) { node.getExp().apply(this); } if (!castNotNeeded(node.getExp(), node.getTarget().getType())) { if (!(node.getTarget().getType() instanceof AUnionTypeIR)) { correctTypes(node.getExp(), node.getTarget().getType()); } } } public AAssignToExpStmIR castFieldObj(AAssignToExpStmIR assign, AFieldExpIR target, STypeIR possibleType) { ACastUnaryExpIR cast = new ACastUnaryExpIR(); cast.setType(possibleType.clone()); cast.setExp(target.getObject().clone()); AAssignToExpStmIR assignCopy = assign.clone(); AFieldExpIR fieldCopy = target.clone(); transAssistant.replaceNodeWith(fieldCopy.getObject(), cast); transAssistant.replaceNodeWith(assignCopy.getTarget(), fieldCopy); return assignCopy; } private boolean castNotNeeded(SExpIR exp, STypeIR type) { return type instanceof AUnknownTypeIR || exp instanceof ANullExpIR || exp instanceof AUndefinedExpIR; } @Override public void caseAReturnStmIR(AReturnStmIR node) throws AnalysisException { if (node.getExp() == null) { return; // When the return type of the method is 'void' } if (node.getExp() instanceof ANullExpIR) { return; } node.getExp().apply(this); AMethodDeclIR methodDecl = node.getAncestor(AMethodDeclIR.class); STypeIR expectedType = methodDecl.getMethodType().getResult(); if (expectedType instanceof AUnknownTypeIR) { return; } if (!(expectedType instanceof AUnionTypeIR)) { correctTypes(node.getExp(), expectedType); } } @Override public void inAElemsUnaryExpIR(AElemsUnaryExpIR node) throws AnalysisException { if (handleUnaryExp(node)) { SExpIR exp = node.getExp(); PType vdmType = (PType) exp.getType().getSourceNode().getVdmNode(); SSeqType seqType = transAssistant.getInfo().getTcFactory().createPTypeAssistant().getSeq(vdmType); try { STypeIR typeCg = seqType.apply(transAssistant.getInfo().getTypeVisitor(), transAssistant.getInfo()); if (typeCg instanceof SSeqTypeIR) { correctTypes(exp, typeCg); } } catch (org.overture.ast.analysis.AnalysisException e) { } } } @Override public void inAMapDomainUnaryExpIR(AMapDomainUnaryExpIR node) throws AnalysisException { if (handleUnaryExp(node)) { SExpIR exp = node.getExp(); PType vdmType = (PType) exp.getType().getSourceNode().getVdmNode(); SMapType mapType = transAssistant.getInfo().getTcFactory().createPTypeAssistant().getMap(vdmType); try { STypeIR typeCg = mapType.apply(transAssistant.getInfo().getTypeVisitor(), transAssistant.getInfo()); if (typeCg instanceof SMapTypeIR) { correctTypes(exp, typeCg); } } catch (org.overture.ast.analysis.AnalysisException e) { } } } private SExpIR getAssignmentExp(INode node, SExpIR fieldExp) { INode parent = node.parent(); if (parent instanceof AApplyExpIR && ((AApplyExpIR) parent).getRoot() == node) { AApplyExpIR applyExp = (AApplyExpIR) parent.clone(); applyExp.setRoot(fieldExp); return applyExp; } else { return fieldExp; } } private STypeIR getResultType(AFieldExpIR node, INode parent, STypeIR fieldObjType, TypeAssistantIR typeAssistant) { if (parent instanceof SExpIR) { if (parent instanceof AApplyExpIR && ((AApplyExpIR) parent).getRoot() == node) { return ((SExpIR) parent).getType().clone(); } } return fieldType(node, fieldObjType, typeAssistant); } private STypeIR fieldType(AFieldExpIR node, STypeIR objectType, TypeAssistantIR typeAssistant) { List<STypeIR> fieldTypes = new LinkedList<STypeIR>(); List<STypeIR> types = ((AUnionTypeIR) objectType).getTypes(); for (STypeIR currentType : types) { String memberName = node.getMemberName(); STypeIR fieldType = null; if (currentType instanceof AClassTypeIR) { AClassTypeIR classType = (AClassTypeIR) currentType; fieldType = typeAssistant.getFieldType(transAssistant.getInfo().getClasses(), classType.getName(), memberName); } else if (currentType instanceof ARecordTypeIR) { ARecordTypeIR recordType = (ARecordTypeIR) currentType; fieldType = transAssistant.getInfo().getTypeAssistant().getFieldType(transAssistant.getInfo().getClasses(), recordType, memberName); } else { // Can be the unknown type continue; } if (fieldType == null) { // The field type may not be found if the member does not exist // For example: // // types // R1 :: x : int; // R2 :: y : int; // ... // let inlines : seq of Inline = [mk_R1(4), mk_R2(5)] // in // return inlines(1).x + inlines(2).y; continue; } if (!typeAssistant.containsType(fieldTypes, fieldType)) { fieldTypes.add(fieldType); } } if (fieldTypes.size() == 1) { return fieldTypes.get(0); } else { AUnionTypeIR unionTypes = new AUnionTypeIR(); unionTypes.setTypes(fieldTypes); return unionTypes; } } }