/*
* #%~
* 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.Collection;
import java.util.Collections;
import java.util.List;
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.AAssignmentDefinition;
import org.overture.ast.definitions.AClassInvariantDefinition;
import org.overture.ast.definitions.AEqualsDefinition;
import org.overture.ast.definitions.AExplicitFunctionDefinition;
import org.overture.ast.definitions.AExplicitOperationDefinition;
import org.overture.ast.definitions.AExternalDefinition;
import org.overture.ast.definitions.AImplicitFunctionDefinition;
import org.overture.ast.definitions.AImplicitOperationDefinition;
import org.overture.ast.definitions.AImportedDefinition;
import org.overture.ast.definitions.AInheritedDefinition;
import org.overture.ast.definitions.AInstanceVariableDefinition;
import org.overture.ast.definitions.ALocalDefinition;
import org.overture.ast.definitions.AMultiBindListDefinition;
import org.overture.ast.definitions.AMutexSyncDefinition;
import org.overture.ast.definitions.ANamedTraceDefinition;
import org.overture.ast.definitions.APerSyncDefinition;
import org.overture.ast.definitions.ARenamedDefinition;
import org.overture.ast.definitions.AStateDefinition;
import org.overture.ast.definitions.AThreadDefinition;
import org.overture.ast.definitions.ATypeDefinition;
import org.overture.ast.definitions.AUntypedDefinition;
import org.overture.ast.definitions.AValueDefinition;
import org.overture.ast.definitions.PDefinition;
import org.overture.ast.definitions.SClassDefinition;
import org.overture.ast.definitions.traces.AApplyExpressionTraceCoreDefinition;
import org.overture.ast.definitions.traces.ABracketedExpressionTraceCoreDefinition;
import org.overture.ast.definitions.traces.AConcurrentExpressionTraceCoreDefinition;
import org.overture.ast.definitions.traces.ALetBeStBindingTraceDefinition;
import org.overture.ast.definitions.traces.ALetDefBindingTraceDefinition;
import org.overture.ast.definitions.traces.ARepeatTraceDefinition;
import org.overture.ast.definitions.traces.ATraceDefinitionTerm;
import org.overture.ast.definitions.traces.PTraceDefinition;
import org.overture.ast.expressions.ANotYetSpecifiedExp;
import org.overture.ast.expressions.ASubclassResponsibilityExp;
import org.overture.ast.expressions.AUndefinedExp;
import org.overture.ast.factory.AstFactory;
import org.overture.ast.intf.lex.ILexNameToken;
import org.overture.ast.lex.LexNameToken;
import org.overture.ast.lex.VDMToken;
import org.overture.ast.node.NodeList;
import org.overture.ast.patterns.APatternListTypePair;
import org.overture.ast.patterns.ATypeBind;
import org.overture.ast.patterns.PMultipleBind;
import org.overture.ast.patterns.PPattern;
import org.overture.ast.statements.AErrorCase;
import org.overture.ast.statements.AExternalClause;
import org.overture.ast.statements.ANotYetSpecifiedStm;
import org.overture.ast.statements.ASubclassResponsibilityStm;
import org.overture.ast.typechecker.NameScope;
import org.overture.ast.typechecker.Pass;
import org.overture.ast.types.ABooleanBasicType;
import org.overture.ast.types.AClassType;
import org.overture.ast.types.AFieldField;
import org.overture.ast.types.AFunctionType;
import org.overture.ast.types.ANamedInvariantType;
import org.overture.ast.types.ANatNumericBasicType;
import org.overture.ast.types.AOperationType;
import org.overture.ast.types.AProductType;
import org.overture.ast.types.ARecordInvariantType;
import org.overture.ast.types.AUnknownType;
import org.overture.ast.types.AVoidType;
import org.overture.ast.types.PType;
import org.overture.typechecker.Environment;
import org.overture.typechecker.ExcludedDefinitions;
import org.overture.typechecker.FlatCheckedEnvironment;
import org.overture.typechecker.FlatEnvironment;
import org.overture.typechecker.PrivateClassEnvironment;
import org.overture.typechecker.TypeCheckInfo;
import org.overture.typechecker.TypeChecker;
import org.overture.typechecker.TypeCheckerErrors;
import org.overture.typechecker.assistant.ITypeCheckerAssistantFactory;
import org.overture.typechecker.assistant.definition.PAccessSpecifierAssistantTC;
import org.overture.typechecker.utilities.DefinitionTypeResolver;
import org.overture.typechecker.utilities.type.QualifiedDefinition;
public class TypeCheckerDefinitionVisitor extends AbstractTypeCheckVisitor
{
public TypeCheckerDefinitionVisitor(
IQuestionAnswer<TypeCheckInfo, PType> typeCheckVisitor)
{
super(typeCheckVisitor);
}
@Override
public PType caseAAssignmentDefinition(AAssignmentDefinition node,
TypeCheckInfo question) throws AnalysisException
{
question.qualifiers = null;
node.setType(question.assistantFactory.createPTypeAssistant().typeResolve(question.assistantFactory.createPDefinitionAssistant().getType(node), null, THIS, question));
ExcludedDefinitions.setExcluded(node);
node.setExpType(node.getExpression().apply(THIS, question));
ExcludedDefinitions.clearExcluded();
question.assistantFactory.getTypeComparator().checkComposeTypes(node.getType(), question.env, false);
if (node.getExpType() instanceof AVoidType)
{
TypeCheckerErrors.report(3048, "Expression does not return a value", node.getExpression().getLocation(), node.getExpression());
}
if (!question.assistantFactory.getTypeComparator().compatible(node.getType(), node.getExpType()))
{
TypeCheckerErrors.report(3000, "Expression does not match declared type", node.getExpression().getLocation(), node);
TypeCheckerErrors.detail2("Declared", node.getType(), "Expression", node.getExpType());
}
return node.getType();
}
@Override
public PType caseAInstanceVariableDefinition(
AInstanceVariableDefinition node, TypeCheckInfo question)
throws AnalysisException
{
if (node.getExpression() instanceof AUndefinedExp)
{
if (question.assistantFactory.createPAccessSpecifierAssistant().isStatic(node.getAccess()))
{
TypeCheckerErrors.report(3037, "Static instance variable is not initialized: "
+ node.getName(), node.getLocation(), node);
}
}
// Initializers can reference class members, so create a new env.
// We set the type qualifier to unknown so that type-based name
// resolution will succeed.
Environment cenv = new PrivateClassEnvironment(question.assistantFactory, node.getClassDefinition(), question.env);
if (question.assistantFactory.createPAccessSpecifierAssistant().isStatic(node.getAccess()))
{
FlatCheckedEnvironment checked = new FlatCheckedEnvironment(question.assistantFactory, new Vector<PDefinition>(), question.env, NameScope.NAMES);
checked.setStatic(true);
cenv = checked;
}
// TODO: This should be a call to the assignment definition typecheck
// but instance is not an subclass of
// assignment in our tree
ExcludedDefinitions.setExcluded(node);
node.setExpType(node.getExpression().apply(THIS, new TypeCheckInfo(question.assistantFactory, cenv, NameScope.NAMESANDSTATE, question.qualifiers)));
ExcludedDefinitions.clearExcluded();
node.setType(question.assistantFactory.createPTypeAssistant().typeResolve(question.assistantFactory.createPDefinitionAssistant().getType(node), null, THIS, question));
if (node.getExpType() instanceof AVoidType)
{
TypeCheckerErrors.report(3048, "Expression does not return a value", node.getExpression().getLocation(), node.getExpression());
}
if (!question.assistantFactory.getTypeComparator().compatible(question.assistantFactory.createPDefinitionAssistant().getType(node), node.getExpType()))
{
TypeCheckerErrors.report(3000, "Expression does not match declared type", node.getLocation(), node);
TypeCheckerErrors.detail2("Declared", question.assistantFactory.createPDefinitionAssistant().getType(node), "Expression", node.getExpType());
}
return node.getType();
}
@Override
public PType caseAClassInvariantDefinition(AClassInvariantDefinition node,
TypeCheckInfo question) throws AnalysisException
{
Environment newEnv = new FlatEnvironment(question.assistantFactory, question.env, true);
newEnv.setEnclosingDefinition(node);
TypeCheckInfo functional = question.newInfo(newEnv);
functional.qualifiers = null;
functional.scope = NameScope.NAMESANDSTATE;
PType type = node.getExpression().apply(THIS, functional);
if (!question.assistantFactory.createPTypeAssistant().isType(type, ABooleanBasicType.class))
{
TypeCheckerErrors.report(3013, "Class invariant is not a boolean expression", node.getLocation(), node);
}
node.setType(type);
return node.getType();
}
@Override
public PType caseAEqualsDefinition(AEqualsDefinition node,
TypeCheckInfo question) throws AnalysisException
{
question.qualifiers = null;
node.setExpType(node.getTest().apply(THIS, question));
PPattern pattern = node.getPattern();
if (pattern != null)
{
question.assistantFactory.createPPatternAssistant().typeResolve(pattern, THIS, question);
node.setDefs(question.assistantFactory.createPPatternAssistant().getDefinitions(pattern, node.getExpType(), question.scope));
node.setDefType(node.getExpType());
}
else if (node.getTypebind() != null)
{
question.assistantFactory.createATypeBindAssistant().typeResolve(node.getTypebind(), THIS, question);
ATypeBind typebind = node.getTypebind();
if (!question.assistantFactory.getTypeComparator().compatible(typebind.getType(), node.getExpType()))
{
TypeCheckerErrors.report(3014, "Expression is not compatible with type bind", typebind.getLocation(), typebind);
}
node.setDefType(typebind.getType()); // Effectively a cast
node.setDefs(question.assistantFactory.createPPatternAssistant().getDefinitions(typebind.getPattern(), node.getDefType(), question.scope));
}
else if (node.getSetbind() != null)
{
question.qualifiers = null;
PType st = node.getSetbind().getSet().apply(THIS, question);
if (!question.assistantFactory.createPTypeAssistant().isSet(st))
{
TypeCheckerErrors.report(3015, "Set bind is not a set type?", node.getLocation(), node);
node.setDefType(node.getExpType());
}
else
{
PType setof = question.assistantFactory.createPTypeAssistant().getSet(st).getSetof();
if (!question.assistantFactory.getTypeComparator().compatible(node.getExpType(), setof))
{
TypeCheckerErrors.report(3016, "Expression is not compatible with set bind", node.getSetbind().getLocation(), node.getSetbind());
}
node.setDefType(setof); // Effectively a cast
}
question.assistantFactory.createPPatternAssistant().typeResolve(node.getSetbind().getPattern(), THIS, question);
node.setDefs(question.assistantFactory.createPPatternAssistant().getDefinitions(node.getSetbind().getPattern(), node.getDefType(), question.scope));
}
else // Seq bind
{
question.qualifiers = null;
PType st = node.getSeqbind().getSeq().apply(THIS, question);
if (!question.assistantFactory.createPTypeAssistant().isSeq(st))
{
TypeCheckerErrors.report(3015, "Seq bind is not a seq type?", node.getLocation(), node);
node.setDefType(node.getExpType());
}
else
{
PType seqof = question.assistantFactory.createPTypeAssistant().getSeq(st).getSeqof();
if (!question.assistantFactory.getTypeComparator().compatible(node.getExpType(), seqof))
{
TypeCheckerErrors.report(3016, "Expression is not compatible with seq bind", node.getSeqbind().getLocation(), node.getSeqbind());
}
node.setDefType(seqof); // Effectively a cast
}
question.assistantFactory.createPPatternAssistant().typeResolve(node.getSeqbind().getPattern(), THIS, question);
node.setDefs(question.assistantFactory.createPPatternAssistant().getDefinitions(node.getSeqbind().getPattern(), node.getDefType(), question.scope));
}
question.assistantFactory.createPDefinitionListAssistant().typeCheck(node.getDefs(), THIS, question);
return node.getType();
}
@Override
public PType caseAExplicitFunctionDefinition(
AExplicitFunctionDefinition node, TypeCheckInfo question)
throws AnalysisException
{
NodeList<PDefinition> defs = new NodeList<PDefinition>(node);
question.assistantFactory.getTypeComparator().checkComposeTypes(node.getType(), question.env, false);
if (node.getTypeParams() != null)
{
defs.addAll(question.assistantFactory.createAExplicitFunctionDefinitionAssistant().getTypeParamDefinitions(node));
}
PType expectedResult = question.assistantFactory.createAExplicitFunctionDefinitionAssistant().checkParams(node, node.getParamPatternList().listIterator(), (AFunctionType) node.getType());
node.setExpectedResult(expectedResult);
List<List<PDefinition>> paramDefinitionList = question.assistantFactory.createSFunctionDefinitionAssistant().getParamDefinitions(node, (AFunctionType) node.getType(), node.getParamPatternList(), node.getLocation());
Collections.reverse(paramDefinitionList);
for (List<PDefinition> pdef : paramDefinitionList)
{
defs.addAll(pdef); // All definitions of all parameter lists
}
FlatCheckedEnvironment local = new FlatCheckedEnvironment(question.assistantFactory, defs, question.env, question.scope);
local.setStatic(question.assistantFactory.createPAccessSpecifierAssistant().isStatic(node.getAccess()));
local.setEnclosingDefinition(node);
local.setFunctional(true);
// building the new scope for subtypechecks
question.assistantFactory.createPDefinitionListAssistant().typeCheck(defs, this, new TypeCheckInfo(question.assistantFactory, local, question.scope, question.qualifiers)); // can
// be
// this
// because
// its
// a
// definition
// list
if (question.env.isVDMPP()
&& !question.assistantFactory.createPAccessSpecifierAssistant().isStatic(node.getAccess()))
{
local.add(question.assistantFactory.createPDefinitionAssistant().getSelfDefinition(node));
}
List<QualifiedDefinition> qualified = new Vector<QualifiedDefinition>();
if (node.getPredef() != null)
{
// building the new scope for subtypechecks
PType b = node.getPredef().getBody().apply(THIS, new TypeCheckInfo(question.assistantFactory, local, NameScope.NAMES));
ABooleanBasicType expected = AstFactory.newABooleanBasicType(node.getLocation());
if (!question.assistantFactory.createPTypeAssistant().isType(b, ABooleanBasicType.class))
{
TypeChecker.report(3018, "Precondition returns unexpected type", node.getLocation());
TypeChecker.detail2("Actual", b, "Expected", expected);
}
qualified = node.getPredef().getBody().apply(question.assistantFactory.getQualificationVisitor(), new TypeCheckInfo(question.assistantFactory, local, NameScope.NAMES));
for (QualifiedDefinition qdef : qualified)
{
qdef.qualifyType();
}
}
if (node.getPostdef() != null)
{
LexNameToken result = new LexNameToken(node.getName().getModule(), "RESULT", node.getLocation());
PPattern rp = AstFactory.newAIdentifierPattern(result);
List<PDefinition> rdefs = question.assistantFactory.createPPatternAssistant().getDefinitions(rp, expectedResult, NameScope.NAMES);
FlatCheckedEnvironment post = new FlatCheckedEnvironment(question.assistantFactory, rdefs, local, NameScope.NAMES);
post.setFunctional(true);
// building the new scope for subtypechecks
PType b = node.getPostdef().getBody().apply(THIS, new TypeCheckInfo(question.assistantFactory, post, NameScope.NAMES));
ABooleanBasicType expected = AstFactory.newABooleanBasicType(node.getLocation());
if (!question.assistantFactory.createPTypeAssistant().isType(b, ABooleanBasicType.class))
{
TypeChecker.report(3018, "Postcondition returns unexpected type", node.getLocation());
TypeChecker.detail2("Actual", b, "Expected", expected);
}
}
// This check returns the type of the function body in the case where
// all of the curried parameter sets are provided.
PType actualResult = node.getBody().apply(THIS, new TypeCheckInfo(question.assistantFactory, local, question.scope, null, expectedResult, null));
node.setActualResult(actualResult);
for (QualifiedDefinition qdef : qualified)
{
qdef.resetType();
}
if (!question.assistantFactory.getTypeComparator().compatible(expectedResult, node.getActualResult()))
{
TypeChecker.report(3018, "Function returns unexpected type", node.getLocation());
TypeChecker.detail2("Actual", node.getActualResult(), "Expected", expectedResult);
}
if (question.assistantFactory.createPTypeAssistant().narrowerThan(node.getType(), node.getAccess()))
{
TypeCheckerErrors.report(3019, "Function parameter visibility less than function definition", node.getLocation(), node);
}
if (question.env.isVDMPP())
{
PAccessSpecifierAssistantTC assist = question.assistantFactory.createPAccessSpecifierAssistant();
if (assist.isPrivate(node.getAccess())
&& node.getBody() instanceof ASubclassResponsibilityExp)
{
TypeCheckerErrors.report(3329, "Abstract function/operation must be public or protected", node.getLocation(), node);
}
}
if (node.getMeasure() == null && node.getRecursive())
{
TypeCheckerErrors.warning(5012, "Recursive function has no measure", node.getLocation(), node);
} else if (node.getMeasure() != null)
{
if (question.env.isVDMPP())
{
node.getMeasure().setTypeQualifier(question.assistantFactory.createAExplicitFunctionDefinitionAssistant().getMeasureParams(node));
}
node.setMeasureDef(question.env.findName(node.getMeasure(), question.scope));
if (node.getMeasureDef() == null)
{
TypeCheckerErrors.report(3270, "Measure " + node.getMeasure()
+ " is not in scope", node.getMeasure().getLocation(), node.getMeasure());
} else if (!(node.getMeasureDef() instanceof AExplicitFunctionDefinition))
{
TypeCheckerErrors.report(3271, "Measure " + node.getMeasure()
+ " is not an explicit function", node.getMeasure().getLocation(), node.getMeasure());
} else if (node.getMeasureDef() == node)
{
TypeCheckerErrors.report(3304, "Recursive function cannot be its own measure", node.getMeasure().getLocation(), node.getMeasure());
} else
{
AExplicitFunctionDefinition efd = (AExplicitFunctionDefinition) node.getMeasureDef();
if (node.getTypeParams() == null && efd.getTypeParams() != null)
{
TypeCheckerErrors.report(3309, "Measure must not be polymorphic", node.getMeasure().getLocation(), node.getMeasure());
} else if (node.getTypeParams() != null
&& efd.getTypeParams() == null)
{
TypeCheckerErrors.report(3310, "Measure must also be polymorphic", node.getMeasure().getLocation(), node.getMeasure());
} else if (node.getTypeParams() != null
&& efd.getTypeParams() != null
&& !node.getTypeParams().equals(efd.getTypeParams()))
{
TypeCheckerErrors.report(3318, "Measure's type parameters must match function's", node.getMeasure().getLocation(), node.getMeasure());
TypeChecker.detail2("Actual", efd.getTypeParams(), "Expected", node.getTypeParams());
}
AFunctionType mtype = (AFunctionType) efd.getType();
if (node.getTypeParams() != null && !node.getTypeParams().isEmpty())
{
if (!mtype.getParameters().toString().equals(question.assistantFactory.createAExplicitFunctionDefinitionAssistant().getMeasureParams(node).toString()))
{
TypeCheckerErrors.report(3303, "Measure parameters different to function", node.getMeasure().getLocation(), node.getMeasure());
TypeChecker.detail2(node.getMeasure().getFullName(), mtype.getParameters(), "Expected", question.assistantFactory.createAExplicitFunctionDefinitionAssistant().getMeasureParams(node));
}
}
else if (!question.assistantFactory.getTypeComparator().compatible(mtype.getParameters(), question.assistantFactory.createAExplicitFunctionDefinitionAssistant().getMeasureParams(node)))
{
TypeCheckerErrors.report(3303, "Measure parameters different to function", node.getMeasure().getLocation(), node.getMeasure());
TypeChecker.detail2(node.getMeasure().getFullName(), mtype.getParameters(), "Expected", question.assistantFactory.createAExplicitFunctionDefinitionAssistant().getMeasureParams(node));
}
if (!(mtype.getResult() instanceof ANatNumericBasicType))
{
if (mtype.getResult() instanceof AProductType)
{
AProductType pt = question.assistantFactory.createPTypeAssistant().getProduct(mtype.getResult());
for (PType t : pt.getTypes())
{
if (!(t instanceof ANatNumericBasicType))
{
TypeCheckerErrors.report(3272, "Measure range is not a nat, or a nat tuple", node.getMeasure().getLocation(), node.getMeasure());
TypeCheckerErrors.detail("Actual", mtype.getResult());
break;
}
}
node.setMeasureLexical(pt.getTypes().size());
} else
{
TypeCheckerErrors.report(3272, "Measure range is not a nat, or a nat tuple", node.getMeasure().getLocation(), node.getMeasure());
TypeCheckerErrors.detail("Actual", mtype.getResult());
}
}
}
}
if (!(node.getBody() instanceof ANotYetSpecifiedExp)
&& !(node.getBody() instanceof ASubclassResponsibilityExp))
{
local.unusedCheck();
}
node.setType(node.getType());
return node.getType();
}
@Override
public PType caseAExternalDefinition(AExternalDefinition node,
TypeCheckInfo question)
{
// Nothing to do - state is type checked separately
return null;
}
@Override
public PType caseAImplicitFunctionDefinition(
AImplicitFunctionDefinition node, TypeCheckInfo question)
throws AnalysisException
{
question.assistantFactory.getTypeComparator().checkComposeTypes(node.getType(), question.env, false);
List<PDefinition> defs = new Vector<PDefinition>();
if (node.getTypeParams() != null)
{
defs.addAll(question.assistantFactory.createAImplicitFunctionDefinitionAssistant().getTypeParamDefinitions(node));
}
List<PDefinition> argdefs = new Vector<PDefinition>();
for (APatternListTypePair pltp : node.getParamPatterns())
{
argdefs.addAll(getDefinitions(pltp, NameScope.LOCAL, question.assistantFactory));
}
defs.addAll(question.assistantFactory.createPDefinitionAssistant().checkDuplicatePatterns(node, argdefs));
FlatCheckedEnvironment local = new FlatCheckedEnvironment(question.assistantFactory, defs, question.env, question.scope);
local.setStatic(question.assistantFactory.createPAccessSpecifierAssistant().isStatic(node.getAccess()));
local.setEnclosingDefinition(node);
local.setFunctional(true);
question.assistantFactory.createPDefinitionListAssistant().typeCheck(defs, THIS, new TypeCheckInfo(question.assistantFactory, local, question.scope, question.qualifiers));
List<QualifiedDefinition> qualified = new Vector<QualifiedDefinition>();
if (node.getPredef() != null)
{
PType b = node.getPredef().getBody().apply(THIS, new TypeCheckInfo(question.assistantFactory, local, question.scope));
ABooleanBasicType expected = AstFactory.newABooleanBasicType(node.getLocation());
if (!question.assistantFactory.createPTypeAssistant().isType(b, ABooleanBasicType.class))
{
TypeCheckerErrors.report(3018, "Precondition returns unexpected type", node.getLocation(), node);
TypeCheckerErrors.detail2("Actual", b, "Expected", expected);
}
qualified = node.getPredef().getBody().apply(question.assistantFactory.getQualificationVisitor(), new TypeCheckInfo(question.assistantFactory, local, question.scope));
for (QualifiedDefinition qdef : qualified)
{
qdef.qualifyType();
}
}
if (node.getBody() != null)
{
if (node.getClassDefinition() != null
&& !question.assistantFactory.createPAccessSpecifierAssistant().isStatic(node.getAccess()))
{
local.add(question.assistantFactory.createPDefinitionAssistant().getSelfDefinition(node));
}
node.setActualResult(node.getBody().apply(THIS, new TypeCheckInfo(question.assistantFactory, local, question.scope, question.qualifiers, node.getResult().getType(), null)));
if (!question.assistantFactory.getTypeComparator().compatible(node.getResult().getType(), node.getActualResult()))
{
TypeCheckerErrors.report(3029, "Function returns unexpected type", node.getLocation(), node);
TypeCheckerErrors.detail2("Actual", node.getActualResult(), "Expected", node.getResult().getType());
}
}
for (QualifiedDefinition qdef : qualified)
{
qdef.resetType();
}
if (question.assistantFactory.createPTypeAssistant().narrowerThan(question.assistantFactory.createPDefinitionAssistant().getType(node), node.getAccess()))
{
TypeCheckerErrors.report(3030, "Function parameter visibility less than function definition", node.getLocation(), node);
}
if (question.env.isVDMPP())
{
PAccessSpecifierAssistantTC assist = question.assistantFactory.createPAccessSpecifierAssistant();
if (assist.isPrivate(node.getAccess())
&& node.getBody() instanceof ASubclassResponsibilityExp)
{
TypeCheckerErrors.report(3329, "Abstract function/operation must be public or protected", node.getLocation(), node);
}
}
// The result variables are in scope for the post condition
if (node.getPostdef() != null)
{
PType b = null;
if (node.getResult() != null)
{
List<PDefinition> postdefs = question.assistantFactory.createAPatternTypePairAssistant().getDefinitions(node.getResult());
FlatCheckedEnvironment post = new FlatCheckedEnvironment(question.assistantFactory, postdefs, local, NameScope.NAMES);
post.setStatic(question.assistantFactory.createPAccessSpecifierAssistant().isStatic(node.getAccess()));
post.setEnclosingDefinition(node);
post.setFunctional(true);
b = node.getPostdef().getBody().apply(THIS, new TypeCheckInfo(question.assistantFactory, post, NameScope.NAMES));
post.unusedCheck();
} else
{
b = node.getPostdef().getBody().apply(THIS, new TypeCheckInfo(question.assistantFactory, local, NameScope.NAMES));
}
ABooleanBasicType expected = AstFactory.newABooleanBasicType(node.getLocation());
if (!question.assistantFactory.createPTypeAssistant().isType(b, ABooleanBasicType.class))
{
TypeCheckerErrors.report(3018, "Postcondition returns unexpected type", node.getLocation(), node);
TypeCheckerErrors.detail2("Actual", b, "Expected", expected);
}
}
if (node.getMeasure() == null && node.getRecursive())
{
TypeCheckerErrors.warning(5012, "Recursive function has no measure", node.getLocation(), node);
} else if (node.getMeasure() != null)
{
if (question.env.isVDMPP())
{
node.getMeasure().setTypeQualifier(((AFunctionType) node.getType()).getParameters());
}
node.setMeasureDef(question.env.findName(node.getMeasure(), question.scope));
if (node.getBody() == null)
{
TypeCheckerErrors.report(3273, "Measure not allowed for an implicit function", node.getMeasure().getLocation(), node);
} else if (node.getMeasureDef() == null)
{
TypeCheckerErrors.report(3270, "Measure " + node.getMeasure()
+ " is not in scope", node.getMeasure().getLocation(), node.getMeasure());
} else if (!(node.getMeasureDef() instanceof AExplicitFunctionDefinition))
{
TypeCheckerErrors.report(3271, "Measure " + node.getMeasure()
+ " is not an explicit function", node.getMeasure().getLocation(), node.getMeasure());
} else
{
AExplicitFunctionDefinition efd = (AExplicitFunctionDefinition) node.getMeasureDef();
if (node.getTypeParams() == null && efd.getTypeParams() != null)
{
TypeCheckerErrors.report(3309, "Measure must not be polymorphic", node.getMeasure().getLocation(), node.getMeasure());
} else if (node.getTypeParams() != null
&& efd.getTypeParams() == null)
{
TypeCheckerErrors.report(3310, "Measure must also be polymorphic", node.getMeasure().getLocation(), node.getMeasure());
} else if (node.getTypeParams() != null
&& efd.getTypeParams() != null
&& !node.getTypeParams().equals(efd.getTypeParams()))
{
TypeCheckerErrors.report(3318, "Measure's type parameters must match function's", node.getMeasure().getLocation(), node.getMeasure());
TypeCheckerErrors.detail2("Actual", efd.getTypeParams(), "Expected", node.getTypeParams());
}
AFunctionType mtype = (AFunctionType) node.getMeasureDef().getType();
if (!question.assistantFactory.getTypeComparator().compatible(mtype.getParameters(), ((AFunctionType) node.getType()).getParameters()))
{
TypeCheckerErrors.report(3303, "Measure parameters different to function", node.getMeasure().getLocation(), node.getMeasure());
TypeCheckerErrors.detail2(node.getMeasure().getName(), mtype.getParameters(), node.getName().getName(), ((AFunctionType) node.getType()).getParameters());
}
if (!(mtype.getResult() instanceof ANatNumericBasicType))
{
if (question.assistantFactory.createPTypeAssistant().isProduct(mtype.getResult()))
{
AProductType pt = question.assistantFactory.createPTypeAssistant().getProduct(mtype.getResult());
for (PType t : pt.getTypes())
{
if (!(t instanceof ANatNumericBasicType))
{
TypeCheckerErrors.report(3272, "Measure range is not a nat, or a nat tuple", node.getMeasure().getLocation(), node.getMeasure());
TypeCheckerErrors.detail("Actual", mtype.getResult());
}
}
node.setMeasureLexical(pt.getTypes().size());
} else
{
TypeCheckerErrors.report(3272, "Measure range is not a nat, or a nat tuple", node.getMeasure().getLocation(), node.getMeasure());
TypeCheckerErrors.detail("Actual", mtype.getResult());
}
}
}
}
if (!(node.getBody() instanceof ANotYetSpecifiedExp)
&& !(node.getBody() instanceof ASubclassResponsibilityExp))
{
local.unusedCheck();
}
node.setType(node.getType());
return node.getType();
}
@Override
public PType caseAExplicitOperationDefinition(
AExplicitOperationDefinition node, TypeCheckInfo question)
throws AnalysisException
{
question.assistantFactory.getTypeComparator().checkComposeTypes(node.getType(), question.env, false);
List<PType> ptypes = ((AOperationType) node.getType()).getParameters();
if (node.getParameterPatterns().size() > ptypes.size())
{
TypeCheckerErrors.report(3023, "Too many parameter patterns", node.getLocation(), node);
TypeCheckerErrors.detail2("Type params", ptypes.size(), "Patterns", node.getParameterPatterns().size());
return null;
} else if (node.getParameterPatterns().size() < ptypes.size())
{
TypeCheckerErrors.report(3024, "Too few parameter patterns", node.getLocation(), node);
TypeCheckerErrors.detail2("Type params", ptypes.size(), "Patterns", node.getParameterPatterns().size());
return null;
}
node.setParamDefinitions(question.assistantFactory.createAExplicitOperationDefinitionAssistant().getParamDefinitions(node));
question.assistantFactory.createPDefinitionListAssistant().typeCheck(node.getParamDefinitions(), THIS, new TypeCheckInfo(question.assistantFactory, question.env, NameScope.NAMESANDSTATE, question.qualifiers));
FlatCheckedEnvironment local = new FlatCheckedEnvironment(question.assistantFactory, node.getParamDefinitions(), question.env, NameScope.NAMESANDSTATE);
local.setStatic(question.assistantFactory.createPAccessSpecifierAssistant().isStatic(node.getAccess()));
local.setEnclosingDefinition(node);
local.setFunctional(false);
if (question.env.isVDMPP())
{
if (!question.assistantFactory.createPAccessSpecifierAssistant().isStatic(node.getAccess()))
{
local.add(question.assistantFactory.createPDefinitionAssistant().getSelfDefinition(node));
}
if (node.getIsConstructor())
{
if (question.assistantFactory.createPAccessSpecifierAssistant().isAsync(node.getAccess()) ||
question.assistantFactory.createPAccessSpecifierAssistant().isStatic(node.getAccess()) ||
node.getAccess().getPure())
{
TypeCheckerErrors.report(3286, "Constructor cannot be 'async', 'static' or 'pure'", node.getLocation(), node);
}
if (question.assistantFactory.createPTypeAssistant().isClass(((AOperationType) node.getType()).getResult(), question.env))
{
AClassType ctype = question.assistantFactory.createPTypeAssistant().getClassType(((AOperationType) node.getType()).getResult(), question.env);
if (ctype.getClassdef() != node.getClassDefinition())
{
// FIXME: This is a TEST, it should be tried to see if
// it is valid
TypeCheckerErrors.report(3025, "Constructor operation must have return type "
+ node.getClassDefinition().getName().getName(), node.getLocation(), node);
}
// TODO: THIS COULD BE A HACK to code (ctype.getClassdef()
// != node.getClassDefinition())
if (!question.assistantFactory.getLexNameTokenAssistant().isEqual(ctype.getClassdef().getName(), node.getClassDefinition().getName()))
{
TypeCheckerErrors.report(3025, "Constructor operation must have return type "
+ node.getClassDefinition().getName().getName(), node.getLocation(), node);
}
} else
{
TypeCheckerErrors.report(3026, "Constructor operation must have return type "
+ node.getClassDefinition().getName().getName(), node.getLocation(), node);
}
}
}
List<QualifiedDefinition> qualified = new Vector<QualifiedDefinition>();
if (node.getPredef() != null)
{
FlatEnvironment pre = new FlatEnvironment(question.assistantFactory, new Vector<PDefinition>(), local);
pre.setEnclosingDefinition(node.getPredef());
pre.setFunctional(true);
PType b = node.getPredef().getBody().apply(THIS, new TypeCheckInfo(question.assistantFactory, pre, NameScope.NAMESANDSTATE));
ABooleanBasicType expected = AstFactory.newABooleanBasicType(node.getLocation());
if (!question.assistantFactory.createPTypeAssistant().isType(b, ABooleanBasicType.class))
{
TypeCheckerErrors.report(3018, "Precondition returns unexpected type", node.getLocation(), node);
TypeCheckerErrors.detail2("Actual", b, "Expected", expected);
}
qualified = node.getPredef().getBody().apply(question.assistantFactory.getQualificationVisitor(), new TypeCheckInfo(question.assistantFactory, pre, NameScope.NAMESANDSTATE));
for (QualifiedDefinition qdef : qualified)
{
qdef.qualifyType();
}
}
if (node.getPostdef() != null)
{
LexNameToken result = new LexNameToken(node.getName().getModule(), "RESULT", node.getLocation());
PPattern rp = AstFactory.newAIdentifierPattern(result);
List<PDefinition> rdefs = question.assistantFactory.createPPatternAssistant().getDefinitions(rp, ((AOperationType) node.getType()).getResult(), NameScope.NAMESANDANYSTATE);
FlatEnvironment post = new FlatEnvironment(question.assistantFactory, rdefs, local);
post.setEnclosingDefinition(node.getPostdef());
post.setFunctional(true);
PType b = node.getPostdef().getBody().apply(THIS, new TypeCheckInfo(question.assistantFactory, post, NameScope.NAMESANDANYSTATE));
ABooleanBasicType expected = AstFactory.newABooleanBasicType(node.getLocation());
if (!question.assistantFactory.createPTypeAssistant().isType(b, ABooleanBasicType.class))
{
TypeCheckerErrors.report(3018, "Postcondition returns unexpected type", node.getLocation(), node);
TypeCheckerErrors.detail2("Actual", b, "Expected", expected);
}
}
PType expectedResult = ((AOperationType) node.getType()).getResult();
PType actualResult = node.getBody().apply(THIS, new TypeCheckInfo(question.assistantFactory, local, NameScope.NAMESANDSTATE, null, null, expectedResult));
node.setActualResult(actualResult);
boolean compatible = question.assistantFactory.getTypeComparator().compatible(expectedResult, node.getActualResult());
for (QualifiedDefinition qdef : qualified)
{
qdef.resetType();
}
if (node.getIsConstructor()
&& !question.assistantFactory.createPTypeAssistant().isType(node.getActualResult(), AVoidType.class)
&& !compatible || !node.getIsConstructor() && !compatible)
{
TypeCheckerErrors.report(3027, "Operation returns unexpected type", node.getLocation(), node);
TypeCheckerErrors.detail2("Actual", node.getActualResult(), "Expected", ((AOperationType) node.getType()).getResult());
} else if (!node.getIsConstructor()
&& !question.assistantFactory.createPTypeAssistant().isUnknown(actualResult))
{
if (question.assistantFactory.createPTypeAssistant().isVoid(((AOperationType) node.getType()).getResult())
&& !question.assistantFactory.createPTypeAssistant().isVoid(actualResult))
{
TypeCheckerErrors.report(3312, "Void operation returns non-void value", node.getLocation(), node);
TypeCheckerErrors.detail2("Actual", actualResult, "Expected", ((AOperationType) node.getType()).getResult());
} else if (!question.assistantFactory.createPTypeAssistant().isVoid(((AOperationType) node.getType()).getResult())
&& question.assistantFactory.createPTypeAssistant().hasVoid(actualResult))
{
TypeCheckerErrors.report(3313, "Operation returns void value", node.getLocation(), node);
TypeCheckerErrors.detail2("Actual", actualResult, "Expected", ((AOperationType) node.getType()).getResult());
}
}
if (question.assistantFactory.createPAccessSpecifierAssistant().isAsync(node.getAccess())
&& !question.assistantFactory.createPTypeAssistant().isType(((AOperationType) node.getType()).getResult(), AVoidType.class))
{
TypeCheckerErrors.report(3293, "Asynchronous operation '"
+ node.getName() + "' cannot return a value", node.getLocation(), node);
}
if (node.getAccess().getPure() &&
question.assistantFactory.createPTypeAssistant().isType(((AOperationType) node.getType()).getResult(), AVoidType.class) &&
!question.assistantFactory.createPTypeAssistant().isUnknown(((AOperationType) node.getType()).getResult()))
{
TypeCheckerErrors.report(3344, "Pure operation '" + node.getName() + "' must return a value", node.getLocation(), node);
}
if (node.getAccess().getPure() && question.assistantFactory.createPAccessSpecifierAssistant().isAsync(node.getAccess()))
{
TypeCheckerErrors.report(3345, "Pure operation '" + node.getName() + "' cannot also be async", node.getLocation(), node);
}
if (question.assistantFactory.createPTypeAssistant().narrowerThan(node.getType(), node.getAccess()))
{
TypeCheckerErrors.report(3028, "Operation parameter visibility less than operation definition", node.getLocation(), node);
}
if (question.env.isVDMPP())
{
PAccessSpecifierAssistantTC assist = question.assistantFactory.createPAccessSpecifierAssistant();
if (assist.isPrivate(node.getAccess())
&& node.getBody() instanceof ASubclassResponsibilityStm)
{
TypeCheckerErrors.report(3329, "Abstract function/operation must be public or protected", node.getLocation(), node);
}
}
if (!(node.getBody() instanceof ANotYetSpecifiedStm)
&& !(node.getBody() instanceof ASubclassResponsibilityStm))
{
local.unusedCheck();
}
node.setType(node.getType());
return node.getType();
}
@Override
public PType caseAImplicitOperationDefinition(
AImplicitOperationDefinition node, TypeCheckInfo question)
throws AnalysisException
{
question.assistantFactory.getTypeComparator().checkComposeTypes(node.getType(), question.env, false);
question = new TypeCheckInfo(question.assistantFactory, question.env, NameScope.NAMESANDSTATE, question.qualifiers);
List<PDefinition> defs = new Vector<PDefinition>();
List<PDefinition> argdefs = new Vector<PDefinition>();
if (question.env.isVDMPP())
{
node.setStateDefinition(question.env.findClassDefinition());
} else
{
node.setStateDefinition(question.env.findStateDefinition());
}
for (APatternListTypePair ptp : node.getParameterPatterns())
{
argdefs.addAll(getDefinitions(ptp, NameScope.LOCAL, question.assistantFactory));
}
defs.addAll(question.assistantFactory.createPDefinitionAssistant().checkDuplicatePatterns(node, argdefs));
if (node.getResult() != null)
{
defs.addAll(question.assistantFactory.createPPatternAssistant().getDefinitions(node.getResult().getPattern(), ((AOperationType) node.getType()).getResult(), NameScope.STATE));
}
// 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 - but only if we have
// an "ext" clause
boolean limitStateScope = false;
if (node.getExternals().size() != 0)
{
for (AExternalClause clause : node.getExternals())
{
question.assistantFactory.getTypeComparator().checkComposeTypes(clause.getType(), question.env, false);
for (ILexNameToken exname : clause.getIdentifiers())
{
PDefinition sdef = question.env.findName(exname, NameScope.STATE);
typeResolve(clause, THIS, question);
if (sdef == null)
{
TypeCheckerErrors.report(3031, "Unknown state variable "
+ exname, exname.getLocation(), exname);
} else
{
if (!(clause.getType() instanceof AUnknownType)
&& !question.assistantFactory.createPTypeAssistant().equals(sdef.getType(), clause.getType()))
{
TypeCheckerErrors.report(3032, "State variable "
+ exname + " is not this type", node.getLocation(), node);
TypeCheckerErrors.detail2("Declared", sdef.getType(), "ext type", clause.getType());
} else
{
defs.add(AstFactory.newAExternalDefinition(sdef, clause.getMode()));
// VDM++ "ext wr" clauses in a constructor
// effectively
// initialize the instance variable concerned.
if (clause.getMode().getType() == VDMToken.WRITE
&& sdef instanceof AInstanceVariableDefinition
&& node.getName().getName().equals(node.getClassDefinition().getName().getName()))
{
AInstanceVariableDefinition iv = (AInstanceVariableDefinition) sdef;
iv.setInitialized(true);
}
}
}
}
}
// All relevant globals are now in defs (local), so we
// limit the state searching scope
limitStateScope = true;
}
question.assistantFactory.createPDefinitionListAssistant().typeCheck(defs, THIS, question);
FlatCheckedEnvironment local = new FlatCheckedEnvironment(question.assistantFactory, defs, question.env, question.scope);
local.setLimitStateScope(limitStateScope);
local.setStatic(question.assistantFactory.createPAccessSpecifierAssistant().isStatic(node.getAccess()));
local.setEnclosingDefinition(node);
local.setFunctional(false);
if (question.env.isVDMPP())
{
if (node.getIsConstructor())
{
if (question.assistantFactory.createPAccessSpecifierAssistant().isAsync(node.getAccess()) ||
question.assistantFactory.createPAccessSpecifierAssistant().isStatic(node.getAccess()) ||
node.getAccess().getPure())
{
TypeCheckerErrors.report(3286, "Constructor cannot be 'async', 'static' or 'pure'", node.getLocation(), node);
}
if (question.assistantFactory.createPTypeAssistant().isClass(((AOperationType) node.getType()).getResult(), question.env))
{
AClassType ctype = question.assistantFactory.createPTypeAssistant().getClassType(((AOperationType) node.getType()).getResult(), question.env);
if (ctype.getClassdef() != node.getClassDefinition())
{
TypeCheckerErrors.report(3025, "Constructor operation must have return type "
+ node.getClassDefinition().getName().getName(), node.getLocation(), node);
}
} else
{
TypeCheckerErrors.report(3026, "Constructor operation must have return type "
+ node.getClassDefinition().getName().getName(), node.getLocation(), node);
}
}
}
List<QualifiedDefinition> qualified = new Vector<QualifiedDefinition>();
if (node.getPredef() != null)
{
FlatEnvironment pre = new FlatEnvironment(question.assistantFactory, new Vector<PDefinition>(), local);
pre.setEnclosingDefinition(node.getPredef());
pre.setFunctional(true);
PType b = node.getPredef().getBody().apply(THIS, new TypeCheckInfo(question.assistantFactory, pre, NameScope.NAMESANDSTATE));
ABooleanBasicType expected = AstFactory.newABooleanBasicType(node.getLocation());
if (!question.assistantFactory.createPTypeAssistant().isType(b, ABooleanBasicType.class))
{
TypeCheckerErrors.report(3018, "Precondition returns unexpected type", node.getLocation(), node);
TypeCheckerErrors.detail2("Actual", b, "Expected", expected);
}
qualified = node.getPredef().getBody().apply(question.assistantFactory.getQualificationVisitor(), new TypeCheckInfo(question.assistantFactory, pre, NameScope.NAMESANDSTATE));
for (QualifiedDefinition qdef : qualified)
{
qdef.qualifyType();
}
}
if (node.getBody() != null)
{
if (node.getClassDefinition() != null
&& !question.assistantFactory.createPAccessSpecifierAssistant().isStatic(node.getAccess()))
{
local.add(question.assistantFactory.createPDefinitionAssistant().getSelfDefinition(node));
}
PType expectedResult = ((AOperationType) node.getType()).getResult();
node.setActualResult(node.getBody().apply(THIS, new TypeCheckInfo(question.assistantFactory, local, NameScope.NAMESANDSTATE, null, null, expectedResult)));
boolean compatible = question.assistantFactory.getTypeComparator().compatible(expectedResult, node.getActualResult());
if (node.getIsConstructor()
&& !question.assistantFactory.createPTypeAssistant().isType(node.getActualResult(), AVoidType.class)
&& !compatible || !node.getIsConstructor() && !compatible)
{
TypeCheckerErrors.report(3035, "Operation returns unexpected type", node.getLocation(), node);
TypeCheckerErrors.detail2("Actual", node.getActualResult(), "Expected", ((AOperationType) node.getType()).getResult());
} else if (!node.getIsConstructor()
&& !question.assistantFactory.createPTypeAssistant().isUnknown(node.getActualResult()))
{
if (question.assistantFactory.createPTypeAssistant().isVoid(((AOperationType) node.getType()).getResult())
&& !question.assistantFactory.createPTypeAssistant().isVoid(node.getActualResult()))
{
TypeCheckerErrors.report(3312, "Void operation returns non-void value", node.getLocation(), node);
TypeCheckerErrors.detail2("Actual", node.getActualResult(), "Expected", ((AOperationType) node.getType()).getResult());
} else if (!question.assistantFactory.createPTypeAssistant().isVoid(((AOperationType) node.getType()).getResult())
&& question.assistantFactory.createPTypeAssistant().hasVoid(node.getActualResult()))
{
TypeCheckerErrors.report(3313, "Operation returns void value", node.getLocation(), node);
TypeCheckerErrors.detail2("Actual", node.getActualResult(), "Expected", ((AOperationType) node.getType()).getResult());
}
}
}
for (QualifiedDefinition qdef : qualified)
{
qdef.resetType();
}
if (question.assistantFactory.createPAccessSpecifierAssistant().isAsync(node.getAccess())
&& !question.assistantFactory.createPTypeAssistant().isType(((AOperationType) node.getType()).getResult(), AVoidType.class))
{
TypeCheckerErrors.report(3293, "Asynchronous operation "
+ node.getName() + " cannot return a value", node.getLocation(), node);
}
if (node.getAccess().getPure() &&
question.assistantFactory.createPTypeAssistant().isType(((AOperationType) node.getType()).getResult(), AVoidType.class) &&
!question.assistantFactory.createPTypeAssistant().isUnknown(((AOperationType) node.getType()).getResult()))
{
TypeCheckerErrors.report(3344, "Pure operation '" + node.getName() + "' must return a value", node.getLocation(), node);
}
if (node.getAccess().getPure() &&
question.assistantFactory.createPAccessSpecifierAssistant().isAsync(node.getAccess()))
{
TypeCheckerErrors.report(3345, "Pure operation '" + node.getName() + "' cannot also be async", node.getLocation(), node);
}
if (question.assistantFactory.createPTypeAssistant().narrowerThan(node.getType(), node.getAccess()))
{
TypeCheckerErrors.report(3036, "Operation parameter visibility less than operation definition", node.getLocation(), node);
}
if (question.env.isVDMPP())
{
PAccessSpecifierAssistantTC assist = question.assistantFactory.createPAccessSpecifierAssistant();
if (assist.isPrivate(node.getAccess())
&& node.getBody() instanceof ASubclassResponsibilityStm)
{
TypeCheckerErrors.report(3329, "Abstract function/operation must be public or protected", node.getLocation(), node);
}
}
// The result variables are in scope for the post condition
if (node.getPostdef() != null)
{
PType b = null;
if (node.getResult() != null)
{
List<PDefinition> postdefs = question.assistantFactory.createAPatternTypePairAssistant().getDefinitions(node.getResult());
FlatCheckedEnvironment post = new FlatCheckedEnvironment(question.assistantFactory, postdefs, local, NameScope.NAMESANDANYSTATE);
post.setStatic(question.assistantFactory.createPAccessSpecifierAssistant().isStatic(node.getAccess()));
post.setEnclosingDefinition(node.getPostdef());
post.setFunctional(true);
b = node.getPostdef().getBody().apply(THIS, new TypeCheckInfo(question.assistantFactory, post, NameScope.NAMESANDANYSTATE));
post.unusedCheck();
} else
{
FlatEnvironment post = new FlatEnvironment(question.assistantFactory, new Vector<PDefinition>(), local);
post.setEnclosingDefinition(node.getPostdef());
post.setFunctional(true);
b = node.getPostdef().getBody().apply(THIS, new TypeCheckInfo(question.assistantFactory, post, NameScope.NAMESANDANYSTATE));
}
ABooleanBasicType expected = AstFactory.newABooleanBasicType(node.getLocation());
if (!question.assistantFactory.createPTypeAssistant().isType(b, ABooleanBasicType.class))
{
TypeCheckerErrors.report(3018, "Postcondition returns unexpected type", node.getLocation(), node);
TypeCheckerErrors.detail2("Actual", b, "Expected", expected);
}
}
if (node.getErrors() != null)
{
for (AErrorCase error : node.getErrors())
{
TypeCheckInfo newQuestion = new TypeCheckInfo(question.assistantFactory, local, NameScope.NAMESANDSTATE);
PType a = error.getLeft().apply(THIS, newQuestion);
if (!question.assistantFactory.createPTypeAssistant().isType(a, ABooleanBasicType.class))
{
TypeCheckerErrors.report(3307, "Errs clause is not bool -> bool", error.getLeft().getLocation(), error.getLeft());
}
newQuestion.scope = NameScope.NAMESANDANYSTATE;
PType b = error.getRight().apply(THIS, newQuestion);
if (!question.assistantFactory.createPTypeAssistant().isType(b, ABooleanBasicType.class))
{
TypeCheckerErrors.report(3307, "Errs clause is not bool -> bool", error.getRight().getLocation(), error.getRight());
}
}
}
if (!(node.getBody() instanceof ANotYetSpecifiedStm)
&& !(node.getBody() instanceof ASubclassResponsibilityStm))
{
local.unusedCheck();
}
// node.setType(node.getActualResult());
return node.getType();
}
@Override
public PType caseAImportedDefinition(AImportedDefinition node,
TypeCheckInfo question) throws AnalysisException
{
node.setType(node.getDef().apply(THIS, question));
return node.getType();
}
@Override
public PType caseAInheritedDefinition(AInheritedDefinition node,
TypeCheckInfo question) throws AnalysisException
{
node.setType(node.getSuperdef().apply(THIS, question));
return node.getType();
}
@Override
public PType caseALocalDefinition(ALocalDefinition node,
TypeCheckInfo question)
{
if (node.getType() != null)
{
node.setType(question.assistantFactory.createPTypeAssistant().typeResolve(node.getType(), null, THIS, question));
}
return node.getType();
}
@Override
public PType caseAMultiBindListDefinition(AMultiBindListDefinition node,
TypeCheckInfo question) throws AnalysisException
{
if (node.getType() != null)
{
question.assistantFactory.getTypeComparator().checkComposeTypes(node.getType(), question.env, false);
}
List<PDefinition> defs = new Vector<PDefinition>();
for (PMultipleBind mb : node.getBindings())
{
PType type = mb.apply(THIS, question);
defs.addAll(question.assistantFactory.createPMultipleBindAssistant().getDefinitions(mb, type, question));
}
question.assistantFactory.createPDefinitionListAssistant().typeCheck(defs, THIS, question);
node.setDefs(defs);
return null;
}
@Override
public PType caseAMutexSyncDefinition(AMutexSyncDefinition node,
TypeCheckInfo question) throws AnalysisException
{
SClassDefinition classdef = question.env.findClassDefinition();
if (node.getOperations().isEmpty())
{
// Add all locally visibly callable operations for mutex(all)
for (PDefinition def : node.getClassDefinition().apply(question.assistantFactory.getDefinitionCollector()))
// SClassDefinitionAssistantTC.getLocalDefinitions(node.getClassDefinition()))
{
if (question.assistantFactory.createPDefinitionAssistant().isCallableOperation(def)
&& !def.getName().getName().equals(classdef.getName().getName()))
{
node.getOperations().add(def.getName());
}
}
}
for (ILexNameToken opname : node.getOperations())
{
int found = 0;
List<PDefinition> definitions = question.assistantFactory.createPDefinitionAssistant().getDefinitions(classdef);
for (PDefinition def : definitions)
{
if (def.getName() != null && def.getName().matches(opname))
{
found++;
if (!question.assistantFactory.createPDefinitionAssistant().isCallableOperation(def))
{
TypeCheckerErrors.report(3038, opname
+ " is not an explicit operation", opname.getLocation(), opname);
}
if (def.getAccess().getPure())
{
TypeCheckerErrors.report(3343, "Cannot have a mutex with pure operations", opname.getLocation(), opname);
}
}
}
if (found == 0)
{
TypeCheckerErrors.report(3039, opname + " is not in scope", opname.getLocation(), opname);
} else if (found > 1)
{
TypeCheckerErrors.warning(5002, "Mutex of overloaded operation", opname.getLocation(), opname);
}
if (opname.getName().equals(classdef.getName().getName()))
{
TypeCheckerErrors.report(3040, "Cannot put mutex on a constructor", opname.getLocation(), opname);
}
for (ILexNameToken other : node.getOperations())
{
if (opname != other
&& question.assistantFactory.getLexNameTokenAssistant().isEqual(opname, other))
{
TypeCheckerErrors.report(3041, "Duplicate mutex name", opname.getLocation(), opname);
}
}
}
return null;
}
@Override
public PType caseANamedTraceDefinition(ANamedTraceDefinition node,
TypeCheckInfo question) throws AnalysisException
{
if (question.env.isVDMPP())
{
question = new TypeCheckInfo(question.assistantFactory, new FlatEnvironment(question.assistantFactory, question.assistantFactory.createPDefinitionAssistant().getSelfDefinition(node), question.env), question.scope, question.qualifiers);
}
for (ATraceDefinitionTerm term : node.getTerms())
{
typeCheck(term.getList(), THIS, new TypeCheckInfo(question.assistantFactory, question.env, NameScope.NAMESANDSTATE));
}
// Mark node as used, as traces are not used anyway
question.assistantFactory.createPDefinitionAssistant().markUsed(node);
return null;
}
@Override
public PType caseAPerSyncDefinition(APerSyncDefinition node,
TypeCheckInfo question) throws AnalysisException
{
Environment base = question.env;
SClassDefinition classdef = base.findClassDefinition();
int opfound = 0;
int perfound = 0;
Boolean isStatic = null;
List<PDefinition> definitions = question.assistantFactory.createPDefinitionAssistant().getDefinitions(classdef);
for (PDefinition def : definitions)
{
if (def.getName() != null
&& def.getName().matches(node.getOpname()))
{
opfound++;
if (!question.assistantFactory.createPDefinitionAssistant().isCallableOperation(def))
{
TypeCheckerErrors.report(3042, node.getOpname()
+ " is not an explicit operation", node.getOpname().getLocation(), node.getOpname());
}
if (isStatic != null
&& isStatic != question.assistantFactory.createPDefinitionAssistant().isStatic(def))
{
TypeCheckerErrors.report(3323, "Overloaded operation cannot mix static and non-static", node.getLocation(), node.getOpname());
}
if (def.getAccess().getPure())
{
TypeCheckerErrors.report(3340, "Pure operation cannot have permission predicate",
node.getOpname().getLocation(), node.getOpname());
}
isStatic = question.assistantFactory.createPDefinitionAssistant().isStatic(def);
}
if (def instanceof APerSyncDefinition)
{
APerSyncDefinition psd = (APerSyncDefinition) def;
if (psd.getOpname().equals(node.getOpname()))
{
perfound++;
}
}
}
ILexNameToken opname = node.getOpname();
if (opfound == 0)
{
TypeCheckerErrors.report(3043, opname + " is not in scope", opname.getLocation(), opname);
} else if (opfound > 1)
{
TypeCheckerErrors.warning(5003, "Permission guard of overloaded operation", opname.getLocation(), opname);
}
if (perfound != 1)
{
TypeCheckerErrors.report(3044, "Duplicate permission guard found for "
+ opname, opname.getLocation(), opname);
}
if (opname.getName().equals(classdef.getName().getName()))
{
TypeCheckerErrors.report(3045, "Cannot put guard on a constructor", opname.getLocation(), opname);
}
FlatCheckedEnvironment local = new FlatCheckedEnvironment(question.assistantFactory, node, base, NameScope.NAMESANDSTATE);
local.setEnclosingDefinition(node); // Prevent op calls
if (isStatic != null)
{
local.setStatic(isStatic);
}
PType rt = node.getGuard().apply(THIS, new TypeCheckInfo(question.assistantFactory, local, NameScope.NAMESANDSTATE));
if (!question.assistantFactory.createPTypeAssistant().isType(rt, ABooleanBasicType.class))
{
TypeCheckerErrors.report(3046, "Guard is not a boolean expression", node.getGuard().getLocation(), node.getGuard());
}
node.setType(rt);
return node.getType();
}
@Override
public PType caseARenamedDefinition(ARenamedDefinition node,
TypeCheckInfo question) throws AnalysisException
{
node.setType(node.getDef().apply(THIS, question));
return node.getType();
}
@Override
public PType caseAStateDefinition(AStateDefinition node,
TypeCheckInfo question) throws AnalysisException
{
Environment base = question.env;
if (base.findStateDefinition() != node)
{
TypeCheckerErrors.report(3047, "Only one state definition allowed per module", node.getLocation(), node);
return null;
}
for (PDefinition def : node.getStateDefs())
{
if (!def.getName().getOld()) // Don't check old names
{
question.assistantFactory.getTypeComparator().checkComposeTypes(def.getType(), question.env, false);
}
}
question.assistantFactory.createPDefinitionListAssistant().typeCheck(node.getStateDefs(), THIS, question);
if (node.getInvdef() != null)
{
node.getInvdef().apply(THIS, question);
}
if (node.getInitdef() != null)
{
node.getInitdef().apply(THIS, question);
}
return null;
}
@Override
public PType caseAThreadDefinition(AThreadDefinition node,
TypeCheckInfo question) throws AnalysisException
{
question.scope = NameScope.NAMESANDSTATE;
FlatEnvironment local = new FlatEnvironment(question.assistantFactory, question.assistantFactory.createPDefinitionAssistant().getSelfDefinition(node), question.env);
PType rt = node.getStatement().apply(THIS, new TypeCheckInfo(question.assistantFactory, local, question.scope));
if (!(rt instanceof AVoidType) && !(rt instanceof AUnknownType))
{
TypeCheckerErrors.report(3049, "Thread statement/operation must not return a value", node.getLocation(), node);
}
node.setType(rt);
node.getOperationDef().setBody(node.getStatement().clone());// This
// operation
// is a
// wrapper
// for the
// thread
return rt;
}
@Override
public PType caseATypeDefinition(ATypeDefinition node,
TypeCheckInfo question) throws AnalysisException
{
if (node.getInvdef() != null)
{
question.scope = NameScope.NAMES;
node.getInvdef().apply(THIS, question);
}
PType type = question.assistantFactory.createPDefinitionAssistant().getType(node);
node.setType(type);
// We have to do the "top level" here, rather than delegating to the types
// because the definition pointer from these top level types just refers
// to the definition we are checking, which is never "narrower" than itself.
// See the narrowerThan method in NamedType and RecordType.
if (type instanceof ANamedInvariantType)
{
ANamedInvariantType ntype = (ANamedInvariantType) type;
// Rebuild the compose definitions, after we check whether they already exist
node.getComposeDefinitions().clear();
for (PType compose : question.assistantFactory.getTypeComparator().checkComposeTypes(ntype.getType(), question.env, true))
{
ARecordInvariantType rtype = (ARecordInvariantType) compose;
PDefinition cdef = AstFactory.newATypeDefinition(rtype.getName(), rtype, null, null);
cdef.setAccess(node.getAccess().clone());
node.getComposeDefinitions().add(cdef);
rtype.getDefinitions().get(0).setAccess(node.getAccess().clone());
}
if (question.assistantFactory.createPTypeAssistant().narrowerThan(ntype.getType(), node.getAccess()))
{
TypeCheckerErrors.report(3321, "Type component visibility less than type's definition", node.getLocation(), node);
}
} else if (type instanceof ARecordInvariantType)
{
ARecordInvariantType rtype = (ARecordInvariantType) type;
for (AFieldField field : rtype.getFields())
{
question.assistantFactory.getTypeComparator().checkComposeTypes(field.getType(), question.env, false);
if (question.assistantFactory.createPTypeAssistant().narrowerThan(field.getType(), node.getAccess()))
{
TypeCheckerErrors.report(3321, "Field type visibility less than type's definition", field.getTagname().getLocation(), field.getTagname());
}
}
}
return node.getType();
}
@Override
public PType caseAUntypedDefinition(AUntypedDefinition node,
TypeCheckInfo question)
{
assert false : "Can't type check untyped definition?";
return null;
}
@Override
public PType caseAValueDefinition(AValueDefinition node,
TypeCheckInfo question) throws AnalysisException
{
if (node.getType() != null)
{
question.assistantFactory.getTypeComparator().checkComposeTypes(node.getType(), question.env, false);
}
// Enable constraint checking
question = question.newConstraint(node.getType());
question.qualifiers = null;
ExcludedDefinitions.setExcluded(node.getDefs());
PType expType = node.getExpression().apply(THIS, question);
ExcludedDefinitions.clearExcluded();
node.setExpType(expType);
PType type = node.getType(); // PDefinitionAssistant.getType(node);
if (expType instanceof AUnknownType)
{
node.setPass(Pass.FINAL); // Do it again later
}
if (expType instanceof AVoidType)
{
TypeCheckerErrors.report(3048, "Expression does not return a value", node.getExpression().getLocation(), node.getExpression());
} else if (type != null && !(type instanceof AUnknownType))
{
if (!question.assistantFactory.getTypeComparator().compatible(type, expType))
{
TypeCheckerErrors.report(3051, "Expression does not match declared type", node.getLocation(), node);
TypeCheckerErrors.detail2("Declared", type, "Expression", expType);
}
} else
{
type = expType;
node.setType(expType);
}
Environment base = question.env;
if (base.isVDMPP() && type instanceof ANamedInvariantType)
{
ANamedInvariantType named = (ANamedInvariantType) type;
PDefinition typedef = base.findType(named.getName(), node.getLocation().getModule());
if (typedef == null)
{
TypeCheckerErrors.report(2048, "Cannot find symbol "
+ named.getName().toString(), named.getLocation(), named);
return node.getType();
}
if (question.assistantFactory.createPAccessSpecifierAssistant().narrowerThan(typedef.getAccess(), node.getAccess()))
{
TypeCheckerErrors.report(3052, "Value type visibility less than value definition", node.getLocation(), node);
}
}
node.apply(question.assistantFactory.getDefinitionTypeResolver(), new DefinitionTypeResolver.NewQuestion(THIS, question));
// PPatternAssistantTC.typeResolve(pattern, THIS, question);
// question.assistantFactory.getTypeResolver().updateDefs(node, question);
question.qualifiers = null;
question.assistantFactory.createPDefinitionListAssistant().typeCheck(node.getDefs(), THIS, question);
return node.getType();
}
@Override
public PType caseALetDefBindingTraceDefinition(
ALetDefBindingTraceDefinition node, TypeCheckInfo question)
throws AnalysisException
{
return typeCheckLet(node, node.getLocalDefs(), node.getBody(), question);
}
@Override
public PType caseALetBeStBindingTraceDefinition(
ALetBeStBindingTraceDefinition node, TypeCheckInfo question)
throws AnalysisException
{
Entry<PType, AMultiBindListDefinition> res = typecheckLetBeSt(node, node.getLocation(), node.getBind(), node.getStexp(), node.getBody(), question);
node.setDef(res.getValue());
return res.getKey();
}
@Override
public PType caseARepeatTraceDefinition(ARepeatTraceDefinition node,
TypeCheckInfo question) throws AnalysisException
{
if (node.getFrom() > node.getTo())
{
TypeCheckerErrors.report(3277, "Trace repeat illegal values", node.getLocation(), node);
}
// Environment local = question.env;
return node.getCore().apply(THIS, question);
}
@Override
public PType caseAConcurrentExpressionTraceCoreDefinition(
AConcurrentExpressionTraceCoreDefinition node,
TypeCheckInfo question) throws AnalysisException
{
for (PTraceDefinition d : node.getDefs())
{
d.apply(THIS, question);
}
return null;
}
@Override
public PType caseABracketedExpressionTraceCoreDefinition(
ABracketedExpressionTraceCoreDefinition node, TypeCheckInfo question)
throws AnalysisException
{
for (ATraceDefinitionTerm term : node.getTerms())
{
for (PTraceDefinition def : term.getList())
{
def.apply(THIS, question);
}
}
return null;
}
@Override
public PType caseAApplyExpressionTraceCoreDefinition(
AApplyExpressionTraceCoreDefinition node, TypeCheckInfo question)
throws AnalysisException
{
return node.getCallStatement().apply(THIS, question);
}
public void typeCheck(List<PTraceDefinition> term,
IQuestionAnswer<TypeCheckInfo, PType> rootVisitor,
TypeCheckInfo question) throws AnalysisException
{
for (PTraceDefinition def : term)
{
def.apply(rootVisitor, question);
}
}
public Collection<? extends PDefinition> getDefinitions(
APatternListTypePair pltp, NameScope scope, ITypeCheckerAssistantFactory assistantFactory)
{
List<PDefinition> list = new Vector<PDefinition>();
for (PPattern p : pltp.getPatterns())
{
list.addAll(assistantFactory.createPPatternAssistant().getDefinitions(p, pltp.getType(), scope));
}
return list;
}
public void typeResolve(AExternalClause clause,
IQuestionAnswer<TypeCheckInfo, PType> rootVisitor,
TypeCheckInfo question)
{
clause.setType(question.assistantFactory.createPTypeAssistant().typeResolve(clause.getType(), null, rootVisitor, question));
}
}