/**
* Copyright (c) 2009-2011, The HATS Consortium. All rights reserved.
* This file is licensed under the terms of the Modified BSD License.
*/
package abs.frontend.typechecker.ext;
import abs.common.CompilerUtils;
import abs.frontend.analyser.ErrorMessage;
import abs.frontend.analyser.TypeError;
import abs.frontend.ast.ASTNode;
import abs.frontend.ast.ClassDecl;
import abs.frontend.ast.FieldDecl;
import abs.frontend.ast.FnApp;
import abs.frontend.ast.FunctionDecl;
import abs.frontend.ast.Model;
import abs.frontend.ast.NewExp;
import abs.frontend.ast.ParamDecl;
import abs.frontend.ast.PureExp;
import abs.frontend.ast.Stmt;
import abs.frontend.ast.VarOrFieldUse;
import abs.frontend.ast.VarUse;
import abs.frontend.typechecker.*;
public class SchedulerChecker extends DefaultTypeSystemExtension {
protected SchedulerChecker(Model m) {
super(m);
}
private void checkScheduleExp(PureExp sched, ClassDecl class_decl, ASTNode<?> loc) {
if (sched == null) return;
if (!(sched instanceof FnApp)) {
errors.add(new TypeError(loc, ErrorMessage.WRONG_SCHEDULER_ANNOTATION_TYPE,
sched.getType()));
return;
}
FnApp s = (FnApp) sched;
Type scheduler_type = s.getType();
FunctionDecl sd = (FunctionDecl)s.getDecl();
// check scheduling function return type
boolean schedulerTypeCorrect = scheduler_type.isDataType()
&& ((DataTypeType)scheduler_type).getQualifiedName().equals("ABS.Scheduler.Process");
// check scheduling function first arg, pt.1: are we a list?
boolean schedulerFunFirstArgCorrect = sd.getNumParam() > 0 &&
sd.getParam(0).getType().getQualifiedName().equals("ABS.StdLib.List");
if (schedulerFunFirstArgCorrect) {
// check scheduling function first arg, pt.2: are we a list of
// processes?
DataTypeType firstArgType = (DataTypeType)sd.getParam(0).getType();
if (firstArgType.numTypeArgs() != 1) {
// should not happen since ABS.StdLib.List takes 1 argument
schedulerFunFirstArgCorrect = false;
} else {
schedulerFunFirstArgCorrect = firstArgType.getTypeArg(0).getQualifiedName().equals("ABS.Scheduler.Process");
}
}
if (!schedulerTypeCorrect || !schedulerFunFirstArgCorrect) {
// emit two messages: one at the annotation location, one for the
// offending scheduler function
errors.add(new TypeError(loc, ErrorMessage.WRONG_SCHEDULER_ANNOTATION_TYPE,
"dummy"));
errors.add(new TypeError(sd, ErrorMessage.WRONG_SCHEDULER_FUN_TYPE,
"dummy"));
}
if (s.getNumParam() == 0 || !(s.getParam(0) instanceof VarUse)
|| !((VarUse)s.getParam(0)).getName().equals("queue")) {
// first arg to the scheduler expression must be the magic `queue'
errors.add(new TypeError(loc,
ErrorMessage.WRONG_SCHEDULER_FIRST_ARGUMENT,
"dummy"));
}
if (s.getNumParam() != sd.getNumParam()) {
errors.add(new TypeError(loc, ErrorMessage.WRONG_NUMBER_OF_ARGS,
s.getNumParam(), sd.getNumParam()));
} else {
// start from 1; magic first parameter `queue' already checked
for (int i = 1; i < s.getNumParam(); i++) {
PureExp arg = s.getParam(i);
String argname = "";
if (!(arg instanceof VarOrFieldUse)) {
// argument was not a plain identifier
errors.add(new TypeError(arg, ErrorMessage.WRONG_SCHEDULER_FIELD_ARGUMENT,
Integer.toString(i + 1),
class_decl.getName()));
} else {
// Check the rest of the parameters against class
// field/param names and argument types of the scheduling
// function. Parts of this could be elided if we verify
// that `VarUse's in scheduler annotation are rewritten to
// `FieldUse's -- then we'd just have to check for the
// presence of `VarUse' in the parameter list to detect
// invalid args. But can't hurt to open-code it (and we
// still have to check the type of all arguments vs the
// scheduling function parameters).
VarOrFieldUse vararg = (VarOrFieldUse)arg;
String name = vararg.getName();
Type argtype = UnknownType.INSTANCE;
for (ParamDecl p : class_decl.getParamList()) {
if (p.getName().equals(name)) argtype = p.getType();
}
for (FieldDecl f : class_decl.getFieldList()) {
if (f.getName().equals(name)) argtype = f.getType();
}
if (argtype.isUnknownType()) {
// identifier, but unknown in the class
errors.add(new TypeError(arg, ErrorMessage.WRONG_SCHEDULER_FIELD_ARGUMENT,
"\"" + name + "\"",
class_decl.getName()));
} else {
// argtype: field; paramtype: function arg
Type paramtype = sd.getParam(i).getType();
if (!argtype.isAssignableTo(paramtype)) {
errors.add(new TypeError(arg, ErrorMessage.TYPE_MISMATCH, argtype, paramtype));
}
}
}
}
}
}
@Override
public void checkNewExp(NewExp e) {
Stmt stmt = CompilerUtils.findStmtForExpression(e);
PureExp sched = CompilerUtils.getAnnotationValueFromName(stmt.getAnnotations(), "ABS.Scheduler.Scheduler");
if (sched != null) {
ClassDecl decl = (ClassDecl)(e.lookup(new KindedName(KindedName.Kind.CLASS,e.getClassName())));
checkScheduleExp(sched, decl, stmt);
}
}
@Override
public void checkClassDecl(ClassDecl decl) {
PureExp sched = CompilerUtils.getAnnotationValueFromName(decl.getAnnotations(), "ABS.Scheduler.Scheduler");
checkScheduleExp(sched, decl, decl);
}
}