use of edu.mit.csail.sdg.alloy4.ErrorWarning in project org.alloytools.alloy by AlloyTools.
the class CompModule method resolveAll.
// ============================================================================================================================//
/**
* This method resolves the entire world; NOTE: if it throws an exception, it
* may leave the world in an inconsistent state!
*/
static CompModule resolveAll(final A4Reporter rep, final CompModule root) throws Err {
final List<ErrorWarning> warns = new ArrayList<ErrorWarning>();
for (CompModule m : root.getAllReachableModules()) root.allModules.add(m);
resolveParams(rep, root.allModules);
resolveModules(rep, root.allModules);
for (CompModule m : root.allModules) for (Sig s : m.sigs.values()) root.sig2module.put(s, m);
// Resolves SigAST -> Sig, and topologically sort the sigs into the
// "sorted" array
root.new2old.put(UNIV, UNIV);
root.new2old.put(SIGINT, SIGINT);
root.new2old.put(SEQIDX, SEQIDX);
root.new2old.put(STRING, STRING);
root.new2old.put(NONE, NONE);
HashSet<Object> topo = new HashSet<Object>();
for (CompModule m : root.allModules) for (Sig s : m.sigs.values()) resolveSig(root, topo, s);
// (since fields in subsigs are allowed to refer to parent's fields)
for (Sig oldS : root.new2old.keySet()) resolveFieldDecl(root, rep, oldS, warns, false);
// Typecheck the function declarations
JoinableList<Err> errors = new JoinableList<Err>();
for (CompModule x : root.allModules) errors = x.resolveFuncDecls(rep, errors, warns);
if (!errors.isEmpty())
throw errors.pick();
// Typecheck the defined fields
for (Sig oldS : root.new2old.keySet()) resolveFieldDecl(root, rep, oldS, warns, true);
if (Version.experimental && root.seenDollar)
resolveMeta(root);
// Reject name clash
rejectNameClash(root.allModules);
// to function declarations)
for (CompModule x : root.allModules) {
errors = x.resolveFuncBody(rep, errors, warns);
errors = x.resolveAssertions(rep, errors, warns);
errors = x.resolveFacts(root, rep, errors, warns);
// root module's list of exact sigs
for (String n : x.exactParams) {
Sig sig = x.params.get(n);
if (sig != null)
root.exactSigs.add(sig);
}
}
if (!errors.isEmpty())
throw errors.pick();
// Typecheck the run/check commands (which can refer to function bodies
// and assertions)
root.resolveCommands(root.getAllReachableFacts());
if (!errors.isEmpty())
throw errors.pick();
for (ErrorWarning w : warns) rep.warning(w);
for (Sig s : root.exactSigs) rep.debug("Forced to be exact: " + s + "\n");
return root;
}
use of edu.mit.csail.sdg.alloy4.ErrorWarning in project org.alloytools.alloy by AlloyTools.
the class CompModule method resolveFieldDecl.
// ============================================================================================================================//
private static void resolveFieldDecl(CompModule res, final A4Reporter rep, final Sig s, final List<ErrorWarning> warns, boolean defined) throws Err {
// When typechecking each field:
// * it is allowed to refer to earlier fields in the same SIG or in any
// visible ancestor sig
// * it is allowed to refer to visible sigs
// * it is NOT allowed to refer to any predicate or function
// For example, if A.als opens B.als, and B/SIGX extends A/SIGY,
// then B/SIGX's fields cannot refer to A/SIGY, nor any fields in
// A/SIGY)
final List<Decl> oldDecls = res.old2fields.get(res.new2old.get(s));
if (oldDecls == null)
return;
final CompModule m = res.sig2module.get(s);
final Context cx = new Context(m, warns);
final ExprHasName dup = Decl.findDuplicateName(oldDecls);
if (dup != null)
throw new ErrorSyntax(dup.span(), "sig \"" + s + "\" cannot have 2 fields named \"" + dup.label + "\"");
for (final Decl d : oldDecls) {
if (d.expr.mult() != ExprUnary.Op.EXACTLYOF) {
if (defined)
continue;
} else {
if (!defined)
continue;
}
// The name "this" does matter, since the parser and the typechecker
// both refer to it as "this"
cx.rootfield = d;
cx.rootsig = s;
cx.put("this", s.decl.get());
Expr bound = cx.check(d.expr).resolve_as_set(warns);
cx.remove("this");
String[] names = new String[d.names.size()];
for (int i = 0; i < names.length; i++) names[i] = d.names.get(i).label;
Field[] fields = s.addTrickyField(d.span(), d.isPrivate, d.disjoint, d.disjoint2, null, names, bound);
for (Field f : fields) {
rep.typecheck("Sig " + s + ", Field " + f.label + ": " + f.type() + "\n");
}
}
}
use of edu.mit.csail.sdg.alloy4.ErrorWarning in project org.alloytools.alloy by AlloyTools.
the class CompModule method resolveFuncBody.
/**
* Each Func's body will now be typechecked Expr object.
*/
private JoinableList<Err> resolveFuncBody(A4Reporter rep, JoinableList<Err> errors, List<ErrorWarning> warns) throws Err {
for (ArrayList<Func> entry : funcs.values()) for (Func ff : entry) {
Context cx = new Context(this, warns);
cx.rootfunbody = ff;
for (Decl d : ff.decls) for (ExprHasName n : d.names) cx.put(n.label, n);
Expr newBody = cx.check(ff.getBody());
if (ff.isPred)
newBody = newBody.resolve_as_formula(warns);
else
newBody = newBody.resolve_as_set(warns);
errors = errors.make(newBody.errors);
if (!newBody.errors.isEmpty())
continue;
try {
ff.setBody(newBody);
} catch (Err er) {
errors = errors.make(er);
continue;
}
if (warns != null && ff.returnDecl.type().hasTuple() && newBody.type().hasTuple() && !newBody.type().intersects(ff.returnDecl.type()))
warns.add(new ErrorWarning(newBody.span(), "Function return value is disjoint from its return type.\n" + "Function body has type " + newBody.type() + "\n" + "but the return type is " + ff.returnDecl.type()));
// else if (warns!=null && Version.experimental &&
// !newBody.type.isSubtypeOf(ff.returnDecl.type))
// warns.add(new ErrorWarning(newBody.span(),
// "Function may return a tuple not in its declared return
// type.\n"
// +"The Alloy Analyzer's analysis may be unsound\n"
// +"if it returns a tuple outside its declared return type.\n"
// +"Function body has type "+newBody.type+"\nbut the return
// type is "+ff.returnDecl.type));
rep.typecheck(ff.toString() + ", BODY:" + newBody.type() + "\n");
}
return errors;
}
use of edu.mit.csail.sdg.alloy4.ErrorWarning in project org.alloytools.alloy by AlloyTools.
the class ExprBinary method resolve.
// ============================================================================================================//
/**
* {@inheritDoc}
*/
@Override
public Expr resolve(Type p, Collection<ErrorWarning> warns) {
if (errors.size() > 0)
return this;
ErrorWarning w = null;
Type a = left.type, b = right.type;
switch(op) {
case MUL:
case DIV:
case REM:
case LT:
case LTE:
case GT:
case GTE:
case SHL:
case SHR:
case SHA:
case NOT_LTE:
case NOT_GTE:
case NOT_LT:
case NOT_GT:
{
a = (b = Type.smallIntType());
break;
}
case AND:
case OR:
case IFF:
case IMPLIES:
{
a = (b = Type.FORMULA);
break;
}
case EQUALS:
case NOT_EQUALS:
{
p = a.intersect(b);
if (p.hasTuple()) {
a = p;
b = p;
} else {
a = a.pickCommonArity(b);
b = b.pickCommonArity(a);
}
// } else
if (warns == null) {
break;
} else if (left.type.hasTuple() && right.type.hasTuple() && !(left.type.intersects(right.type))) {
w = warn("== is redundant, because the left and right expressions are always disjoint.");
} else if (left.isSame(right)) {
w = warn("== is redundant, because the left and right expressions always have the same value.");
}
break;
}
case IN:
case NOT_IN:
{
a = a.pickCommonArity(b);
b = b.intersect(a);
if (warns == null)
break;
if (left.type.hasNoTuple() && right.type.hasNoTuple())
w = warn("Subset operator is redundant, because both subexpressions are always empty.");
else if (left.type.hasNoTuple())
w = warn("Subset operator is redundant, because the left subexpression is always empty.");
else if (right.type.hasNoTuple())
w = warn("Subset operator is redundant, because the right subexpression is always empty.");
else if (b.hasNoTuple())
w = warn("Subset operator is redundant, because the left and right subexpressions are always disjoint.");
else if (left.isSame(right))
w = warn("Subset operator is redundant, because the left and right expressions always have the same value.");
break;
}
case INTERSECT:
{
a = a.intersect(p);
b = b.intersect(p);
if (warns != null && type.hasNoTuple())
w = warn("& is irrelevant because the two subexpressions are always disjoint.");
break;
}
case IPLUS:
case IMINUS:
{
a = Type.smallIntType();
b = Type.smallIntType();
break;
}
case PLUSPLUS:
case PLUS:
{
a = a.intersect(p);
b = b.intersect(p);
// b=Type.makeInt(b); }
if (warns == null)
break;
if (a == EMPTY && b == EMPTY)
w = warn(this + " is irrelevant since both subexpressions are redundant.", p);
else if (a == EMPTY)
w = warn(this + " is irrelevant since the left subexpression is redundant.", p);
else if (b == EMPTY || (op == Op.PLUSPLUS && !right.type.canOverride(left.type)))
w = warn(this + " is irrelevant since the right subexpression is redundant.", p);
break;
}
case MINUS:
{
a = p;
b = p.intersect(b);
// } else
if (warns != null && (type.hasNoTuple() || b.hasNoTuple())) {
w = warn("- is irrelevant since the right expression is redundant.", p);
}
break;
}
case JOIN:
{
if (warns != null && type.hasNoTuple())
w = warn("The join operation here always yields an empty set.");
a = (b = EMPTY);
for (ProductType aa : left.type) for (ProductType bb : right.type) if (p.hasArity(aa.arity() + bb.arity() - 2)) {
PrimSig j = aa.get(aa.arity() - 1).intersect(bb.get(0));
if (j != Sig.NONE)
for (ProductType cc : p.intersect(aa.join(bb))) if (!cc.isEmpty()) {
List<PrimSig> v = new ArrayList<PrimSig>(cc.arity() + 1);
for (int i = 0; i < cc.arity(); i++) v.add(cc.get(i));
v.add(aa.arity() - 1, j);
a = a.merge(Type.make(v, 0, aa.arity()));
b = b.merge(Type.make(v, aa.arity() - 1, v.size()));
}
}
if (a == EMPTY || b == EMPTY) {
// Continue the best we can; we
// should have issued a
// relevance warning elsewhere
// already.
a = (b = EMPTY);
for (ProductType aa : left.type) for (ProductType bb : right.type) if (p.hasArity(aa.arity() + bb.arity() - 2) && aa.get(aa.arity() - 1).intersects(bb.get(0))) {
a = a.merge(aa);
b = b.merge(bb);
}
}
if (a == EMPTY || b == EMPTY) {
// Continue the best we can; we
// should have issued a
// relevance warning elsewhere
// already.
a = (b = EMPTY);
for (ProductType aa : left.type) for (ProductType bb : right.type) if (p.hasArity(aa.arity() + bb.arity() - 2)) {
a = a.merge(aa);
b = b.merge(bb);
}
}
break;
}
case DOMAIN:
{
// leftType such that r1<:r2 in parentType}
if (warns != null && type.hasNoTuple())
w = warn("<: is irrelevant because the result is always empty.");
Type leftType = EMPTY, rightType = EMPTY;
for (ProductType aa : a) if (aa.arity() == 1)
for (ProductType bb : b) if (p.hasArity(bb.arity()))
for (ProductType cc : p.intersect(bb.columnRestrict(aa.get(0), 0))) if (!cc.isEmpty()) {
leftType = leftType.merge(cc, 0, 1);
rightType = rightType.merge(cc);
}
if (leftType == EMPTY || rightType == EMPTY) {
// We try to
// proceed the
// best we can
leftType = a.extract(1);
rightType = b.pickCommonArity(p);
}
a = leftType;
b = rightType;
break;
}
case RANGE:
{
// leftType such that r1:>r2 in parentType}
if (warns != null && type.hasNoTuple())
w = warn(":> is irrelevant because the result is always empty.");
Type leftType = EMPTY, rightType = EMPTY;
for (ProductType bb : b) if (bb.arity() == 1)
for (ProductType aa : a) if (p.hasArity(aa.arity()))
for (ProductType cc : p.intersect(aa.columnRestrict(bb.get(0), aa.arity() - 1))) if (!cc.isEmpty()) {
leftType = leftType.merge(cc);
rightType = rightType.merge(cc, cc.arity() - 1, cc.arity());
}
if (leftType == EMPTY || rightType == EMPTY) {
// We try to
// proceed the
// best we can
leftType = a.pickCommonArity(p);
rightType = b.extract(1);
}
a = leftType;
b = rightType;
break;
}
default:
{
// leftType such that r1->r2 in parentType}
if (warns == null) {
// do nothing
} else if (a.hasTuple()) {
if (b.hasNoTuple())
w = warn("The left expression of -> is irrelevant because the right expression is always empty.");
} else {
if (b.hasTuple())
w = warn("The right expression of -> is irrelevant because the left expression is always empty.");
}
Type leftType = EMPTY, rightType = EMPTY;
for (ProductType aa : a) if (!aa.isEmpty())
for (ProductType bb : b) if (!bb.isEmpty() && p.hasArity(aa.arity() + bb.arity()))
for (ProductType cc : p.intersect(aa.product(bb))) if (!cc.isEmpty()) {
leftType = leftType.merge(cc, 0, aa.arity());
rightType = rightType.merge(cc, aa.arity(), cc.arity());
}
// relevance warning already.
if (leftType == EMPTY || rightType == EMPTY) {
leftType = a;
rightType = b;
}
a = leftType;
b = rightType;
}
}
Expr left = this.left.resolve(a, warns);
Expr right = this.right.resolve(b, warns);
if (w != null)
warns.add(w);
return (left == this.left && right == this.right) ? this : op.make(pos, closingBracket, left, right);
}
use of edu.mit.csail.sdg.alloy4.ErrorWarning in project org.alloytools.alloy by AlloyTools.
the class ExprChoice method resolveHelper.
// ============================================================================================================//
/**
* Resolve the list of choices, or return an ExprBad object containing the list
* of unresolvable ambiguities.
*/
private Expr resolveHelper(boolean firstPass, final Type t, List<Expr> choices, List<String> reasons, Collection<ErrorWarning> warns) {
List<Expr> ch = new ArrayList<Expr>(choices.size());
List<String> re = new ArrayList<String>(choices.size());
// We first prefer exact matches
for (int i = 0; i < choices.size(); i++) {
Type tt = choices.get(i).type;
if (/* [AM](t.is_int() && tt.is_int()) || */
(t.is_bool && tt.is_bool) || t.intersects(tt)) {
ch.add(choices.get(i));
re.add(reasons.get(i));
}
}
// If none, we try any legal matches
if (ch.size() == 0) {
for (int i = 0; i < choices.size(); i++) if (choices.get(i).type.hasCommonArity(t)) {
ch.add(choices.get(i));
re.add(reasons.get(i));
}
}
// If too many, then keep the choices with the smallest weight
if (ch.size() > 1) {
List<Expr> ch2 = new ArrayList<Expr>(ch.size());
List<String> re2 = new ArrayList<String>(ch.size());
long w = 0;
for (int i = 0; i < ch.size(); i++) {
Expr c = ch.get(i);
String r = re.get(i);
if (ch2.size() > 0 && c.weight > w)
continue;
else if (ch2.size() == 0 || c.weight < w) {
ch2.clear();
re2.clear();
w = c.weight;
}
ch2.add(c);
re2.add(r);
}
ch = ch2;
re = re2;
// resolve them all and try again
if (firstPass && ch.size() > 1) {
ch2 = new ArrayList<Expr>(ch.size());
for (Expr c : ch) ch2.add(c.resolve(t, null));
return resolveHelper(false, t, ch2, re, warns);
}
}
// If we are down to exactly 1 match, return it
if (ch.size() == 1)
return ch.get(0).resolve(t, warns);
// emptyset-of-the-same-arity, then just return emptyset
none: while (ch.size() > 1) {
int arity = -1;
for (Expr c : ch) {
if (c.type.is_bool || c.type.is_int() || c.type.hasTuple())
break none;
int a = c.type.arity();
if (a < 1)
break none;
if (arity < 0)
arity = a;
else if (arity != a)
break none;
}
Expr ans = Sig.NONE;
while (arity > 1) {
ans = ans.product(Sig.NONE);
arity--;
}
return ExprUnary.Op.NOOP.make(span(), ans);
}
// Otherwise, complain!
String txt;
if (ch.size() > 1) {
txt = "\nThis name is ambiguous due to multiple matches:";
} else {
txt = "\nThis name cannot be resolved; its relevant type does not intersect with any of the following candidates:";
re = reasons;
}
StringBuilder msg = new StringBuilder(txt);
for (String r : re) msg.append('\n').append(r);
return new ExprBad(pos, toString(), new ErrorType(pos, msg.toString()));
}
Aggregations