/******************************************************************************* * * Copyright (C) 2008 Fujitsu Services Ltd. * * Author: Nick Battle * * This file is part of VDMJ. * * VDMJ 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. * * VDMJ 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 VDMJ. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ package org.overture.pog.obligation; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Vector; import org.overture.ast.analysis.AnalysisException; import org.overture.ast.definitions.AExplicitFunctionDefinition; import org.overture.ast.definitions.AExplicitOperationDefinition; import org.overture.ast.definitions.AImplicitFunctionDefinition; import org.overture.ast.definitions.AImplicitOperationDefinition; import org.overture.ast.expressions.ABooleanConstExp; import org.overture.ast.expressions.ACharLiteralExp; import org.overture.ast.expressions.AGreaterEqualNumericBinaryExp; import org.overture.ast.expressions.AGreaterNumericBinaryExp; import org.overture.ast.expressions.AIsExp; import org.overture.ast.expressions.ALenUnaryExp; import org.overture.ast.expressions.ALessEqualNumericBinaryExp; import org.overture.ast.expressions.AMapEnumMapExp; import org.overture.ast.expressions.AMapletExp; import org.overture.ast.expressions.AMkTypeExp; import org.overture.ast.expressions.ANotEqualBinaryExp; import org.overture.ast.expressions.ANotYetSpecifiedExp; import org.overture.ast.expressions.ASeqEnumSeqExp; import org.overture.ast.expressions.ASetEnumSetExp; import org.overture.ast.expressions.ASetRangeSetExp; import org.overture.ast.expressions.ASubclassResponsibilityExp; import org.overture.ast.expressions.ASubseqExp; import org.overture.ast.expressions.ATupleExp; import org.overture.ast.expressions.AVariableExp; import org.overture.ast.expressions.PExp; import org.overture.ast.factory.AstFactory; import org.overture.ast.intf.lex.ILexLocation; import org.overture.ast.lex.LexKeywordToken; import org.overture.ast.lex.LexNameToken; import org.overture.ast.lex.VDMToken; import org.overture.ast.node.INode; import org.overture.ast.patterns.AIdentifierPattern; import org.overture.ast.patterns.APatternListTypePair; import org.overture.ast.patterns.ATuplePattern; import org.overture.ast.patterns.PPattern; import org.overture.ast.types.ABooleanBasicType; import org.overture.ast.types.ACharBasicType; import org.overture.ast.types.AFieldField; import org.overture.ast.types.ANamedInvariantType; import org.overture.ast.types.ANatNumericBasicType; import org.overture.ast.types.ANatOneNumericBasicType; import org.overture.ast.types.AOperationType; import org.overture.ast.types.AProductType; import org.overture.ast.types.ARecordInvariantType; import org.overture.ast.types.ASeq1SeqType; import org.overture.ast.types.ASet1SetType; import org.overture.ast.types.SSetType; import org.overture.ast.types.AUnionType; import org.overture.ast.types.PType; import org.overture.ast.types.SBasicType; import org.overture.ast.types.SInvariantType; import org.overture.ast.types.SMapType; import org.overture.ast.types.SNumericBasicType; import org.overture.ast.types.SSeqType; import org.overture.ast.util.PTypeSet; import org.overture.pog.pub.IPOContextStack; import org.overture.pog.pub.IPogAssistantFactory; import org.overture.pog.pub.POType; public class TypeCompatibilityObligation extends ProofObligation { private static final long serialVersionUID = 1108478780469068741L; public final IPogAssistantFactory assistantFactory; /** * Factory Method since we need to return null STOs (which should be discarded * * @param exp * The expression to be checked * @param etype * The expected type * @param atype * The actual type * @param ctxt * Context Information * @param assistantFactory * @return * @throws AnalysisException */ public static TypeCompatibilityObligation newInstance(PExp exp, PType etype, PType atype, IPOContextStack ctxt, IPogAssistantFactory assistantFactory) throws AnalysisException { TypeCompatibilityObligation sto = new TypeCompatibilityObligation(exp, etype, atype, ctxt, assistantFactory); if (sto.getValueTree() != null) { return sto; } return null; } public static TypeCompatibilityObligation newInstance( AExplicitFunctionDefinition func, PType etype, PType atype, IPOContextStack ctxt, IPogAssistantFactory assistantFactory) throws AnalysisException { TypeCompatibilityObligation sto = new TypeCompatibilityObligation(func, etype, atype, ctxt, assistantFactory); if (sto.getValueTree() != null) { return sto; } return null; } public static TypeCompatibilityObligation newInstance( AImplicitFunctionDefinition func, PType etype, PType atype, IPOContextStack ctxt, IPogAssistantFactory assistantFactory) throws AnalysisException { TypeCompatibilityObligation sto = new TypeCompatibilityObligation(func, etype, atype, ctxt, assistantFactory); if (sto.getValueTree() != null) { return sto; } return null; } public static TypeCompatibilityObligation newInstance( AExplicitOperationDefinition def, PType actualResult, IPOContextStack ctxt, IPogAssistantFactory assistantFactory) throws AnalysisException { TypeCompatibilityObligation sto = new TypeCompatibilityObligation(def, actualResult, ctxt, assistantFactory); if (sto.getValueTree() != null) { return sto; } return null; } public static TypeCompatibilityObligation newInstance( AImplicitOperationDefinition def, PType actualResult, IPOContextStack ctxt, IPogAssistantFactory af) throws AnalysisException { TypeCompatibilityObligation sto = new TypeCompatibilityObligation(def, actualResult, ctxt, af); if (sto.getValueTree() != null) { return sto; } return null; } /** * Help Constructor for the COMPASS Subtype POs <br> * <b> Do not use this constructor directly! </b> Use one of the factory methods instead * * @param root * The root node generating the PO * @param loc * The location of the root node * @param resultexp * The PExp identifying the result to be testes for subtyping * @param deftype * The declared type * @param actualtype * The actual type * @param ctxt * Context Information * @throws AnalysisException */ protected TypeCompatibilityObligation(INode root, ILexLocation loc, PExp resultexp, PType deftype, PType actualtype, IPOContextStack ctxt, IPogAssistantFactory assistantFactory) throws AnalysisException { super(root, POType.TYPE_COMP, ctxt, loc, assistantFactory); this.assistantFactory = assistantFactory; stitch = oneType(false, resultexp, deftype, actualtype); valuetree.setPredicate(ctxt.getPredWithContext(stitch)); } private TypeCompatibilityObligation(PExp exp, PType etype, PType atype, IPOContextStack ctxt, IPogAssistantFactory assistantFactory) throws AnalysisException { super(exp, POType.TYPE_COMP, ctxt, exp.getLocation(), assistantFactory); this.assistantFactory = assistantFactory; // valuetree.setContext(ctxt.getContextNodeList()); PExp onetype_exp = oneType(false, exp.clone(), etype.clone(), atype.clone()); if (onetype_exp == null) { valuetree = null; } else { stitch = onetype_exp; valuetree.setPredicate(ctxt.getPredWithContext(onetype_exp)); } } private TypeCompatibilityObligation(AExplicitFunctionDefinition func, PType etype, PType atype, IPOContextStack ctxt, IPogAssistantFactory assistantFactory) throws AnalysisException { super(func, POType.TYPE_COMP, ctxt, func.getLocation(), assistantFactory); this.assistantFactory = assistantFactory; PExp body = null; if (func.getBody() instanceof ANotYetSpecifiedExp || func.getBody() instanceof ASubclassResponsibilityExp) { // We have to say "f(a)" because we have no body PExp root = AstFactory.newAVariableExp(func.getName()); List<PExp> args = new ArrayList<PExp>(); for (PPattern p : func.getParamPatternList().get(0)) { args.add(patternToExp(p)); } body = AstFactory.newAApplyExp(root, args); } else { body = func.getBody().clone(); } stitch = oneType(false, body, etype.clone(), atype.clone()); valuetree.setPredicate(ctxt.getPredWithContext(stitch)); } private TypeCompatibilityObligation(AImplicitFunctionDefinition func, PType etype, PType atype, IPOContextStack ctxt, IPogAssistantFactory assistantFactory) throws AnalysisException { super(func, POType.TYPE_COMP, ctxt, func.getLocation(), assistantFactory); this.assistantFactory = assistantFactory; PExp body = null; if (func.getBody() instanceof ANotYetSpecifiedExp || func.getBody() instanceof ASubclassResponsibilityExp) { // We have to say "f(a)" because we have no body PExp root = AstFactory.newAVariableExp(func.getName()); List<PExp> args = new ArrayList<PExp>(); for (APatternListTypePair pltp : func.getParamPatterns()) { for (PPattern p : pltp.getPatterns()) { args.add(patternToExp(p)); } } body = AstFactory.newAApplyExp(root, args); } else { body = func.getBody().clone(); } stitch = oneType(false, body, etype.clone(), atype.clone()); valuetree.setPredicate(ctxt.getPredWithContext(stitch)); } private TypeCompatibilityObligation(AExplicitOperationDefinition def, PType actualResult, IPOContextStack ctxt, IPogAssistantFactory assistantFactory) throws AnalysisException { super(def, POType.TYPE_COMP, ctxt, def.getLocation(), assistantFactory); this.assistantFactory = assistantFactory; AVariableExp result = AstFactory.newAVariableExp(new LexNameToken(def.getName().getModule(), "RESULT", def.getLocation())); stitch = oneType(false, result, ((AOperationType) def.getType()).getResult().clone(), actualResult.clone()); valuetree.setPredicate(ctxt.getPredWithContext(stitch)); } private TypeCompatibilityObligation(AImplicitOperationDefinition def, PType actualResult, IPOContextStack ctxt, IPogAssistantFactory assistantFactory) throws AnalysisException { super(def, POType.TYPE_COMP, ctxt, def.getLocation(), assistantFactory); this.assistantFactory = assistantFactory; PExp result = null; if (def.getResult().getPattern() instanceof AIdentifierPattern) { AIdentifierPattern ip = (AIdentifierPattern) def.getResult().getPattern(); result = AstFactory.newAVariableExp(ip.getName()); } else { ATuplePattern tp = (ATuplePattern) def.getResult().getPattern(); List<PExp> args = new ArrayList<PExp>(); for (PPattern p : tp.getPlist()) { AIdentifierPattern ip = (AIdentifierPattern) p; args.add(AstFactory.newAVariableExp(ip.getName())); } result = AstFactory.newATupleExp(def.getLocation(), args); } stitch = oneType(false, result, ((AOperationType) def.getType()).getResult().clone(), actualResult.clone()); valuetree.setPredicate(ctxt.getPredWithContext(stitch)); } private PExp oneType(boolean rec, PExp exp, PType etype, PType atype) { if (atype != null && rec) { if (assistantFactory.getTypeComparator().isSubType(atype, etype)) { return null; // Means a sub-comparison is OK without PO checks } } if (exp.getType() != null && etype != null && rec) { if (assistantFactory.getTypeComparator().isSubType(exp.getType(), etype)) { return null; // Means a sub-comparison is OK without PO checks } } PExp po = null; etype = assistantFactory.createPTypeAssistant().deBracket(etype); if (etype instanceof AUnionType) { AUnionType ut = (AUnionType) etype; PTypeSet possibles = new PTypeSet(assistantFactory); for (PType pos : ut.getTypes()) { if (atype == null || assistantFactory.getTypeComparator().compatible(pos, atype)) { possibles.add(pos); } } po = null; for (PType poss : possibles) { PExp s = oneType(true, exp, poss, null); PExp e = addIs(exp, poss); if (s != null && !(s instanceof AIsExp)) { e = makeAnd(e, s); } po = makeOr(po, e); } } else if (etype instanceof SInvariantType) { SInvariantType et = (SInvariantType) etype; po = null; if (et.getInvDef() != null) { AVariableExp root = getVarExp(et.getInvDef().getName()); root.setType(et.getInvDef().getType().clone()); // This needs to be put back if/when we change the inv_R // signature to take // the record fields as arguments, rather than one R value. // // if (exp instanceof MkTypeExpression) // { // MkTypeExpression mk = (MkTypeExpression)exp; // sb.append(Utils.listToString(mk.args)); // } // else // { // ab.append(exp); // } po = getApplyExp(root, exp); po.setType(new ABooleanBasicType()); } if (etype instanceof ANamedInvariantType) { ANamedInvariantType nt = (ANamedInvariantType) etype; if (atype instanceof ANamedInvariantType) { atype = ((ANamedInvariantType) atype).getType(); } else { atype = null; } PExp s = oneType(true, exp, nt.getType(), atype); if (s != null) { po = makeAnd(po, s); } } else if (etype instanceof ARecordInvariantType) { if (exp instanceof AMkTypeExp) { ARecordInvariantType rt = (ARecordInvariantType) etype; AMkTypeExp mk = (AMkTypeExp) exp; if (rt.getFields().size() == mk.getArgs().size()) { Iterator<AFieldField> fit = rt.getFields().iterator(); Iterator<PType> ait = mk.getArgTypes().iterator(); for (PExp e : mk.getArgs()) { PExp s = oneType(true, e.clone(), fit.next().getType(), ait.next()); if (s != null) { po = makeAnd(po, s); } } } } else { po = makeAnd(po, addIs(exp, etype)); } } else { po = makeAnd(po, addIs(exp, etype)); } } else if (etype instanceof SSeqType) { po = null; if (etype instanceof ASeq1SeqType) { ANotEqualBinaryExp ne = new ANotEqualBinaryExp(); ne.setLeft(exp); ne.setOp(new LexKeywordToken(VDMToken.NE, exp.getLocation())); ASeqEnumSeqExp empty = new ASeqEnumSeqExp(); empty.setMembers(new Vector<PExp>()); ne.setRight(empty); po = ne; } if (exp instanceof ASeqEnumSeqExp) { SSeqType stype = (SSeqType) etype; ASeqEnumSeqExp seq = (ASeqEnumSeqExp) exp; Iterator<PType> it = seq.getTypes().iterator(); for (PExp m : seq.getMembers()) { PExp s = oneType(true, m.clone(), stype.getSeqof().clone(), it.next().clone()); if (s != null) { po = makeAnd(po, s); } } } else if (exp instanceof ASubseqExp) { ASubseqExp subseq = (ASubseqExp) exp; PType itype = AstFactory.newANatOneNumericBasicType(exp.getLocation()); PExp s = oneType(true, subseq.getFrom(), itype, subseq.getFtype()); if (s != null) { po = makeAnd(po, s); } s = oneType(true, subseq.getTo(), itype, subseq.getTtype()); if (s != null) { po = makeAnd(po, s); } ALessEqualNumericBinaryExp le = new ALessEqualNumericBinaryExp(); le.setLeft(subseq.getTo()); ALenUnaryExp len = new ALenUnaryExp(); len.setExp(subseq.getSeq()); le.setRight(len); po = makeAnd(po, le); po = makeAnd(po, addIs(exp, etype)); // Like set range does } else { po = addIs(exp, etype); // remove any "x <> []" } } else if (etype instanceof SMapType) { if (exp instanceof AMapEnumMapExp) { SMapType mtype = (SMapType) etype; AMapEnumMapExp seq = (AMapEnumMapExp) exp; Iterator<PType> dit = seq.getDomTypes().iterator(); Iterator<PType> rit = seq.getRngTypes().iterator(); po = null; for (AMapletExp m : seq.getMembers()) { PExp s = oneType(true, m.getLeft(), mtype.getFrom(), dit.next()); if (s != null) { po = makeAnd(po, s); } s = oneType(true, m.getRight(), mtype.getTo(), rit.next()); if (s != null) { po = makeAnd(po, s); } } } else { po = addIs(exp, etype); } } else if (etype instanceof SSetType) { po = null; if (etype instanceof ASet1SetType) { ANotEqualBinaryExp ne = new ANotEqualBinaryExp(); ne.setLeft(exp); ne.setOp(new LexKeywordToken(VDMToken.NE, exp.getLocation())); ASetEnumSetExp empty = new ASetEnumSetExp(); empty.setMembers(new Vector<PExp>()); ne.setRight(empty); po = ne; } if (exp instanceof ASetEnumSetExp) { SSetType stype = (SSetType) etype; ASetEnumSetExp set = (ASetEnumSetExp) exp; Iterator<PType> it = set.getTypes().iterator(); for (PExp m : set.getMembers()) { PExp s = oneType(true, m.clone(), stype.getSetof(), it.next().clone()); if (s != null) { po = makeAnd(po, s); } } } else if (exp instanceof ASetRangeSetExp) { SSetType stype = (SSetType) etype; ASetRangeSetExp range = (ASetRangeSetExp) exp; PType itype = AstFactory.newAIntNumericBasicType(exp.getLocation()); PExp s = oneType(true, range.getFirst(), itype, range.getFtype()); if (s != null) { po = makeAnd(po, s); } s = oneType(true, range.getFirst(), stype.getSetof(), range.getFtype()); if (s != null) { po = makeAnd(po, s); } s = oneType(true, range.getLast(), itype, range.getLtype()); if (s != null) { po = makeAnd(po, s); } s = oneType(true, range.getLast(), stype.getSetof(), range.getLtype()); if (s != null) { po = makeAnd(po, s); } } po = makeAnd(po, addIs(exp, etype)); } else if (etype instanceof AProductType) { if (exp instanceof ATupleExp) { AProductType pt = (AProductType) etype; ATupleExp te = (ATupleExp) exp; Iterator<PType> eit = pt.getTypes().iterator(); Iterator<PType> ait = te.getTypes().iterator(); po = null; for (PExp e : te.getArgs()) { PExp s = oneType(true, e.clone(), eit.next(), ait.next()); if (s != null) { po = makeAnd(po, s); } } } else { po = addIs(exp, etype); } } else if (etype instanceof SBasicType) { if (etype instanceof SNumericBasicType) { SNumericBasicType ent = (SNumericBasicType) etype; if (atype instanceof SNumericBasicType) { SNumericBasicType ant = (SNumericBasicType) atype; if (assistantFactory.createSNumericBasicTypeAssistant().getWeight(ant) > assistantFactory.createSNumericBasicTypeAssistant().getWeight(ent)) { boolean isWhole = assistantFactory.createSNumericBasicTypeAssistant().getWeight(ant) < 3; if (isWhole && ent instanceof ANatOneNumericBasicType) { AGreaterNumericBinaryExp gt = new AGreaterNumericBinaryExp(); gt.setLeft(exp); gt.setOp(new LexKeywordToken(VDMToken.GT, exp.getLocation())); gt.setRight(getIntLiteral(0)); po = gt; } else if (isWhole && ent instanceof ANatNumericBasicType) { AGreaterEqualNumericBinaryExp ge = new AGreaterEqualNumericBinaryExp(); ge.setLeft(exp); ge.setOp(new LexKeywordToken(VDMToken.GE, exp.getLocation())); ge.setRight(getIntLiteral(0)); po = ge; } else { AIsExp isExp = new AIsExp(); isExp.setBasicType(ent); isExp.setType(new ABooleanBasicType()); isExp.setTest(exp); po = isExp; } } } else { AIsExp isExp = new AIsExp(); isExp.setBasicType(ent); isExp.setType(new ABooleanBasicType()); isExp.setTest(exp); po = isExp; } } else if (etype instanceof ABooleanBasicType) { if (!(exp instanceof ABooleanConstExp)) { po = addIs(exp, etype); } } else if (etype instanceof ACharBasicType) { if (!(exp instanceof ACharLiteralExp)) { po = addIs(exp, etype); } } else { po = addIs(exp, etype); } } else { po = addIs(exp, etype); } return po; } /** * Just produce one is_(<expression>, <type>) node. */ private PExp addIs(PExp exp, PType type) { AIsExp isExp = new AIsExp(); isExp.setBasicType(type); isExp.setType(new ABooleanBasicType()); isExp.setTest(exp.clone()); return isExp; } }