/* * #%~ * The VDM Type Checker * %% * 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.typechecker.visitor; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.Vector; import java.util.Map.Entry; import org.overture.ast.analysis.AnalysisException; import org.overture.ast.analysis.intf.IQuestionAnswer; import org.overture.ast.definitions.AExplicitOperationDefinition; import org.overture.ast.definitions.AImplicitOperationDefinition; import org.overture.ast.definitions.AInstanceVariableDefinition; import org.overture.ast.definitions.AMultiBindListDefinition; import org.overture.ast.definitions.PDefinition; import org.overture.ast.definitions.SClassDefinition; import org.overture.ast.expressions.ABooleanConstExp; import org.overture.ast.expressions.ASelfExp; import org.overture.ast.expressions.AVariableExp; import org.overture.ast.expressions.PExp; import org.overture.ast.factory.AstFactory; import org.overture.ast.intf.lex.ILexNameToken; import org.overture.ast.lex.Dialect; import org.overture.ast.lex.LexNameToken; import org.overture.ast.lex.LexStringToken; import org.overture.ast.patterns.ADefPatternBind; import org.overture.ast.patterns.AExpressionPattern; import org.overture.ast.patterns.ASetBind; import org.overture.ast.patterns.ATypeBind; import org.overture.ast.patterns.PBind; import org.overture.ast.statements.AAlwaysStm; import org.overture.ast.statements.AAssignmentStm; import org.overture.ast.statements.AAtomicStm; import org.overture.ast.statements.ABlockSimpleBlockStm; import org.overture.ast.statements.ACallObjectStm; import org.overture.ast.statements.ACallStm; import org.overture.ast.statements.ACaseAlternativeStm; import org.overture.ast.statements.ACasesStm; import org.overture.ast.statements.AClassInvariantStm; import org.overture.ast.statements.ACyclesStm; import org.overture.ast.statements.ADurationStm; import org.overture.ast.statements.AElseIfStm; import org.overture.ast.statements.AErrorCase; import org.overture.ast.statements.AErrorStm; import org.overture.ast.statements.AExitStm; import org.overture.ast.statements.AExternalClause; import org.overture.ast.statements.AForAllStm; import org.overture.ast.statements.AForIndexStm; import org.overture.ast.statements.AForPatternBindStm; import org.overture.ast.statements.AIdentifierStateDesignator; import org.overture.ast.statements.AIfStm; import org.overture.ast.statements.ALetBeStStm; import org.overture.ast.statements.ALetStm; import org.overture.ast.statements.ANonDeterministicSimpleBlockStm; import org.overture.ast.statements.ANotYetSpecifiedStm; import org.overture.ast.statements.APeriodicStm; import org.overture.ast.statements.AReturnStm; import org.overture.ast.statements.ASkipStm; import org.overture.ast.statements.ASpecificationStm; import org.overture.ast.statements.ASporadicStm; import org.overture.ast.statements.AStartStm; import org.overture.ast.statements.AStopStm; import org.overture.ast.statements.ASubclassResponsibilityStm; import org.overture.ast.statements.ATixeStm; import org.overture.ast.statements.ATixeStmtAlternative; import org.overture.ast.statements.ATrapStm; import org.overture.ast.statements.AWhileStm; import org.overture.ast.statements.PStateDesignator; import org.overture.ast.statements.PStm; import org.overture.ast.statements.SSimpleBlockStm; import org.overture.ast.typechecker.NameScope; import org.overture.ast.types.ABooleanBasicType; import org.overture.ast.types.AClassType; import org.overture.ast.types.AFunctionType; import org.overture.ast.types.AOperationType; import org.overture.ast.types.SSetType; import org.overture.ast.types.AUnionType; import org.overture.ast.types.AUnknownType; import org.overture.ast.types.AVoidReturnType; import org.overture.ast.types.AVoidType; import org.overture.ast.types.PType; import org.overture.ast.util.PTypeSet; import org.overture.config.Settings; import org.overture.typechecker.Environment; import org.overture.typechecker.FlatCheckedEnvironment; import org.overture.typechecker.FlatEnvironment; import org.overture.typechecker.PrivateClassEnvironment; import org.overture.typechecker.PublicClassEnvironment; import org.overture.typechecker.TypeCheckInfo; import org.overture.typechecker.TypeCheckerErrors; import org.overture.typechecker.assistant.type.AClassTypeAssistantTC; import org.overture.typechecker.utilities.type.QualifiedDefinition; public class TypeCheckerStmVisitor extends AbstractTypeCheckVisitor { public TypeCheckerStmVisitor( IQuestionAnswer<TypeCheckInfo, PType> typeCheckVisitor) { super(typeCheckVisitor); } @Override public PType caseAAlwaysStm(AAlwaysStm node, TypeCheckInfo question) throws AnalysisException { node.getAlways().apply(THIS, question); node.setType(node.getBody().apply(THIS, question)); return node.getType(); } @Override public PType caseAAssignmentStm(AAssignmentStm node, TypeCheckInfo question) throws AnalysisException { node.setTargetType(node.getTarget().apply(THIS, new TypeCheckInfo(question.assistantFactory, question.env))); node.setExpType(node.getExp().apply(THIS, new TypeCheckInfo(question.assistantFactory, question.env, question.scope, null, node.getTargetType(), null))); if (!question.assistantFactory.getTypeComparator().compatible(node.getTargetType(), node.getExpType())) { TypeCheckerErrors.report(3239, "Incompatible types in assignment", node.getLocation(), node); TypeCheckerErrors.detail2("Target", node.getTarget().getType(), "Expression", node.getExp().getType()); } node.setClassDefinition(question.env.findClassDefinition()); node.setStateDefinition(question.env.findStateDefinition()); AClassTypeAssistantTC assist = question.assistantFactory.createAClassTypeAssistant(); node.setInConstructor(assist.inConstructor(question.env)); if (node.getInConstructor()) { // Mark assignment target as initialized (so no warnings) PDefinition state; state = targetDefinition(node.getTarget(), question); if (state instanceof AInstanceVariableDefinition) { AInstanceVariableDefinition iv = (AInstanceVariableDefinition) state; iv.setInitialized(true); } } node.setType(AstFactory.newAVoidType(node.getLocation())); return node.getType(); } @Override public PType caseAAtomicStm(AAtomicStm node, TypeCheckInfo question) throws AnalysisException { for (AAssignmentStm stmt : node.getAssignments()) { stmt.apply(THIS, question); } node.setType(AstFactory.newAVoidType(node.getLocation())); return node.getType(); } @Override public PType caseAForPatternBindStm(AForPatternBindStm node, TypeCheckInfo question) throws AnalysisException { PType stype = node.getExp().apply(THIS, new TypeCheckInfo(question.assistantFactory, question.env, question.scope)); Environment local = question.env; if (question.assistantFactory.createPTypeAssistant().isSeq(stype)) { node.setSeqType(question.assistantFactory.createPTypeAssistant().getSeq(stype)); node.getPatternBind().apply(THIS, new TypeCheckInfo(question.assistantFactory, question.env, question.scope)); List<PDefinition> defs = getDefinitions(node.getPatternBind()); question.assistantFactory.createPDefinitionListAssistant().typeCheck(defs, THIS, new TypeCheckInfo(question.assistantFactory, question.env, question.scope)); local = new FlatCheckedEnvironment(question.assistantFactory, defs, question.env, question.scope); } else { TypeCheckerErrors.report(3223, "Expecting sequence type after 'in'", node.getLocation(), node); } PType rt = node.getStatement().apply(THIS, new TypeCheckInfo(question.assistantFactory, local, question.scope)); local.unusedCheck(); node.setType(rt); return rt; } @Override public PType defaultSSimpleBlockStm(SSimpleBlockStm node, TypeCheckInfo question) throws AnalysisException { boolean notreached = false; PTypeSet rtypes = new PTypeSet(question.assistantFactory); PType last = null; for (PStm stmt : node.getStatements()) { PType stype = stmt.apply(THIS, question); if (notreached) { TypeCheckerErrors.warning(5006, "Statement will not be reached", stmt.getLocation(), stmt); } else { last = stype; notreached = true; if (stype instanceof AUnionType) { AUnionType ust = (AUnionType) stype; for (PType t : ust.getTypes()) { addOneType(rtypes, t); if (t instanceof AVoidType || t instanceof AUnknownType) { notreached = false; } } } else { addOneType(rtypes, stype); if (stype instanceof AVoidType || stype instanceof AUnknownType) { notreached = false; } } } } // If the last statement reached has a void component, add this to the // overall // return type, as the block may return nothing. if (last != null && (question.assistantFactory.createPTypeAssistant().isType(last, AVoidType.class) || question.assistantFactory.createPTypeAssistant().isUnknown(last))) { rtypes.add(AstFactory.newAVoidType(node.getLocation())); } node.setType(rtypes.isEmpty() ? AstFactory.newAVoidType(node.getLocation()) : rtypes.getType(node.getLocation())); return node.getType(); } @Override public PType caseABlockSimpleBlockStm(ABlockSimpleBlockStm node, TypeCheckInfo question) throws AnalysisException { // Each dcl definition is in scope for later definitions... Environment local = question.env; for (PDefinition d : node.getAssignmentDefs()) { local = new FlatCheckedEnvironment(question.assistantFactory, d, local, question.scope); // cumulative question.assistantFactory.createPDefinitionAssistant().implicitDefinitions(d, local); d.setType(question.assistantFactory.createPTypeAssistant().typeResolve(d.getType(), null, THIS, question)); d.apply(THIS, new TypeCheckInfo(question.assistantFactory, local, question.scope, question.qualifiers, d.getType(), question.returnType)); } // For type checking purposes, the definitions are treated as // local variables. At runtime (below) they have to be treated // more like (updatable) state. PType r = defaultSSimpleBlockStm(node, new TypeCheckInfo(question.assistantFactory, local, question.scope, null, null, question.returnType)); local.unusedCheck(question.env); node.setType(r); return r; } @Override public PType caseACallObjectStm(ACallObjectStm node, TypeCheckInfo question) throws AnalysisException { PType dtype = node.getDesignator().apply(THIS, question); if (!question.assistantFactory.createPTypeAssistant().isClass(dtype, question.env)) { TypeCheckerErrors.report(3207, "Object designator is not an object type", node.getLocation(), node); node.setType(AstFactory.newAUnknownType(node.getLocation())); return node.getType(); } AClassType ctype = question.assistantFactory.createPTypeAssistant().getClassType(dtype, question.env); SClassDefinition classdef = ctype.getClassdef(); SClassDefinition self = question.env.findClassDefinition(); Environment classenv = null; if (self == classdef || question.assistantFactory.createPDefinitionAssistant().hasSupertype(self, question.assistantFactory.createPDefinitionAssistant().getType(classdef))) { // All fields visible. Note that protected fields are inherited // into "locals" so they are effectively private classenv = new PrivateClassEnvironment(question.assistantFactory, self); } else { // Only public fields externally visible classenv = new PublicClassEnvironment(question.assistantFactory, classdef); } if (node.getClassname() == null) { node.setField(new LexNameToken(ctype.getName().getName(), node.getFieldname().getName(), node.getFieldname().getLocation())); } else { node.setField(node.getClassname()); } node.getField().getLocation().executable(true); List<PType> atypes = getArgTypes(node.getArgs(), THIS, question); node.getField().setTypeQualifier(atypes); PDefinition fdef = classenv.findName(node.getField(), question.scope); AClassTypeAssistantTC assist = question.assistantFactory.createAClassTypeAssistant(); if (assist.isConstructor(fdef) && !assist.inConstructor(question.env)) { TypeCheckerErrors.report(3337, "Cannot call a constructor from here", node.getLocation(), node); node.setType(AstFactory.newAUnknownType(node.getLocation())); return node.getType(); } // Special code for the deploy method of CPU if (Settings.dialect == Dialect.VDM_RT && node.getField().getModule().equals("CPU") && node.getField().getName().equals("deploy")) { if (!question.assistantFactory.createPTypeAssistant().isType(atypes.get(0), AClassType.class)) { TypeCheckerErrors.report(3280, "Argument to deploy must be an object", node.getArgs().get(0).getLocation(), node.getArgs().get(0)); } node.setType(AstFactory.newAVoidType(node.getLocation())); return node.getType(); } else if (Settings.dialect == Dialect.VDM_RT && node.getField().getModule().equals("CPU") && node.getField().getName().equals("setPriority")) { if (!(atypes.get(0) instanceof AOperationType)) { TypeCheckerErrors.report(3290, "Argument to setPriority must be an operation", node.getArgs().get(0).getLocation(), node.getArgs().get(0)); } else { // Convert the variable expression to a string... AVariableExp a1 = (AVariableExp) node.getArgs().get(0); node.getArgs().remove(0); node.getArgs().add(0, AstFactory.newAStringLiteralExp(new LexStringToken(a1.getName().getExplicit(true).getFullName(), a1.getLocation()))); if (a1.getName().getModule().equals(a1.getName().getName())) // it's a // constructor { TypeCheckerErrors.report(3291, "Argument to setPriority cannot be a constructor", node.getArgs().get(0).getLocation(), node.getArgs().get(0)); } } node.setType(AstFactory.newAVoidType(node.getLocation())); return node.getType(); } else if (fdef == null) { TypeCheckerErrors.report(3209, "Member " + node.getField() + " is not in scope", node.getLocation(), node); node.setType(AstFactory.newAUnknownType(node.getLocation())); return node.getType(); } else { question.assistantFactory.createPDefinitionAssistant(); if (question.assistantFactory.createPDefinitionAssistant().isStatic(fdef) && !question.env.isStatic()) { // warning(5005, "Should invoke member " + field + // " from a static context"); } } PType type = question.assistantFactory.createPDefinitionAssistant().getType(fdef); if (question.assistantFactory.createPTypeAssistant().isOperation(type)) { AOperationType optype = question.assistantFactory.createPTypeAssistant().getOperation(type); PDefinition encl = question.env.getEnclosingDefinition(); if (encl != null && encl.getAccess().getPure() && !optype.getPure()) { TypeCheckerErrors.report(3339, "Cannot call impure operation from a pure operation", node.getLocation(), node); } optype.apply(THIS, question); node.getField().setTypeQualifier(optype.getParameters()); checkArgTypes(type, optype.getParameters(), atypes, question); // Not // necessary? node.setType(optype.getResult()); return question.assistantFactory.createPTypeAssistant().checkReturnType(question.returnType, node.getType(), node.getLocation()); } else if (question.assistantFactory.createPTypeAssistant().isFunction(type)) { // This is the case where a function is called as an operation // without // a "return" statement. AFunctionType ftype = question.assistantFactory.createPTypeAssistant().getFunction(type); ftype.apply(THIS, question); node.getField().setTypeQualifier(ftype.getParameters()); checkArgTypes(type, ftype.getParameters(), atypes, question); // Not // necessary? node.setType(ftype.getResult()); return question.assistantFactory.createPTypeAssistant().checkReturnType(question.returnType, node.getType(), node.getLocation()); } else { TypeCheckerErrors.report(3210, "Object member is neither a function nor an operation", node.getLocation(), node); node.setType(AstFactory.newAUnknownType(node.getLocation())); return node.getType(); } } @Override public PType caseACallStm(ACallStm node, TypeCheckInfo question) throws AnalysisException { List<PType> atypes = getArgTypes(node.getArgs(), THIS, question); if (question.env.isVDMPP()) { node.getName().setTypeQualifier(atypes); } PDefinition opdef = question.env.findName(node.getName(), question.scope); node.setRootdef(opdef); if (opdef == null) { TypeCheckerErrors.report(3213, "Operation " + node.getName() + " is not in scope", node.getLocation(), node); question.env.listAlternatives(node.getName()); node.setType(AstFactory.newAUnknownType(node.getLocation())); return node.getType(); } if (question.env.isVDMPP() && node.getName().getExplicit()) { // A call like X`op() is local if X is in our hierarchy // else it's a static call of a different class. SClassDefinition self = question.env.findClassDefinition(); PType ctype = opdef.getClassDefinition().getType(); if (!question.assistantFactory.createPDefinitionAssistant().hasSupertype(self, ctype) && opdef.getAccess().getStatic() == null) { TypeCheckerErrors.report(3324, "Operation " + node.getName() + " is not static", node.getLocation(), node); node.setType(AstFactory.newAUnknownType(node.getLocation())); return node.getType(); } } AClassTypeAssistantTC assist = question.assistantFactory.createAClassTypeAssistant(); if (assist.isConstructor(opdef) && !assist.inConstructor(question.env)) { TypeCheckerErrors.report(3337, "Cannot call a constructor from here", node.getLocation(), node); node.setType(AstFactory.newAUnknownType(node.getLocation())); return node.getType(); } if (!question.assistantFactory.createPDefinitionAssistant().isStatic(opdef) && question.env.isStatic()) { TypeCheckerErrors.report(3214, "Cannot call " + node.getName() + " from static context", node.getLocation(), node); node.setType(AstFactory.newAUnknownType(node.getLocation())); return node.getType(); } PType type = question.assistantFactory.createPDefinitionAssistant().getType(opdef); if (question.assistantFactory.createPTypeAssistant().isOperation(type)) { AOperationType optype = question.assistantFactory.createPTypeAssistant().getOperation(type); question.assistantFactory.createPTypeAssistant().typeResolve(optype, null, THIS, question); PDefinition encl = question.env.getEnclosingDefinition(); if (encl != null && encl.getAccess().getPure() && !optype.getPure()) { TypeCheckerErrors.report(3339, "Cannot call impure operation from a pure operation", node.getLocation(), node); } // Reset the name's qualifier with the actual operation type so // that runtime search has a simple TypeComparator call. if (question.env.isVDMPP()) { node.getName().setTypeQualifier(optype.getParameters()); } checkArgTypes(node, optype, optype.getParameters(), atypes, question); node.setType(optype.getResult()); return question.assistantFactory.createPTypeAssistant().checkReturnType(question.returnType, optype.getResult(), node.getLocation()); } else if (question.assistantFactory.createPTypeAssistant().isFunction(type)) { // This is the case where a function is called as an operation // without // a "return" statement. AFunctionType ftype = question.assistantFactory.createPTypeAssistant().getFunction(type); question.assistantFactory.createPTypeAssistant().typeResolve(ftype, null, THIS, question); // Reset the name's qualifier with the actual function type so // that runtime search has a simple TypeComparator call. if (question.env.isVDMPP()) { node.getName().setTypeQualifier(ftype.getParameters()); } checkArgTypes(node, ftype, ftype.getParameters(), atypes, question); node.setType(ftype.getResult()); return question.assistantFactory.createPTypeAssistant().checkReturnType(question.returnType, ftype.getResult(), node.getLocation()); } else { TypeCheckerErrors.report(3210, "Name is neither a function nor an operation", node.getLocation(), node); node.setType(AstFactory.newAUnknownType(node.getLocation())); return node.getType(); } } // TODO correct, possibly wrong typecheck implementation @Override public PType caseACaseAlternativeStm(ACaseAlternativeStm node, TypeCheckInfo question) throws AnalysisException { if (node.getDefs().size() == 0) { node.setDefs(new LinkedList<PDefinition>()); question.assistantFactory.createPPatternAssistant().typeResolve(node.getPattern(), THIS, question); if (node.getPattern() instanceof AExpressionPattern) { // Only expression patterns need type checking... AExpressionPattern ep = (AExpressionPattern) node.getPattern(); PType ptype = ep.getExp().apply(THIS, question); if (!question.assistantFactory.getTypeComparator().compatible(ptype, node.getCtype())) { TypeCheckerErrors.report(3311, "Pattern cannot match", node.getPattern().getLocation(), node.getPattern()); } } question.assistantFactory.createPPatternAssistant().typeResolve(node.getPattern(), THIS, question); ACasesStm stm = (ACasesStm) node.parent(); node.getDefs().addAll(question.assistantFactory.createPPatternAssistant().getDefinitions(node.getPattern(), stm.getExp().getType(), NameScope.LOCAL)); } question.assistantFactory.createPDefinitionListAssistant().typeCheck(node.getDefs(), THIS, question); if (!question.assistantFactory.createPPatternAssistant().matches(node.getPattern(), node.getCtype())) { TypeCheckerErrors.report(3311, "Pattern cannot match", node.getPattern().getLocation(), node.getPattern()); } Environment local = new FlatCheckedEnvironment(question.assistantFactory, node.getDefs(), question.env, question.scope); PType r = node.getResult().apply(THIS, new TypeCheckInfo(question.assistantFactory, local, question.scope, null, null, question.returnType)); local.unusedCheck(); return r; } @Override public PType caseACasesStm(ACasesStm node, TypeCheckInfo question) throws AnalysisException { PType expType = node.getExp().apply(THIS, question); PTypeSet rtypes = new PTypeSet(question.assistantFactory); for (ACaseAlternativeStm c : node.getCases()) { c.setCtype(expType); rtypes.add(c.apply(THIS, question)); } if (node.getOthers() != null) { rtypes.add(node.getOthers().apply(THIS, question)); } else { rtypes.add(AstFactory.newAVoidType(node.getLocation())); } node.setType(rtypes.getType(node.getLocation())); return node.getType(); } @Override public PType caseAClassInvariantStm(AClassInvariantStm node, TypeCheckInfo question) { // Definitions already checked. node.setType(AstFactory.newABooleanBasicType(node.getLocation())); return node.getType(); } @Override public PType caseACyclesStm(ACyclesStm node, TypeCheckInfo question) throws AnalysisException { PDefinition encl = question.env.getEnclosingDefinition(); if (encl != null && encl.getAccess().getPure()) { TypeCheckerErrors.report(3346, "Cannot use cycles in pure operations", node.getLocation(), node); } Environment newEnv = new FlatEnvironment(question.assistantFactory, question.env, true); TypeCheckInfo functional = question.newInfo(newEnv); PType argType = node.getCycles().apply(THIS, functional); if (!question.assistantFactory.getTypeComparator().compatible(AstFactory.newANatNumericBasicType(node.getLocation()), argType)) { TypeCheckerErrors.report(3282, "Arguments to cycles must be a nat", node.getLocation(), node); TypeCheckerErrors.detail("Actual", argType); } return node.getStatement().apply(THIS, question); } // TODO: Missing the other DefStatement @Override public PType caseALetStm(ALetStm node, TypeCheckInfo question) throws AnalysisException { node.setType(typeCheckLet(node, node.getLocalDefs(),node.getStatement(),question)); return node.getType(); } @Override public PType caseADurationStm(ADurationStm node, TypeCheckInfo question) throws AnalysisException { PDefinition encl = question.env.getEnclosingDefinition(); if (encl != null && encl.getAccess().getPure()) { TypeCheckerErrors.report(3346, "Cannot use duration in pure operations", node.getLocation(), node); } Environment newEnv = new FlatEnvironment(question.assistantFactory, question.env, true); TypeCheckInfo functional = question.newInfo(newEnv); PType argType = node.getDuration().apply(THIS, functional); if (!question.assistantFactory.getTypeComparator().compatible(AstFactory.newANatNumericBasicType(node.getLocation()), argType)) { TypeCheckerErrors.report(3281, "Arguments to duration must be a nat", node.getLocation(), node); TypeCheckerErrors.detail("Actual", argType); } return node.getStatement().apply(THIS, question); } @Override public PType caseAElseIfStm(AElseIfStm node, TypeCheckInfo question) throws AnalysisException { node.setType(typeCheckAElseIf(node, node.getLocation(), node.getElseIf(), node.getThenStm(), question)); return node.getType(); } @Override public PType caseAErrorStm(AErrorStm node, TypeCheckInfo question) { node.setType(AstFactory.newAUnknownType(node.getLocation())); return node.getType(); // Because we terminate anyway } @Override public PType caseAExitStm(AExitStm node, TypeCheckInfo question) throws AnalysisException { if (node.getExpression() != null) { node.setExpType(node.getExpression().apply(THIS, question)); } // This is unknown because the statement doesn't actually return a // value - so if this is the only statement in a body, it is not a // type error (should return the same type as the definition return // type). node.setType(AstFactory.newAUnknownType(node.getLocation())); return node.getType(); } @Override public PType caseAForAllStm(AForAllStm node, TypeCheckInfo question) throws AnalysisException { node.setType(node.getSet().apply(THIS, question)); question.assistantFactory.createPPatternAssistant().typeResolve(node.getPattern(), THIS, question); if (question.assistantFactory.createPTypeAssistant().isSet(node.getType())) { SSetType st = question.assistantFactory.createPTypeAssistant().getSet(node.getType()); List<PDefinition> defs = question.assistantFactory.createPPatternAssistant().getDefinitions(node.getPattern(), st.getSetof(), NameScope.LOCAL); Environment local = new FlatCheckedEnvironment(question.assistantFactory, defs, question.env, question.scope); PType rt = node.getStatement().apply(THIS, new TypeCheckInfo(question.assistantFactory, local, question.scope)); local.unusedCheck(); node.setType(rt); return rt; } else { TypeCheckerErrors.report(3219, "For all statement does not contain a set type", node.getLocation(), node); node.setType(AstFactory.newAUnknownType(node.getLocation())); return node.getType(); } } @Override public PType caseAForIndexStm(AForIndexStm node, TypeCheckInfo question) throws AnalysisException { PType ft = node.getFrom().apply(THIS, question); PType tt = node.getTo().apply(THIS, question); if (!question.assistantFactory.createPTypeAssistant().isNumeric(ft)) { TypeCheckerErrors.report(3220, "From type is not numeric", node.getLocation(), node); } if (!question.assistantFactory.createPTypeAssistant().isNumeric(tt)) { TypeCheckerErrors.report(3221, "To type is not numeric", node.getLocation(), node); } if (node.getBy() != null) { PType bt = node.getBy().apply(THIS, question); if (!question.assistantFactory.createPTypeAssistant().isNumeric(bt)) { TypeCheckerErrors.report(3222, "By type is not numeric", node.getLocation(), node); } } PDefinition vardef = AstFactory.newALocalDefinition(node.getVar().getLocation(), node.getVar(), NameScope.LOCAL, ft); Environment local = new FlatCheckedEnvironment(question.assistantFactory, vardef, question.env, question.scope); PType rt = node.getStatement().apply(THIS, new TypeCheckInfo(question.assistantFactory, local, question.scope)); local.unusedCheck(); node.setType(rt); return rt; } @Override public PType caseAIfStm(AIfStm node, TypeCheckInfo question) throws AnalysisException { node.setType(typeCheckIf(node.getLocation(), node.getIfExp(), node.getThenStm(), node.getElseIf(), node.getElseStm(), question));//rtypes.getType(node.getLocation())); return node.getType(); } @Override public PType caseALetBeStStm(ALetBeStStm node, TypeCheckInfo question) throws AnalysisException { Entry<PType, AMultiBindListDefinition> res = typecheckLetBeSt(node, node.getLocation(), node.getBind(), node.getSuchThat(), node.getStatement(), question); node.setDef(res.getValue()); node.setType(res.getKey()); return node.getType(); } @Override public PType caseANonDeterministicSimpleBlockStm( ANonDeterministicSimpleBlockStm node, TypeCheckInfo question) throws AnalysisException { PDefinition encl = question.env.getEnclosingDefinition(); if (encl != null && encl.getAccess().getPure()) { TypeCheckerErrors.report(3346, "Cannot use non-deterministic statement in pure operations", node.getLocation(), node); } PTypeSet rtypes = new PTypeSet(question.assistantFactory); int rcount = 0; for (PStm stmt : node.getStatements()) { PType stype = stmt.apply(THIS, question); if (question.assistantFactory.createPTypeAssistant().isType(stype, AUnionType.class)) { AUnionType ust = (AUnionType) stype; for (PType t : ust.getTypes()) { if (addOne(rtypes, t)) { rcount++; } } } else { if (addOne(rtypes, stype)) { rcount++; } } } if (rcount > 1) { TypeCheckerErrors.warning(5016, "Some statements will not be reached", node.getLocation(), node); } node.setType(rtypes.isEmpty() ? AstFactory.newAVoidType(node.getLocation()) : rtypes.getType(node.getLocation())); return node.getType(); } @Override public PType caseANotYetSpecifiedStm(ANotYetSpecifiedStm node, TypeCheckInfo question) { node.setType(typeCheckANotYetSpecifiedExp(node,node.getLocation())); return node.getType(); } @Override public PType caseAReturnStm(AReturnStm node, TypeCheckInfo question) throws AnalysisException { PDefinition encl = question.env.getEnclosingDefinition(); boolean inConstructor = false; if (encl instanceof AExplicitOperationDefinition) { AExplicitOperationDefinition op = (AExplicitOperationDefinition) encl; inConstructor = op.getIsConstructor(); } else if (encl instanceof AImplicitOperationDefinition) { AImplicitOperationDefinition op = (AImplicitOperationDefinition) encl; inConstructor = op.getIsConstructor(); } if (inConstructor && !(node.getExpression() instanceof ASelfExp)) { TypeCheckerErrors.report(3326, "Constructor can only return 'self'", node.getLocation(), node); } if (node.getExpression() == null) { node.setType(AstFactory.newAVoidReturnType(node.getLocation())); } else { node.setType(node.getExpression().apply(THIS, question)); } return question.assistantFactory.createPTypeAssistant().checkReturnType(question.returnType, node.getType(), node.getLocation()); } @Override public PType caseASkipStm(ASkipStm node, TypeCheckInfo question) { node.setType(AstFactory.newAVoidType(node.getLocation())); return node.getType(); } @Override public PType caseASpecificationStm(ASpecificationStm node, TypeCheckInfo question) throws AnalysisException { List<PDefinition> defs = new LinkedList<PDefinition>(); // Now we build local definitions for each of the externals, so // that they can be added to the local environment, while the // global state is made inaccessible. if (node.getExternals() != null) { for (AExternalClause clause : node.getExternals()) { for (ILexNameToken name : clause.getIdentifiers()) { if (question.env.findName(name, NameScope.STATE) == null) { TypeCheckerErrors.report(3274, "External variable is not in scope: " + name, name.getLocation(), name); } else { defs.add(AstFactory.newALocalDefinition(name.getLocation(), name, NameScope.STATE, clause.getType())); } } } } if (node.getErrors() != null) { for (AErrorCase err : node.getErrors()) { PType lt = err.getLeft().apply(THIS, question); PType rt = err.getRight().apply(THIS, question); if (!question.assistantFactory.createPTypeAssistant().isType(lt, ABooleanBasicType.class)) { TypeCheckerErrors.report(3275, "Error clause must be a boolean", err.getLeft().getLocation(), err.getLeft()); } if (!question.assistantFactory.createPTypeAssistant().isType(rt, ABooleanBasicType.class)) { TypeCheckerErrors.report(3275, "Error clause must be a boolean", err.getRight().getLocation(), err.getRight()); } } } question.assistantFactory.createPDefinitionListAssistant().typeCheck(defs, THIS, question); Environment local = new FlatEnvironment(question.assistantFactory, defs, question.env); // NB. No // check // //Unused if (node.getPrecondition() != null && !question.assistantFactory.createPTypeAssistant().isType(node.getPrecondition().apply(THIS, new TypeCheckInfo(question.assistantFactory, local, NameScope.NAMESANDSTATE)), ABooleanBasicType.class)) { TypeCheckerErrors.report(3233, "Precondition is not a boolean expression", node.getPrecondition().getLocation(), node.getPrecondition()); } if (node.getPostcondition() != null && !question.assistantFactory.createPTypeAssistant().isType(node.getPostcondition().apply(THIS, new TypeCheckInfo(question.assistantFactory, local, NameScope.NAMESANDANYSTATE)), ABooleanBasicType.class)) { TypeCheckerErrors.report(3234, "Postcondition is not a boolean expression", node.getPostcondition().getLocation(), node.getPostcondition()); } node.setType(AstFactory.newAVoidType(node.getLocation())); return node.getType(); } @Override public PType caseATrapStm(ATrapStm node, TypeCheckInfo question) throws AnalysisException { PTypeSet rtypes = new PTypeSet(question.assistantFactory); PStm body = node.getBody(); PType bt = body.apply(THIS, question); rtypes.add(bt); PTypeSet extype = exitCheck(body, question); PType ptype = null; if (extype.isEmpty()) { TypeCheckerErrors.report(3241, "Body of trap statement does not throw exceptions", node.getLocation(), node); ptype = AstFactory.newAUnknownType(body.getLocation()); } else { ptype = extype.getType(body.getLocation()); } node.setType(ptype); node.getPatternBind().apply(THIS, question); // TODO: PatternBind stuff List<PDefinition> defs = getDefinitions(node.getPatternBind()); question.assistantFactory.createPDefinitionListAssistant().typeCheck(defs, THIS, question); Environment local = new FlatCheckedEnvironment(question.assistantFactory, defs, question.env, question.scope); rtypes.add(node.getWith().apply(THIS, new TypeCheckInfo(question.assistantFactory, local, question.scope, question.qualifiers))); node.setType(rtypes.getType(node.getLocation())); return node.getType(); } @Override public PType caseAWhileStm(AWhileStm node, TypeCheckInfo question) throws AnalysisException { question.qualifiers = null; PType etype = node.getExp().apply(THIS, question); if (!question.assistantFactory.createPTypeAssistant().isType(etype, ABooleanBasicType.class)) { TypeCheckerErrors.report(3218, "Expression is not boolean", node.getLocation(), node); } List<QualifiedDefinition> qualified = node.getExp().apply(question.assistantFactory.getQualificationVisitor(), question); for (QualifiedDefinition qdef : qualified) { qdef.qualifyType(); } PType stype = node.getStatement().apply(THIS, question); for (QualifiedDefinition qdef : qualified) { qdef.resetType(); } if (node.getExp() instanceof ABooleanConstExp && stype instanceof AUnionType) { ABooleanConstExp boolLiteral = (ABooleanConstExp) node.getExp(); if (boolLiteral.getValue().getValue()) // while true do... { List<PType> edited = new Vector<PType>(); AUnionType original = (AUnionType) stype; for (PType t : original.getTypes()) { if (!(t instanceof AVoidType)) { edited.add(t); } } stype = AstFactory.newAUnionType(node.getLocation(), edited); } } return stype; } @Override public PType caseAPeriodicStm(APeriodicStm node, TypeCheckInfo question) throws AnalysisException { List<PExp> args = node.getArgs(); if (args.size() != 4) { TypeCheckerErrors.report(3287, "Periodic thread must have 4 argument(s)", node.getLocation(), node); } else { Environment newEnv = new FlatEnvironment(question.assistantFactory, question.env, true); TypeCheckInfo functional = question.newInfo(newEnv); for (PExp arg : args) { PType type = arg.apply(THIS, functional); if (!question.assistantFactory.createPTypeAssistant().isNumeric(type)) { TypeCheckerErrors.report(3316, "Expecting number in periodic argument", arg.getLocation(), arg); } } } ILexNameToken opname = node.getOpname(); opname.setTypeQualifier(new LinkedList<PType>()); opname.getLocation().hit(); PDefinition opdef = question.env.findName(opname, NameScope.NAMES); if (opdef == null) { TypeCheckerErrors.report(3228, opname + " is not in scope", node.getLocation(), node); node.setType(AstFactory.newAUnknownType(node.getLocation())); return node.getType(); } // Operation must be "() ==> ()" AOperationType expected = AstFactory.newAOperationType(node.getLocation(), new Vector<PType>(), AstFactory.newAVoidType(node.getLocation())); opdef = question.assistantFactory.createPDefinitionAssistant().deref(opdef); if (opdef instanceof AExplicitOperationDefinition) { AExplicitOperationDefinition def = (AExplicitOperationDefinition) opdef; if (!question.assistantFactory.createPTypeAssistant().equals(def.getType(), expected)) { TypeCheckerErrors.report(3229, opname + " should have no parameters or return type", node.getLocation(), node); TypeCheckerErrors.detail("Actual", def.getType()); } else if (def.getAccess().getPure()) { TypeCheckerErrors.report(3347, "Cannot have a pure operation as the body of a thread", node.getLocation(), node); } } else if (opdef instanceof AImplicitOperationDefinition) { AImplicitOperationDefinition def = (AImplicitOperationDefinition) opdef; if (def.getBody() == null) { TypeCheckerErrors.report(3230, opname + " is implicit", node.getLocation(), node); } if (!question.assistantFactory.createPTypeAssistant().equals(def.getType(), expected)) { TypeCheckerErrors.report(3231, opname + " should have no parameters or return type", node.getLocation(), node); TypeCheckerErrors.detail("Actual", def.getType()); } else if (def.getAccess().getPure()) { TypeCheckerErrors.report(3347, "Cannot have a pure operation as the body of a thread", node.getLocation(), node); } } else { TypeCheckerErrors.report(3232, opname + " is not an operation name", node.getLocation(), node); } node.setType(AstFactory.newAVoidType(node.getLocation())); return node.getType(); } @Override public PType caseASporadicStm(ASporadicStm node, TypeCheckInfo question) throws AnalysisException { List<PExp> args = node.getArgs(); if (args.size() != 3) { TypeCheckerErrors.report(3287, "Sporadic thread must have 3 arguments", node.getLocation(), node); } else { Environment newEnv = new FlatEnvironment(question.assistantFactory, question.env, true); TypeCheckInfo functional = question.newInfo(newEnv); for (PExp arg : args) { PType type = arg.apply(THIS, functional); if (!question.assistantFactory.createPTypeAssistant().isNumeric(type)) { TypeCheckerErrors.report(3316, "Expecting number in sporadic argument", arg.getLocation(), node); } } } ILexNameToken opname = node.getOpname(); opname.setTypeQualifier(new LinkedList<PType>()); opname.getLocation().hit(); PDefinition opdef = question.env.findName(opname, NameScope.NAMES); if (opdef == null) { TypeCheckerErrors.report(3228, opname + " is not in scope", node.getLocation(), node); node.setType(AstFactory.newAUnknownType(node.getLocation())); return node.getType(); } // Operation must be "() ==> ()" AOperationType expected = AstFactory.newAOperationType(node.getLocation(), new Vector<PType>(), AstFactory.newAVoidType(node.getLocation())); opdef = question.assistantFactory.createPDefinitionAssistant().deref(opdef); if (opdef instanceof AExplicitOperationDefinition) { AExplicitOperationDefinition def = (AExplicitOperationDefinition) opdef; if (!question.assistantFactory.createPTypeAssistant().equals(def.getType(), expected)) { TypeCheckerErrors.report(3229, opname + " should have no parameters or return type", node.getLocation(), node); TypeCheckerErrors.detail("Actual", def.getType()); } else if (def.getAccess().getPure()) { TypeCheckerErrors.report(3347, "Cannot have a pure operation as the body of a thread", node.getLocation(), node); } } else if (opdef instanceof AImplicitOperationDefinition) { AImplicitOperationDefinition def = (AImplicitOperationDefinition) opdef; if (def.getBody() == null) { TypeCheckerErrors.report(3230, opname + " is implicit", node.getLocation(), node); } if (!question.assistantFactory.createPTypeAssistant().equals(def.getType(), expected)) { TypeCheckerErrors.report(3231, opname + " should have no parameters or return type", node.getLocation(), node); TypeCheckerErrors.detail("Actual", def.getType()); } else if (def.getAccess().getPure()) { TypeCheckerErrors.report(3347, "Cannot have a pure operation as the body of a thread", node.getLocation(), node); } } else { TypeCheckerErrors.report(3232, opname + " is not an operation name", node.getLocation(), node); } node.setType(AstFactory.newAVoidType(node.getLocation())); return node.getType(); } @Override public PType caseAStartStm(AStartStm node, TypeCheckInfo question) throws AnalysisException { PDefinition encl = question.env.getEnclosingDefinition(); if (encl != null && encl.getAccess().getPure()) { TypeCheckerErrors.report(3346, "Cannot use start in pure operations", node.getLocation(), node); } PType type = node.getObj().apply(THIS, question); if (question.assistantFactory.createPTypeAssistant().isSet(type)) { SSetType set = question.assistantFactory.createPTypeAssistant().getSet(type); if (!question.assistantFactory.createPTypeAssistant().isClass(set.getSetof(), null)) { TypeCheckerErrors.report(3235, "Expression is not a set of object references", node.getObj().getLocation(), node.getObj()); } else { AClassType ctype = question.assistantFactory.createPTypeAssistant().getClassType(set.getSetof(), null); if (question.assistantFactory.createSClassDefinitionAssistant().findThread(ctype.getClassdef()) == null) { TypeCheckerErrors.report(3236, "Class does not define a thread", node.getObj().getLocation(), node.getObj()); } } } else if (question.assistantFactory.createPTypeAssistant().isClass(type, null)) { AClassType ctype = question.assistantFactory.createPTypeAssistant().getClassType(type, null); if (question.assistantFactory.createSClassDefinitionAssistant().findThread(ctype.getClassdef()) == null) { TypeCheckerErrors.report(3237, "Class does not define a thread", node.getObj().getLocation(), node.getObj()); } } else { TypeCheckerErrors.report(3238, "Expression is not an object reference or set of object references", node.getObj().getLocation(), node.getObj()); } node.setType(AstFactory.newAVoidType(node.getLocation())); return node.getType(); } @Override public PType caseAStopStm(AStopStm node, TypeCheckInfo question) throws AnalysisException { PType type = node.getObj().apply(THIS, question); if (question.assistantFactory.createPTypeAssistant().isSet(type)) { SSetType set = question.assistantFactory.createPTypeAssistant().getSet(type); if (!question.assistantFactory.createPTypeAssistant().isClass(set.getSetof(), null)) { TypeCheckerErrors.report(3235, "Expression is not a set of object references", node.getObj().getLocation(), node.getObj()); } else { AClassType ctype = question.assistantFactory.createPTypeAssistant().getClassType(set.getSetof(), null); if (question.assistantFactory.createSClassDefinitionAssistant().findThread(ctype.getClassdef()) == null) { TypeCheckerErrors.report(3236, "Class does not define a thread", node.getObj().getLocation(), node.getObj()); } } } else if (question.assistantFactory.createPTypeAssistant().isClass(type, null)) { AClassType ctype = question.assistantFactory.createPTypeAssistant().getClassType(type, null); if (question.assistantFactory.createSClassDefinitionAssistant().findThread(ctype.getClassdef()) == null) { TypeCheckerErrors.report(3237, "Class does not define a thread", node.getObj().getLocation(), node.getObj()); } } else { TypeCheckerErrors.report(3238, "Expression is not an object reference or set of object references", node.getObj().getLocation(), node.getObj()); } node.setType(AstFactory.newAVoidType(node.getLocation())); return node.getType(); } @Override public PType caseASubclassResponsibilityStm( ASubclassResponsibilityStm node, TypeCheckInfo question) { node.setType(AstFactory.newAUnknownType(node.getLocation())); // Because // we // terminate // anyway return node.getType(); } @Override public PType caseATixeStm(ATixeStm node, TypeCheckInfo question) throws AnalysisException { PType rt = node.getBody().apply(THIS, question); PTypeSet extypes = exitCheck(node.getBody(), question); if (!extypes.isEmpty()) { PType union = extypes.getType(node.getLocation()); for (ATixeStmtAlternative tsa : node.getTraps()) { tsa.setExp(union); tsa.apply(THIS, question); } } node.setType(rt); return rt; } @Override public PType caseATixeStmtAlternative(ATixeStmtAlternative node, TypeCheckInfo question) throws AnalysisException { // TODO fix // patternBind.typeCheck(base, scope, ext) // PPatternBindAssistant.typeCheck(node.getPatternBind(), null, // THIS, question); // DefinitionList defs = patternBind.getDefinitions(); node.getPatternBind().apply(THIS, new TypeCheckInfo(question.assistantFactory, question.env, question.scope)); List<PDefinition> defs = getDefinitions(node.getPatternBind()); question.assistantFactory.createPDefinitionListAssistant().typeCheck(defs, THIS, question); Environment local = new FlatCheckedEnvironment(question.assistantFactory, defs, question.env, question.scope); node.getStatement().apply(THIS, new TypeCheckInfo(question.assistantFactory, local, question.scope, question.qualifiers)); local.unusedCheck(); return null; } @Override public PType caseADefPatternBind(ADefPatternBind node, TypeCheckInfo question) throws AnalysisException { node.setDefs(null); PBind bind = node.getBind(); PType type = node.getType(); if (bind != null) { if (bind instanceof ATypeBind) { ATypeBind typebind = (ATypeBind) bind; question.assistantFactory.createATypeBindAssistant().typeResolve(typebind, THIS, question); if (!question.assistantFactory.getTypeComparator().compatible(typebind.getType(), type)) { TypeCheckerErrors.report(3198, "Type bind not compatible with expression", bind.getLocation(), bind); TypeCheckerErrors.detail2("Bind", typebind.getType(), "Exp", type); } } else { ASetBind setbind = (ASetBind) bind; SSetType settype = question.assistantFactory.createPTypeAssistant().getSet(setbind.getSet().apply(THIS, question)); if (!question.assistantFactory.getTypeComparator().compatible(type, settype.getSetof())) { TypeCheckerErrors.report(3199, "Set bind not compatible with expression", bind.getLocation(), bind); TypeCheckerErrors.detail2("Bind", settype.getSetof(), "Exp", type); } } PDefinition def = AstFactory.newAMultiBindListDefinition(bind.getLocation(), question.assistantFactory.createPBindAssistant().getMultipleBindList(bind)); def.apply(THIS, question); List<PDefinition> defs = new LinkedList<PDefinition>(); defs.add(def); node.setDefs(defs); } else { assert type != null : "Can't typecheck a pattern without a type"; question.assistantFactory.createPPatternAssistant().typeResolve(node.getPattern(), THIS, question); node.setDefs(question.assistantFactory.createPPatternAssistant().getDefinitions(node.getPattern(), type, NameScope.LOCAL)); } return null; } public List<PDefinition> getDefinitions(ADefPatternBind patternBind) { assert patternBind.getDefs() != null : "PatternBind must be type checked before getDefinitions"; return patternBind.getDefs(); } public PTypeSet exitCheck(PStm statement, TypeCheckInfo question) { try { return statement.apply(question.assistantFactory.getExitTypeCollector()); } catch (AnalysisException e) { return new PTypeSet(question.assistantFactory); } } public List<PType> getArgTypes(List<PExp> args, IQuestionAnswer<TypeCheckInfo, PType> rootVisitor, TypeCheckInfo question) throws AnalysisException { List<PType> types = new LinkedList<PType>(); for (PExp e : args) { types.add(e.apply(rootVisitor, question)); } return types; } public void checkArgTypes(PType type, List<PType> ptypes, List<PType> atypes, TypeCheckInfo question) { if (ptypes.size() != atypes.size()) { TypeCheckerErrors.report(3211, "Expecting " + ptypes.size() + " arguments", type.getLocation(), type); } else { int i = 0; for (PType atype : atypes) { PType ptype = ptypes.get(i++); if (!question.assistantFactory.getTypeComparator().compatible(ptype, atype)) { TypeCheckerErrors.report(3212, "Unexpected type for argument " + i, atype.getLocation(), atype); TypeCheckerErrors.detail2("Expected", ptype, "Actual", atype); } } } } public boolean addOne(PTypeSet rtypes, PType add) { if (add instanceof AVoidReturnType) { rtypes.add(AstFactory.newAVoidType(add.getLocation())); return true; } else if (!(add instanceof AVoidType)) { rtypes.add(add); return true; } else { rtypes.add(add); return false; } } public void addOneType(Set<PType> rtypes, PType add) { if (add instanceof AVoidReturnType) { rtypes.add(AstFactory.newAVoidType(add.getLocation())); } else if (!(add instanceof AVoidType)) { rtypes.add(add); } } public PDefinition targetDefinition(PStateDesignator pStateDesignator, TypeCheckInfo question) { if (pStateDesignator instanceof AIdentifierStateDesignator) { AIdentifierStateDesignator stateDesignator = (AIdentifierStateDesignator) pStateDesignator; return question.env.findName(stateDesignator.getName(), NameScope.STATE); } else { return null; } } public void checkArgTypes(ACallStm node, PType type, List<PType> ptypes, List<PType> atypes, TypeCheckInfo question) { if (ptypes.size() != atypes.size()) { TypeCheckerErrors.report(3216, "Expecting " + ptypes.size() + " arguments", node.getLocation(), node); } else { int i = 0; for (PType atype : atypes) { PType ptype = ptypes.get(i++); if (!question.assistantFactory.getTypeComparator().compatible(ptype, atype)) { TypeCheckerErrors.report(3217, "Unexpected type for argument " + i, node.getLocation(), type); TypeCheckerErrors.detail2("Expected", ptype, "Actual", atype); } } } } }