//-----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All Rights Reserved.
//
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Diagnostics.Contracts;
using Microsoft.Boogie;
namespace Microsoft.Dafny
{
public class ResolutionErrorReporter
{
public int ErrorCount = 0;
///
/// This method is virtual, because it is overridden in the VSX plug-in for Dafny.
///
public virtual void Error(IToken tok, string msg, params object[] args) {
Contract.Requires(tok != null);
Contract.Requires(msg != null);
ConsoleColor col = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("{0}({1},{2}): Error: {3}",
tok.filename, tok.line, tok.col - 1,
string.Format(msg, args));
Console.ForegroundColor = col;
ErrorCount++;
}
public void Error(Declaration d, string msg, params object[] args) {
Contract.Requires(d != null);
Contract.Requires(msg != null);
Error(d.tok, msg, args);
}
public void Error(Statement s, string msg, params object[] args) {
Contract.Requires(s != null);
Contract.Requires(msg != null);
Error(s.Tok, msg, args);
}
public void Error(NonglobalVariable v, string msg, params object[] args) {
Contract.Requires(v != null);
Contract.Requires(msg != null);
Error(v.tok, msg, args);
}
public void Error(Expression e, string msg, params object[] args) {
Contract.Requires(e != null);
Contract.Requires(msg != null);
Error(e.tok, msg, args);
}
public void Warning(IToken tok, string msg, params object[] args) {
Contract.Requires(tok != null);
Contract.Requires(msg != null);
ConsoleColor col = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("{0}({1},{2}): Warning: {3}",
tok.filename, tok.line, tok.col - 1,
string.Format(msg, args));
Console.ForegroundColor = col;
}
}
public class Resolver : ResolutionErrorReporter
{
readonly BuiltIns builtIns;
//Dictionary/*!*/ classes; // can map to AmbiguousTopLevelDecl
//Dictionary importedNames; // the imported modules, as a map.
ModuleSignature moduleInfo = null;
class AmbiguousTopLevelDecl : TopLevelDecl // only used with "classes"
{
readonly TopLevelDecl A;
readonly TopLevelDecl B;
public AmbiguousTopLevelDecl(ModuleDefinition m, TopLevelDecl a, TopLevelDecl b)
: base(a.tok, a.Name + "/" + b.Name, m, new List(), null) {
A = a;
B = b;
}
public string ModuleNames() {
string nm;
if (A is AmbiguousTopLevelDecl) {
nm = ((AmbiguousTopLevelDecl)A).ModuleNames();
} else {
nm = A.Module.Name;
}
if (B is AmbiguousTopLevelDecl) {
nm += ", " + ((AmbiguousTopLevelDecl)B).ModuleNames();
} else {
nm += ", " + B.Module.Name;
}
return nm;
}
}
class AmbiguousMemberDecl : MemberDecl // only used with "classes"
{
readonly MemberDecl A;
readonly MemberDecl B;
public AmbiguousMemberDecl(ModuleDefinition m, MemberDecl a, MemberDecl b)
: base(a.tok, a.Name + "/" + b.Name, a.IsStatic, a.IsGhost, null) {
A = a;
B = b;
}
public string ModuleNames() {
string nm;
if (A is AmbiguousMemberDecl) {
nm = ((AmbiguousMemberDecl)A).ModuleNames();
} else {
nm = A.EnclosingClass.Module.Name;
}
if (B is AmbiguousMemberDecl) {
nm += ", " + ((AmbiguousMemberDecl)B).ModuleNames();
} else {
nm += ", " + B.EnclosingClass.Module.Name;
}
return nm;
}
}
//Dictionary> allDatatypeCtors;
readonly Dictionary/*!*/>/*!*/ classMembers = new Dictionary/*!*/>();
readonly Dictionary/*!*/>/*!*/ datatypeMembers = new Dictionary/*!*/>();
readonly Dictionary/*!*/>/*!*/ datatypeCtors = new Dictionary/*!*/>();
readonly Graph/*!*/ dependencies = new Graph();
private ModuleSignature systemNameInfo = null;
private bool useCompileSignatures = false;
public Resolver(Program prog) {
Contract.Requires(prog != null);
builtIns = prog.BuiltIns;
}
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(builtIns != null);
Contract.Invariant(cce.NonNullElements(dependencies));
Contract.Invariant(cce.NonNullDictionaryAndValues(classMembers) && Contract.ForAll(classMembers.Values, v => cce.NonNullDictionaryAndValues(v)));
Contract.Invariant(cce.NonNullDictionaryAndValues(datatypeCtors) && Contract.ForAll(datatypeCtors.Values, v => cce.NonNullDictionaryAndValues(v)));
}
public void ResolveProgram(Program prog) {
Contract.Requires(prog != null);
var bindings = new ModuleBindings(null);
var b = BindModuleNames(prog.DefaultModuleDef, bindings);
bindings.BindName("_module", prog.DefaultModule, b);
if (ErrorCount > 0) { return; } // if there were errors, then the implict ModuleBindings data structure invariant
// is violated, so Processing dependencies will not succeed.
ProcessDependencies(prog.DefaultModule, b, dependencies);
// check for cycles in the import graph
List cycle = dependencies.TryFindCycle();
if (cycle != null) {
var cy = Util.Comma(" -> ", cycle, m => m.Name);
Error(cycle[0], "module definition contains a cycle (note: parent modules implicitly depend on submodules): {0}", cy);
}
if (ErrorCount > 0) { return; } // give up on trying to resolve anything else
// fill in module heights
List sortedDecls = dependencies.TopologicallySortedComponents();
int h = 0;
foreach (ModuleDecl m in sortedDecls) {
m.Height = h;
if (m is LiteralModuleDecl) {
var mdef = ((LiteralModuleDecl)m).ModuleDef;
mdef.Height = h;
prog.Modules.Add(mdef);
}
h++;
}
var refinementTransformer = new RefinementTransformer(this, prog);
IRewriter rewriter = new AutoContractsRewriter();
systemNameInfo = RegisterTopLevelDecls(prog.BuiltIns.SystemModule, false);
foreach (var decl in sortedDecls) {
if (decl is LiteralModuleDecl) {
// The declaration is a literal module, so it has members and such that we need
// to resolve. First we do refinement transformation. Then we construct the signature
// of the module. This is the public, externally visible signature. Then we add in
// everything that the system defines, as well as any "import" (i.e. "opened" modules)
// directives (currently not supported, but this is where we would do it.) This signature,
// which is only used while resolving the members of the module is stored in the (basically)
// global variable moduleInfo. Then the signatures of the module members are resolved, followed
// by the bodies.
var literalDecl = (LiteralModuleDecl)decl;
var m = (literalDecl).ModuleDef;
var errorCount = ErrorCount;
rewriter.PreResolve(m);
ModuleSignature refinedSig = null;
if (m.RefinementBaseRoot != null) {
if (ResolvePath(m.RefinementBaseRoot, m.RefinementBaseName, out refinedSig)) {
if (refinedSig.ModuleDef != null) {
m.RefinementBase = refinedSig.ModuleDef;
refinementTransformer.PreResolve(m);
} else {
Error(m.RefinementBaseName[0], "module ({0}) named as refinement base is not a literal module or simple reference to a literal module", Util.Comma(".", m.RefinementBaseName, x => x.val));
}
} else {
Error(m.RefinementBaseName[0], "module ({0}) named as refinement base does not exist", Util.Comma(".", m.RefinementBaseName, x => x.val));
}
}
literalDecl.Signature = RegisterTopLevelDecls(m, true);
literalDecl.Signature.Refines = refinedSig;
var sig = literalDecl.Signature;
// set up environment
var preResolveErrorCount = ErrorCount;
useCompileSignatures = false;
ResolveModuleDefinition(m, sig);
if (ErrorCount == preResolveErrorCount) {
refinementTransformer.PostResolve(m);
// give rewriter a chance to do processing
rewriter.PostResolve(m);
}
if (ErrorCount == errorCount && !m.IsGhost) {
// compilation should only proceed if everything is good, including the signature (which preResolveErrorCount does not include);
var nw = (new Cloner()).CloneModuleDefinition(m, m.CompileName + "_Compile");
var compileSig = RegisterTopLevelDecls(nw, true);
compileSig.Refines = refinedSig;
sig.CompileSignature = compileSig;
useCompileSignatures = true;
ResolveModuleDefinition(nw, compileSig);
prog.CompileModules.Add(nw);
}
} else if (decl is AliasModuleDecl) {
var alias = (AliasModuleDecl)decl;
// resolve the path
ModuleSignature p;
if (ResolvePath(alias.Root, alias.Path, out p)) {
alias.Signature = p;
} else {
alias.Signature = new ModuleSignature(); // there was an error, give it a valid but empty signature
}
} else if (decl is AbstractModuleDecl) {
var abs = (AbstractModuleDecl)decl;
ModuleSignature p;
if (ResolvePath(abs.Root, abs.Path, out p)) {
abs.Signature = MakeAbstractSignature(p, abs.FullCompileName, abs.Height, prog.Modules);
abs.OriginalSignature = p;
ModuleSignature compileSig;
if (abs.CompilePath != null) {
if (ResolvePath(abs.CompileRoot, abs.CompilePath, out compileSig)) {
if (refinementTransformer.CheckIsRefinement(compileSig, p)) {
abs.Signature.CompileSignature = compileSig;
} else {
Error(abs.CompilePath[0],
"module " + Util.Comma(".", abs.CompilePath, x => x.val) + " must be a refinement of " + Util.Comma(".", abs.Path, x => x.val));
}
abs.Signature.IsGhost = compileSig.IsGhost;
// always keep the ghost information, to supress a spurious error message when the compile module isn't actually a refinement
}
}
} else {
abs.Signature = new ModuleSignature(); // there was an error, give it a valid but empty signature
}
} else { Contract.Assert(false); }
Contract.Assert(decl.Signature != null);
}
// compute IsRecursive bit for mutually recursive functions
foreach (ModuleDefinition m in prog.Modules) {
foreach (var fn in ModuleDefinition.AllFunctions(m.TopLevelDecls)) {
if (!fn.IsRecursive) { // note, self-recursion has already been determined
int n = m.CallGraph.GetSCCSize(fn);
if (2 <= n) {
// the function is mutually recursive (note, the SCC does not determine self recursion)
fn.IsRecursive = true;
}
}
}
}
}
private void ResolveModuleDefinition(ModuleDefinition m, ModuleSignature sig) {
moduleInfo = MergeSignature(sig, systemNameInfo);
// resolve
var datatypeDependencies = new Graph();
int prevErrorCount = ErrorCount;
ResolveTopLevelDecls_Signatures(m, m.TopLevelDecls, datatypeDependencies);
if (ErrorCount == prevErrorCount)
ResolveTopLevelDecls_Meat(m.TopLevelDecls, datatypeDependencies);
}
public class ModuleBindings
{
private ModuleBindings parent;
private Dictionary modules;
private Dictionary bindings;
public ModuleBindings(ModuleBindings p) {
parent = p;
modules = new Dictionary();
bindings = new Dictionary();
}
public bool BindName(string name, ModuleDecl subModule, ModuleBindings b) {
if (modules.ContainsKey(name)) {
return false;
} else {
modules.Add(name, subModule);
bindings.Add(name, b);
return true;
}
}
public bool TryLookup(IToken name, out ModuleDecl m) {
Contract.Requires(name != null);
if (modules.TryGetValue(name.val, out m)) {
return true;
} else if (parent != null) {
return parent.TryLookup(name, out m);
} else return false;
}
public bool TryLookupIgnore(IToken name, out ModuleDecl m, ModuleDecl ignore) {
Contract.Requires(name != null);
if (modules.TryGetValue(name.val, out m) && m != ignore) {
return true;
} else if (parent != null) {
return parent.TryLookup(name, out m);
} else return false;
}
public IEnumerable ModuleList {
get { return modules.Values; }
}
public ModuleBindings SubBindings(string name) {
ModuleBindings v = null;
bindings.TryGetValue(name, out v);
return v;
}
}
private ModuleBindings BindModuleNames(ModuleDefinition moduleDecl, ModuleBindings parentBindings) {
var bindings = new ModuleBindings(parentBindings);
foreach (var tld in moduleDecl.TopLevelDecls) {
if (tld is LiteralModuleDecl) {
var subdecl = (LiteralModuleDecl)tld;
var subBindings = BindModuleNames(subdecl.ModuleDef, bindings);
if (!bindings.BindName(subdecl.Name, subdecl, subBindings)) {
Error(subdecl.tok, "Duplicate module name: {0}", subdecl.Name);
}
} else if (tld is AbstractModuleDecl) {
var subdecl = (AbstractModuleDecl)tld;
if (!bindings.BindName(subdecl.Name, subdecl, null)) {
Error(subdecl.tok, "Duplicate module name: {0}", subdecl.Name);
}
} else if (tld is AliasModuleDecl) {
var subdecl = (AliasModuleDecl)tld;
if (!bindings.BindName(subdecl.Name, subdecl, null)) {
Error(subdecl.tok, "Duplicate module name: {0}", subdecl.Name);
}
}
}
return bindings;
}
private void ProcessDependenciesDefinition(ModuleDecl decl, ModuleDefinition m, ModuleBindings bindings, Graph dependencies) {
if (m.RefinementBaseName != null) {
ModuleDecl other;
if (!bindings.TryLookup(m.RefinementBaseName[0], out other)) {
Error(m, "module {0} named as refinement base does not exist", m.RefinementBaseName[0].val);
} else if (other is LiteralModuleDecl && ((LiteralModuleDecl)other).ModuleDef == m) {
Error(m, "module cannot refine itself: {0}", m.RefinementBaseName[0].val);
} else {
Contract.Assert(other != null); // follows from postcondition of TryGetValue
dependencies.AddEdge(decl, other);
m.RefinementBaseRoot = other;
}
}
foreach (var toplevel in m.TopLevelDecls) {
if (toplevel is ModuleDecl) {
var d = (ModuleDecl)toplevel;
dependencies.AddEdge(decl, d);
var subbindings = bindings.SubBindings(d.Name);
ProcessDependencies(d, subbindings ?? bindings, dependencies);
}
}
}
private void ProcessDependencies(ModuleDecl moduleDecl, ModuleBindings bindings, Graph dependencies) {
dependencies.AddVertex(moduleDecl);
if (moduleDecl is LiteralModuleDecl) {
ProcessDependenciesDefinition(moduleDecl, ((LiteralModuleDecl)moduleDecl).ModuleDef, bindings, dependencies);
} else if (moduleDecl is AliasModuleDecl) {
var alias = moduleDecl as AliasModuleDecl;
ModuleDecl root;
if (!bindings.TryLookupIgnore(alias.Path[0], out root, alias))
Error(alias.tok, ModuleNotFoundErrorMessage(0, alias.Path));
else {
dependencies.AddEdge(moduleDecl, root);
alias.Root = root;
}
} else if (moduleDecl is AbstractModuleDecl) {
var abs = moduleDecl as AbstractModuleDecl;
ModuleDecl root;
if (!bindings.TryLookup(abs.Path[0], out root))
Error(abs.tok, ModuleNotFoundErrorMessage(0, abs.Path));
else {
dependencies.AddEdge(moduleDecl, root);
abs.Root = root;
}
if (abs.CompilePath != null) {
if (!bindings.TryLookup(abs.CompilePath[0], out root))
Error(abs.tok, ModuleNotFoundErrorMessage(0, abs.CompilePath));
else {
dependencies.AddEdge(moduleDecl, root);
abs.CompileRoot = root;
}
}
}
}
private string ModuleNotFoundErrorMessage(int i, List path) {
return "module " + path[i].val + " does not exist" +
(1 < path.Count ? " (position " + i.ToString() + " in path " + Util.Comma(".", path, x => x.val) + ")" : "");
}
public static ModuleSignature MergeSignature(ModuleSignature m, ModuleSignature system) {
var info = new ModuleSignature();
// add the system-declared information, among which we know there are no duplicates
foreach (var kv in system.TopLevels) {
info.TopLevels.Add(kv.Key, kv.Value);
}
foreach (var kv in system.Ctors) {
info.Ctors.Add(kv.Key, kv.Value);
}
// add for the module itself
foreach (var kv in m.TopLevels) {
info.TopLevels[kv.Key] = kv.Value;
}
foreach (var kv in m.Ctors) {
info.Ctors[kv.Key] = kv.Value;
}
foreach (var kv in m.StaticMembers) {
info.StaticMembers[kv.Key] = kv.Value;
}
info.IsGhost = m.IsGhost;
return info;
}
ModuleSignature RegisterTopLevelDecls(ModuleDefinition moduleDef, bool useImports) {
Contract.Requires(moduleDef != null);
var sig = new ModuleSignature();
sig.ModuleDef = moduleDef;
sig.IsGhost = moduleDef.IsGhost;
List declarations = moduleDef.TopLevelDecls;
if (useImports) {
// First go through and add anything from the opened imports
foreach (var im in declarations) {
if (im is ModuleDecl && ((ModuleDecl)im).Opened) {
var s = ((ModuleDecl)im).Signature;
// classes:
foreach (var kv in s.TopLevels) {
TopLevelDecl d;
if (sig.TopLevels.TryGetValue(kv.Key, out d)) {
sig.TopLevels[kv.Key] = new AmbiguousTopLevelDecl(moduleDef, d, kv.Value);
} else {
sig.TopLevels.Add(kv.Key, kv.Value);
}
}
// constructors:
foreach (var kv in s.Ctors) {
Tuple pair;
if (sig.Ctors.TryGetValue(kv.Key, out pair)) {
// mark it as a duplicate
sig.Ctors[kv.Key] = new Tuple(pair.Item1, true);
} else {
// add new
sig.Ctors.Add(kv.Key, kv.Value);
}
}
// static members:
foreach (var kv in s.StaticMembers) {
MemberDecl md;
if (sig.StaticMembers.TryGetValue(kv.Key, out md)) {
sig.StaticMembers[kv.Key] = new AmbiguousMemberDecl(moduleDef, md, kv.Value);
} else {
// add new
sig.StaticMembers.Add(kv.Key, kv.Value);
}
}
}
}
}
// This is solely used to detect duplicates amongst the various e
Dictionary toplevels = new Dictionary();
// Now add the things present
foreach (TopLevelDecl d in declarations) {
Contract.Assert(d != null);
// register the class/datatype/module name
if (toplevels.ContainsKey(d.Name)) {
Error(d, "Duplicate name of top-level declaration: {0}", d.Name);
} else {
toplevels[d.Name] = d;
sig.TopLevels[d.Name] = d;
}
if (d is ModuleDecl) {
// nothing to do
} else if (d is ArbitraryTypeDecl) {
// nothing more to register
} else if (d is ClassDecl) {
ClassDecl cl = (ClassDecl)d;
// register the names of the class members
Dictionary members = new Dictionary();
classMembers.Add(cl, members);
bool hasConstructor = false;
foreach (MemberDecl m in cl.Members) {
if (members.ContainsKey(m.Name)) {
Error(m, "Duplicate member name: {0}", m.Name);
} else {
members.Add(m.Name, m);
}
if (m is Constructor) {
hasConstructor = true;
}
}
cl.HasConstructor = hasConstructor;
if (cl.IsDefaultClass) {
foreach (MemberDecl m in cl.Members) {
if (m.IsStatic && (m is Function || m is Method)) {
sig.StaticMembers[m.Name] = m;
}
}
}
} else {
DatatypeDecl dt = (DatatypeDecl)d;
// register the names of the constructors
Dictionary ctors = new Dictionary();
datatypeCtors.Add(dt, ctors);
// ... and of the other members
Dictionary members = new Dictionary();
datatypeMembers.Add(dt, members);
foreach (DatatypeCtor ctor in dt.Ctors) {
if (ctor.Name.EndsWith("?")) {
Error(ctor, "a datatype constructor name is not allowed to end with '?'");
} else if (ctors.ContainsKey(ctor.Name)) {
Error(ctor, "Duplicate datatype constructor name: {0}", ctor.Name);
} else {
ctors.Add(ctor.Name, ctor);
// create and add the query "method" (field, really)
string queryName = ctor.Name + "?";
var query = new SpecialField(ctor.tok, queryName, "is_" + ctor.CompileName, "", "", false, false, Type.Bool, null);
query.EnclosingClass = dt; // resolve here
members.Add(queryName, query);
ctor.QueryField = query;
// also register the constructor name globally
Tuple pair;
if (sig.Ctors.TryGetValue(ctor.Name, out pair)) {
// mark it as a duplicate
sig.Ctors[ctor.Name] = new Tuple(pair.Item1, true);
} else {
// add new
sig.Ctors.Add(ctor.Name, new Tuple(ctor, false));
}
}
}
// add deconstructors now (that is, after the query methods have been added)
foreach (DatatypeCtor ctor in dt.Ctors) {
foreach (var formal in ctor.Formals) {
SpecialField dtor = null;
if (formal.HasName) {
if (members.ContainsKey(formal.Name)) {
Error(ctor, "Name of deconstructor is used by another member of the datatype: {0}", formal.Name);
} else {
dtor = new DatatypeDestructor(formal.tok, ctor, formal, formal.Name, "dtor_" + formal.Name, "", "", formal.IsGhost, formal.Type, null);
dtor.EnclosingClass = dt; // resolve here
members.Add(formal.Name, dtor);
}
}
ctor.Destructors.Add(dtor);
}
}
}
}
return sig;
}
private ModuleSignature MakeAbstractSignature(ModuleSignature p, string Name, int Height, List mods) {
var mod = new ModuleDefinition(Token.NoToken, Name + ".Abs", true, true, null, null, false);
mod.Height = Height;
foreach (var kv in p.TopLevels) {
mod.TopLevelDecls.Add(CloneDeclaration(kv.Value, mod, mods, Name));
}
var sig = RegisterTopLevelDecls(mod, false);
sig.Refines = p.Refines;
sig.CompileSignature = p;
sig.IsGhost = p.IsGhost;
mods.Add(mod);
ResolveModuleDefinition(mod, sig);
return sig;
}
TopLevelDecl CloneDeclaration(TopLevelDecl d, ModuleDefinition m, List mods, string Name) {
Contract.Requires(d != null);
Contract.Requires(m != null);
if (d is ArbitraryTypeDecl) {
var dd = (ArbitraryTypeDecl)d;
return new ArbitraryTypeDecl(dd.tok, dd.Name, m, dd.EqualitySupport, null);
} else if (d is IndDatatypeDecl) {
var dd = (IndDatatypeDecl)d;
var tps = dd.TypeArgs.ConvertAll(CloneTypeParam);
var ctors = dd.Ctors.ConvertAll(CloneCtor);
var dt = new IndDatatypeDecl(dd.tok, dd.Name, m, tps, ctors, null);
return dt;
} else if (d is CoDatatypeDecl) {
var dd = (CoDatatypeDecl)d;
var tps = dd.TypeArgs.ConvertAll(CloneTypeParam);
var ctors = dd.Ctors.ConvertAll(CloneCtor);
var dt = new CoDatatypeDecl(dd.tok, dd.Name, m, tps, ctors, null);
return dt;
} else if (d is ClassDecl) {
var dd = (ClassDecl)d;
var tps = dd.TypeArgs.ConvertAll(CloneTypeParam);
var mm = dd.Members.ConvertAll(CloneMember);
if (dd is DefaultClassDecl) {
return new DefaultClassDecl(m, mm);
} else return new ClassDecl(dd.tok, dd.Name, m, tps, mm, null);
} else if (d is ModuleDecl) {
if (d is LiteralModuleDecl) {
return new LiteralModuleDecl(((LiteralModuleDecl)d).ModuleDef, m);
} else if (d is AliasModuleDecl) {
var a = (AliasModuleDecl)d;
var alias = new AliasModuleDecl(a.Path, a.tok, m, a.Opened);
alias.ModuleReference = a.ModuleReference;
alias.Signature = a.Signature;
return alias;
} else if (d is AbstractModuleDecl) {
var abs = (AbstractModuleDecl)d;
var sig = MakeAbstractSignature(abs.OriginalSignature, Name + "." + abs.Name, abs.Height, mods);
var a = new AbstractModuleDecl(abs.Path, abs.tok, m, abs.CompilePath, abs.Opened);
a.Signature = sig;
a.OriginalSignature = abs.OriginalSignature;
return a;
} else {
Contract.Assert(false); // unexpected declaration
return null; // to please compiler
}
} else {
Contract.Assert(false); // unexpected declaration
return null; // to please compiler
}
}
MemberDecl CloneMember(MemberDecl member) {
if (member is Field) {
var f = (Field)member;
return new Field(f.tok, f.Name, f.IsGhost, f.IsMutable, CloneType(f.Type), null);
} else if (member is Function) {
var f = (Function)member;
return CloneFunction(f.tok, f, f.IsGhost);
} else {
var m = (Method)member;
return CloneMethod(m);
}
}
TypeParameter CloneTypeParam(TypeParameter tp) {
return new TypeParameter(tp.tok, tp.Name);
}
DatatypeCtor CloneCtor(DatatypeCtor ct) {
return new DatatypeCtor(ct.tok, ct.Name, ct.Formals.ConvertAll(CloneFormal), null);
}
Formal CloneFormal(Formal formal) {
return new Formal(formal.tok, formal.Name, CloneType(formal.Type), formal.InParam, formal.IsGhost);
}
Type CloneType(Type t) {
if (t is BasicType) {
return t;
} else if (t is SetType) {
var tt = (SetType)t;
return new SetType(CloneType(tt.Arg));
} else if (t is SeqType) {
var tt = (SeqType)t;
return new SeqType(CloneType(tt.Arg));
} else if (t is MultiSetType) {
var tt = (MultiSetType)t;
return new MultiSetType(CloneType(tt.Arg));
} else if (t is MapType) {
var tt = (MapType)t;
return new MapType(CloneType(tt.Domain), CloneType(tt.Range));
} else if (t is UserDefinedType) {
var tt = (UserDefinedType)t;
return new UserDefinedType(tt.tok, tt.Name, tt.TypeArgs.ConvertAll(CloneType), tt.Path.ConvertAll(x => x));
} else if (t is InferredTypeProxy) {
return new InferredTypeProxy();
} else {
Contract.Assert(false); // unexpected type (e.g., no other type proxies are expected at this time)
return null; // to please compiler
}
}
Function CloneFunction(IToken tok, Function f, bool isGhost) {
var tps = f.TypeArgs.ConvertAll(CloneTypeParam);
var formals = f.Formals.ConvertAll(CloneFormal);
var req = f.Req.ConvertAll(CloneExpr);
var reads = f.Reads.ConvertAll(CloneFrameExpr);
var decreases = CloneSpecExpr(f.Decreases);
var ens = f.Ens.ConvertAll(CloneExpr);
Expression body = CloneExpr(f.Body);
if (f is Predicate) {
return new Predicate(tok, f.Name, f.IsStatic, isGhost, tps, f.OpenParen, formals,
req, reads, ens, decreases, body, Predicate.BodyOriginKind.OriginalOrInherited, null, false);
} else if (f is CoPredicate) {
return new CoPredicate(tok, f.Name, f.IsStatic, tps, f.OpenParen, formals,
req, reads, ens, body, null, false);
} else {
return new Function(tok, f.Name, f.IsStatic, isGhost, tps, f.OpenParen, formals, CloneType(f.ResultType),
req, reads, ens, decreases, body, null, false);
}
}
Method CloneMethod(Method m) {
Contract.Requires(m != null);
var tps = m.TypeArgs.ConvertAll(CloneTypeParam);
var ins = m.Ins.ConvertAll(CloneFormal);
var req = m.Req.ConvertAll(CloneMayBeFreeExpr);
var mod = CloneSpecFrameExpr(m.Mod);
var decreases = CloneSpecExpr(m.Decreases);
var ens = m.Ens.ConvertAll(CloneMayBeFreeExpr);
if (m is Constructor) {
return new Constructor(m.tok, m.Name, tps, ins,
req, mod, ens, decreases, null, null, false);
} else {
return new Method(m.tok, m.Name, m.IsStatic, m.IsGhost, tps, ins, m.Outs.ConvertAll(CloneFormal),
req, mod, ens, decreases, null, null, false);
}
}
Specification CloneSpecExpr(Specification spec) {
var ee = spec.Expressions == null ? null : spec.Expressions.ConvertAll(CloneExpr);
return new Specification(ee, null);
}
Specification CloneSpecFrameExpr(Specification frame) {
var ee = frame.Expressions == null ? null : frame.Expressions.ConvertAll(CloneFrameExpr);
return new Specification(ee, null);
}
FrameExpression CloneFrameExpr(FrameExpression frame) {
return new FrameExpression(frame.tok, CloneExpr(frame.E), frame.FieldName);
}
MaybeFreeExpression CloneMayBeFreeExpr(MaybeFreeExpression expr) {
return new MaybeFreeExpression(CloneExpr(expr.E), expr.IsFree);
}
BoundVar CloneBoundVar(BoundVar bv) {
return new BoundVar(bv.tok, bv.Name, CloneType(bv.Type));
}
Expression CloneExpr(Expression expr) {
if (expr == null) {
return null;
} else if (expr is LiteralExpr) {
var e = (LiteralExpr)expr;
if (e.Value == null) {
return new LiteralExpr(e.tok);
} else if (e.Value is bool) {
return new LiteralExpr(e.tok, (bool)e.Value);
} else {
return new LiteralExpr(e.tok, (BigInteger)e.Value);
}
} else if (expr is ThisExpr) {
if (expr is ImplicitThisExpr) {
return new ImplicitThisExpr(expr.tok);
} else {
return new ThisExpr(expr.tok);
}
} else if (expr is IdentifierExpr) {
var e = (IdentifierExpr)expr;
return new IdentifierExpr(e.tok, e.Name);
} else if (expr is DatatypeValue) {
var e = (DatatypeValue)expr;
return new DatatypeValue(e.tok, e.DatatypeName, e.MemberName, e.Arguments.ConvertAll(CloneExpr));
} else if (expr is DisplayExpression) {
DisplayExpression e = (DisplayExpression)expr;
if (expr is SetDisplayExpr) {
return new SetDisplayExpr(e.tok, e.Elements.ConvertAll(CloneExpr));
} else if (expr is MultiSetDisplayExpr) {
return new MultiSetDisplayExpr(e.tok, e.Elements.ConvertAll(CloneExpr));
} else {
Contract.Assert(expr is SeqDisplayExpr);
return new SeqDisplayExpr(e.tok, e.Elements.ConvertAll(CloneExpr));
}
} else if (expr is MapDisplayExpr) {
MapDisplayExpr e = (MapDisplayExpr)expr;
List pp = new List();
foreach (ExpressionPair p in e.Elements) {
pp.Add(new ExpressionPair(CloneExpr(p.A), CloneExpr(p.B)));
}
return new MapDisplayExpr(expr.tok, pp);
} else if (expr is ExprDotName) {
var e = (ExprDotName)expr;
return new ExprDotName(e.tok, CloneExpr(e.Obj), e.SuffixName);
} else if (expr is FieldSelectExpr) {
var e = (FieldSelectExpr)expr;
return new FieldSelectExpr(e.tok, CloneExpr(e.Obj), e.FieldName);
} else if (expr is SeqSelectExpr) {
var e = (SeqSelectExpr)expr;
return new SeqSelectExpr(e.tok, e.SelectOne, CloneExpr(e.Seq), CloneExpr(e.E0), CloneExpr(e.E1));
} else if (expr is MultiSelectExpr) {
var e = (MultiSelectExpr)expr;
return new MultiSelectExpr(e.tok, CloneExpr(e.Array), e.Indices.ConvertAll(CloneExpr));
} else if (expr is SeqUpdateExpr) {
var e = (SeqUpdateExpr)expr;
return new SeqUpdateExpr(e.tok, CloneExpr(e.Seq), CloneExpr(e.Index), CloneExpr(e.Value));
} else if (expr is FunctionCallExpr) {
var e = (FunctionCallExpr)expr;
return new FunctionCallExpr(e.tok, e.Name, CloneExpr(e.Receiver), e.OpenParen == null ? null : (e.OpenParen), e.Args.ConvertAll(CloneExpr));
} else if (expr is OldExpr) {
var e = (OldExpr)expr;
return new OldExpr(e.tok, CloneExpr(e.E));
} else if (expr is MultiSetFormingExpr) {
var e = (MultiSetFormingExpr)expr;
return new MultiSetFormingExpr(e.tok, CloneExpr(e.E));
} else if (expr is FreshExpr) {
var e = (FreshExpr)expr;
return new FreshExpr(e.tok, CloneExpr(e.E));
} else if (expr is UnaryExpr) {
var e = (UnaryExpr)expr;
return new UnaryExpr(e.tok, e.Op, CloneExpr(e.E));
} else if (expr is BinaryExpr) {
var e = (BinaryExpr)expr;
return new BinaryExpr(e.tok, e.Op, CloneExpr(e.E0), CloneExpr(e.E1));
} else if (expr is ChainingExpression) {
var e = (ChainingExpression)expr;
return CloneExpr(e.E); // just clone the desugaring, since it's already available
} else if (expr is LetExpr) {
var e = (LetExpr)expr;
return new LetExpr(e.tok, e.Vars.ConvertAll(CloneBoundVar), e.RHSs.ConvertAll(CloneExpr), CloneExpr(e.Body));
} else if (expr is ComprehensionExpr) {
var e = (ComprehensionExpr)expr;
var tk = e.tok;
var bvs = e.BoundVars.ConvertAll(CloneBoundVar);
var range = CloneExpr(e.Range);
var term = CloneExpr(e.Term);
if (e is ForallExpr) {
return new ForallExpr(tk, bvs, range, term, null);
} else if (e is ExistsExpr) {
return new ExistsExpr(tk, bvs, range, term, null);
} else if (e is MapComprehension) {
return new MapComprehension(tk, bvs, range, term);
} else {
Contract.Assert(e is SetComprehension);
return new SetComprehension(tk, bvs, range, term);
}
} else if (expr is WildcardExpr) {
return new WildcardExpr(expr.tok);
} else if (expr is PredicateExpr) {
var e = (PredicateExpr)expr;
if (e is AssertExpr) {
return new AssertExpr(e.tok, CloneExpr(e.Guard), CloneExpr(e.Body));
} else {
Contract.Assert(e is AssumeExpr);
return new AssumeExpr(e.tok, CloneExpr(e.Guard), CloneExpr(e.Body));
}
} else if (expr is ITEExpr) {
var e = (ITEExpr)expr;
return new ITEExpr(e.tok, CloneExpr(e.Test), CloneExpr(e.Thn), CloneExpr(e.Els));
} else if (expr is ParensExpression) {
var e = (ParensExpression)expr;
return CloneExpr(e.E); // skip the parentheses in the clone
} else if (expr is IdentifierSequence) {
var e = (IdentifierSequence)expr;
var aa = e.Arguments == null ? null : e.Arguments.ConvertAll(CloneExpr);
return new IdentifierSequence(e.Tokens.ConvertAll(tk => (tk)), e.OpenParen == null ? null : (e.OpenParen), aa);
} else if (expr is MatchExpr) {
var e = (MatchExpr)expr;
return new MatchExpr(e.tok, CloneExpr(e.Source),
e.Cases.ConvertAll(c => new MatchCaseExpr(c.tok, c.Id, c.Arguments.ConvertAll(CloneBoundVar), CloneExpr(c.Body))));
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression
}
}
private bool ResolvePath(ModuleDecl root, List Path, out ModuleSignature p) {
p = root.Signature;
int i = 1;
while (i < Path.Count) {
ModuleSignature pp;
if (p.FindSubmodule(Path[i].val, out pp)) {
p = pp;
i++;
} else {
Error(Path[i], ModuleNotFoundErrorMessage(i, Path));
break;
}
}
return i == Path.Count;
}
public void ResolveTopLevelDecls_Signatures(ModuleDefinition def, List/*!*/ declarations, Graph/*!*/ datatypeDependencies) {
Contract.Requires(declarations != null);
Contract.Requires(datatypeDependencies != null); // more expensive check: Contract.Requires(cce.NonNullElements(datatypeDependencies));
foreach (TopLevelDecl d in declarations) {
Contract.Assert(d != null);
allTypeParameters.PushMarker();
ResolveTypeParameters(d.TypeArgs, true, d, true);
if (d is ArbitraryTypeDecl) {
// nothing to do
} else if (d is ClassDecl) {
ResolveClassMemberTypes((ClassDecl)d);
} else if (d is ModuleDecl) {
var decl = (ModuleDecl)d;
if (!def.IsGhost) {
if (decl.Signature.IsGhost)
{
if (!(def.IsDefaultModule)) // _module is allowed to contain ghost modules, but not be ghost itself. Note this presents a challenge to
// trusted verification, as toplevels can't be trusted if they invoke ghost module members.
Error(d.tok, "ghost modules can only be imported into other ghost modules, not physical ones.");
} else {
// physical modules are allowed everywhere
}
} else {
// everything is allowed in a ghost module
}
} else {
ResolveCtorTypes((DatatypeDecl)d, datatypeDependencies);
}
allTypeParameters.PopMarker();
}
}
public void ResolveTopLevelDecls_Meat(List/*!*/ declarations, Graph/*!*/ datatypeDependencies) {
Contract.Requires(declarations != null);
Contract.Requires(cce.NonNullElements(datatypeDependencies));
int prevErrorCount = ErrorCount;
// Resolve the meat of classes, and the type parameters of all top-level type declarations
foreach (TopLevelDecl d in declarations) {
Contract.Assert(d != null);
allTypeParameters.PushMarker();
ResolveTypeParameters(d.TypeArgs, false, d, true);
if (d is ClassDecl) {
ResolveClassMemberBodies((ClassDecl)d);
}
allTypeParameters.PopMarker();
}
if (ErrorCount == prevErrorCount) {
foreach (TopLevelDecl d in declarations) {
if (d is ClassDecl) {
foreach (var member in ((ClassDecl)d).Members) {
if (member is Method) {
var m = ((Method)member);
if (m.Body != null)
CheckTypeInference(m.Body);
} else if (member is Function) {
var f = (Function)member;
if (f.Body != null)
CheckTypeInference(f.Body);
}
}
}
}
}
// Perform the stratosphere check on inductive datatypes, and compute to what extent the inductive datatypes require equality support
foreach (var dtd in datatypeDependencies.TopologicallySortedComponents()) {
if (datatypeDependencies.GetSCCRepresentative(dtd) == dtd) {
// do the following check once per SCC, so call it on each SCC representative
SccStratosphereCheck(dtd, datatypeDependencies);
DetermineEqualitySupport(dtd, datatypeDependencies);
}
}
if (ErrorCount == prevErrorCount) { // because CheckCoCalls requires the given expression to have been successfully resolved
// Perform the guardedness check on co-datatypes
foreach (var fn in ModuleDefinition.AllFunctions(declarations)) {
var module = fn.EnclosingClass.Module;
if (fn.Body != null && module.CallGraph.GetSCCRepresentative(fn) == fn) {
bool dealsWithCodatatypes = false;
foreach (var m in module.CallGraph.GetSCC(fn)) {
var f = (Function)m;
if (f.ResultType.InvolvesCoDatatype) {
dealsWithCodatatypes = true;
break;
}
}
foreach (var m in module.CallGraph.GetSCC(fn)) {
var f = (Function)m;
var checker = new CoCallResolution(f, dealsWithCodatatypes);
checker.CheckCoCalls(f.Body);
}
}
}
// Inferred required equality support for datatypes and for Function and Method signatures
// First, do datatypes until a fixpoint is reached
bool inferredSomething;
do {
inferredSomething = false;
foreach (var d in declarations) {
if (d is DatatypeDecl) {
var dt = (DatatypeDecl)d;
foreach (var tp in dt.TypeArgs) {
if (tp.EqualitySupport == TypeParameter.EqualitySupportValue.Unspecified) {
// here's our chance to infer the need for equality support
foreach (var ctor in dt.Ctors) {
foreach (var arg in ctor.Formals) {
if (InferRequiredEqualitySupport(tp, arg.Type)) {
tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
inferredSomething = true;
goto DONE_DT; // break out of the doubly-nested loop
}
}
}
DONE_DT: ;
}
}
}
}
} while (inferredSomething);
// Now do it for Function and Method signatures
foreach (var d in declarations) {
if (d is ClassDecl) {
var cl = (ClassDecl)d;
foreach (var member in cl.Members) {
if (!member.IsGhost) {
if (member is Function) {
var f = (Function)member;
foreach (var tp in f.TypeArgs) {
if (tp.EqualitySupport == TypeParameter.EqualitySupportValue.Unspecified) {
// here's our chance to infer the need for equality support
if (InferRequiredEqualitySupport(tp, f.ResultType)) {
tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
} else {
foreach (var p in f.Formals) {
if (InferRequiredEqualitySupport(tp, p.Type)) {
tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
break;
}
}
}
}
}
} else if (member is Method) {
var m = (Method)member;
foreach (var tp in m.TypeArgs) {
if (tp.EqualitySupport == TypeParameter.EqualitySupportValue.Unspecified) {
// here's our chance to infer the need for equality support
foreach (var p in m.Ins) {
if (InferRequiredEqualitySupport(tp, p.Type)) {
tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
goto DONE;
}
}
foreach (var p in m.Outs) {
if (InferRequiredEqualitySupport(tp, p.Type)) {
tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
goto DONE;
}
}
DONE: ;
}
}
}
}
}
}
}
// Check that all == and != operators in non-ghost contexts are applied to equality-supporting types.
// Note that this check can only be done after determining which expressions are ghosts.
foreach (var d in declarations) {
if (d is ClassDecl) {
var cl = (ClassDecl)d;
foreach (var member in cl.Members) {
if (!member.IsGhost) {
if (member is Field) {
var f = (Field)member;
CheckEqualityTypes_Type(f.tok, f.Type);
} else if (member is Function) {
var f = (Function)member;
foreach (var p in f.Formals) {
if (!p.IsGhost) {
CheckEqualityTypes_Type(p.tok, p.Type);
}
}
CheckEqualityTypes_Type(f.tok, f.ResultType);
if (f.Body != null) {
CheckEqualityTypes(f.Body);
}
} else if (member is Method) {
var m = (Method)member;
foreach (var p in m.Ins) {
if (!p.IsGhost) {
CheckEqualityTypes_Type(p.tok, p.Type);
}
}
foreach (var p in m.Outs) {
if (!p.IsGhost) {
CheckEqualityTypes_Type(p.tok, p.Type);
}
}
if (m.Body != null) {
CheckEqualityTypes_Stmt(m.Body);
}
}
}
}
} else if (d is DatatypeDecl) {
var dt = (DatatypeDecl)d;
foreach (var ctor in dt.Ctors) {
foreach (var p in ctor.Formals) {
if (!p.IsGhost) {
CheckEqualityTypes_Type(p.tok, p.Type);
}
}
}
}
}
// Check that copredicates are not recursive with non-copredicate functions.
foreach (var fn in ModuleDefinition.AllFunctions(declarations)) {
if (fn.Body != null && (fn is CoPredicate || fn.IsRecursive)) {
CoPredicateChecks(fn.Body, fn, CallingPosition.Positive);
}
}
}
}
enum CallingPosition { Positive, Negative, Neither }
static CallingPosition Invert(CallingPosition cp) {
switch (cp) {
case CallingPosition.Positive: return CallingPosition.Negative;
case CallingPosition.Negative: return CallingPosition.Positive;
default: return CallingPosition.Neither;
}
}
void CoPredicateChecks(Expression expr, Function context, CallingPosition cp) {
Contract.Requires(expr != null);
Contract.Requires(context != null);
if (expr is ConcreteSyntaxExpression) {
var e = (ConcreteSyntaxExpression)expr;
CoPredicateChecks(e.Resolved, context, cp);
return;
} else if (expr is FunctionCallExpr) {
var e = (FunctionCallExpr)expr;
var moduleCaller = context.EnclosingClass.Module;
var moduleCallee = e.Function.EnclosingClass.Module;
if (moduleCaller == moduleCallee && moduleCaller.CallGraph.GetSCCRepresentative(context) == moduleCaller.CallGraph.GetSCCRepresentative(e.Function)) {
// we're looking at a recursive call
if (context is CoPredicate) {
if (!(e.Function is CoPredicate)) {
Error(e, "a recursive call from a copredicate can go only to other copredicates");
} else if (cp != CallingPosition.Positive) {
Error(e, "a recursive copredicate call can only be done in positive positions");
}
} else if (e.Function is CoPredicate) {
Error(e, "a recursive call from a non-copredicate can go only to other non-copredicates");
}
}
// fall through to do the subexpressions
} else if (expr is UnaryExpr) {
var e = (UnaryExpr)expr;
if (e.Op == UnaryExpr.Opcode.Not) {
CoPredicateChecks(e.E, context, Invert(cp));
return;
}
} else if (expr is BinaryExpr) {
var e = (BinaryExpr)expr;
switch (e.ResolvedOp) {
case BinaryExpr.ResolvedOpcode.And:
case BinaryExpr.ResolvedOpcode.Or:
CoPredicateChecks(e.E0, context, cp);
CoPredicateChecks(e.E1, context, cp);
return;
case BinaryExpr.ResolvedOpcode.Imp:
CoPredicateChecks(e.E0, context, Invert(cp));
CoPredicateChecks(e.E1, context, cp);
return;
default:
break;
}
} else if (expr is LetExpr) {
var e = (LetExpr)expr;
CoPredicateChecks(e.Body, context, cp);
return;
} else if (expr is QuantifierExpr) {
var e = (QuantifierExpr)expr;
if (e.Range != null) {
CoPredicateChecks(e.Range, context, e is ExistsExpr ? Invert(cp) : cp);
}
CoPredicateChecks(e.Term, context, cp);
return;
}
expr.SubExpressions.Iter(ee => CoPredicateChecks(ee, context, CallingPosition.Neither));
}
void CheckEqualityTypes_Stmt(Statement stmt) {
Contract.Requires(stmt != null);
if (stmt.IsGhost) {
return;
} else if (stmt is PrintStmt) {
var s = (PrintStmt)stmt;
foreach (var arg in s.Args) {
if (arg.E != null) {
CheckEqualityTypes(arg.E);
}
}
} else if (stmt is BreakStmt) {
} else if (stmt is ReturnStmt) {
var s = (ReturnStmt)stmt;
if (s.rhss != null) {
s.rhss.Iter(CheckEqualityTypes_Rhs);
}
} else if (stmt is AssignStmt) {
AssignStmt s = (AssignStmt)stmt;
CheckEqualityTypes(s.Lhs);
CheckEqualityTypes_Rhs(s.Rhs);
} else if (stmt is VarDecl) {
var s = (VarDecl)stmt;
s.SubStatements.Iter(CheckEqualityTypes_Stmt);
} else if (stmt is CallStmt) {
var s = (CallStmt)stmt;
CheckEqualityTypes(s.Receiver);
s.Args.Iter(CheckEqualityTypes);
s.Lhs.Iter(CheckEqualityTypes);
Contract.Assert(s.Method.TypeArgs.Count <= s.TypeArgumentSubstitutions.Count);
var i = 0;
foreach (var formalTypeArg in s.Method.TypeArgs) {
var actualTypeArg = s.TypeArgumentSubstitutions[formalTypeArg];
if (formalTypeArg.MustSupportEquality && !actualTypeArg.SupportsEquality) {
Error(s.Tok, "type parameter {0} ({1}) passed to method {2} must support equality (got {3}){4}", i, formalTypeArg.Name, s.Method.Name, actualTypeArg, TypeEqualityErrorMessageHint(actualTypeArg));
}
i++;
}
} else if (stmt is BlockStmt) {
var s = (BlockStmt)stmt;
s.Body.Iter(CheckEqualityTypes_Stmt);
} else if (stmt is IfStmt) {
var s = (IfStmt)stmt;
if (s.Guard != null) {
CheckEqualityTypes(s.Guard);
}
s.SubStatements.Iter(CheckEqualityTypes_Stmt);
} else if (stmt is AlternativeStmt) {
var s = (AlternativeStmt)stmt;
foreach (var alt in s.Alternatives) {
CheckEqualityTypes(alt.Guard);
alt.Body.Iter(CheckEqualityTypes_Stmt);
}
} else if (stmt is WhileStmt) {
var s = (WhileStmt)stmt;
if (s.Guard != null) {
CheckEqualityTypes(s.Guard);
}
CheckEqualityTypes_Stmt(s.Body);
} else if (stmt is AlternativeLoopStmt) {
var s = (AlternativeLoopStmt)stmt;
foreach (var alt in s.Alternatives) {
CheckEqualityTypes(alt.Guard);
alt.Body.Iter(CheckEqualityTypes_Stmt);
}
} else if (stmt is ParallelStmt) {
var s = (ParallelStmt)stmt;
CheckEqualityTypes(s.Range);
CheckEqualityTypes_Stmt(s.Body);
} else if (stmt is MatchStmt) {
var s = (MatchStmt)stmt;
CheckEqualityTypes(s.Source);
foreach (MatchCaseStmt mc in s.Cases) {
mc.Body.Iter(CheckEqualityTypes_Stmt);
}
} else if (stmt is ConcreteSyntaxStatement) {
var s = (ConcreteSyntaxStatement)stmt;
s.ResolvedStatements.Iter(CheckEqualityTypes_Stmt);
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement
}
}
void CheckEqualityTypes_Rhs(AssignmentRhs rhs) {
Contract.Requires(rhs != null);
rhs.SubExpressions.Iter(CheckEqualityTypes);
rhs.SubStatements.Iter(CheckEqualityTypes_Stmt);
}
void CheckEqualityTypes(Expression expr) {
Contract.Requires(expr != null);
if (expr is BinaryExpr) {
var e = (BinaryExpr)expr;
var t0 = e.E0.Type.Normalize();
var t1 = e.E1.Type.Normalize();
switch (e.Op) {
case BinaryExpr.Opcode.Eq:
case BinaryExpr.Opcode.Neq:
// First, check a special case: a datatype value (like Nil) that takes no parameters
var e0 = e.E0.Resolved as DatatypeValue;
var e1 = e.E1.Resolved as DatatypeValue;
if (e0 != null && e0.Arguments.Count == 0) {
// that's cool
} else if (e1 != null && e1.Arguments.Count == 0) {
// oh yeah!
} else if (!t0.SupportsEquality) {
Error(e.E0, "{0} can only be applied to expressions of types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t0, TypeEqualityErrorMessageHint(t0));
} else if (!t1.SupportsEquality) {
Error(e.E1, "{0} can only be applied to expressions of types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t1, TypeEqualityErrorMessageHint(t1));
}
break;
default:
switch (e.ResolvedOp) {
// Note, all operations on sets, multisets, and maps are guaranteed to work because of restrictions placed on how
// these types are instantiated. (Except: This guarantee does not apply to equality on maps, because the Range type
// of maps is not restricted, only the Domain type. However, the equality operator is checked above.)
case BinaryExpr.ResolvedOpcode.InSeq:
case BinaryExpr.ResolvedOpcode.NotInSeq:
case BinaryExpr.ResolvedOpcode.Prefix:
case BinaryExpr.ResolvedOpcode.ProperPrefix:
if (!t1.SupportsEquality) {
Error(e.E1, "{0} can only be applied to expressions of sequence types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t1, TypeEqualityErrorMessageHint(t1));
} else if (!t0.SupportsEquality) {
if (e.ResolvedOp == BinaryExpr.ResolvedOpcode.InSet || e.ResolvedOp == BinaryExpr.ResolvedOpcode.NotInSeq) {
Error(e.E0, "{0} can only be applied to expressions of types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t0, TypeEqualityErrorMessageHint(t0));
} else {
Error(e.E0, "{0} can only be applied to expressions of sequence types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t0, TypeEqualityErrorMessageHint(t0));
}
}
break;
default:
break;
}
break;
}
} else if (expr is ComprehensionExpr) {
var e = (ComprehensionExpr)expr;
foreach (var bv in e.BoundVars) {
CheckEqualityTypes_Type(bv.tok, bv.Type);
}
} else if (expr is LetExpr) {
var e = (LetExpr)expr;
foreach (var bv in e.Vars) {
CheckEqualityTypes_Type(bv.tok, bv.Type);
}
} else if (expr is FunctionCallExpr) {
var e = (FunctionCallExpr)expr;
Contract.Assert(e.Function.TypeArgs.Count <= e.TypeArgumentSubstitutions.Count);
var i = 0;
foreach (var formalTypeArg in e.Function.TypeArgs) {
var actualTypeArg = e.TypeArgumentSubstitutions[formalTypeArg];
if (formalTypeArg.MustSupportEquality && !actualTypeArg.SupportsEquality) {
Error(e.tok, "type parameter {0} ({1}) passed to function {2} must support equality (got {3}){4}", i, formalTypeArg.Name, e.Function.Name, actualTypeArg, TypeEqualityErrorMessageHint(actualTypeArg));
}
i++;
}
}
foreach (var ee in expr.SubExpressions) {
CheckEqualityTypes(ee);
}
}
void CheckEqualityTypes_Type(IToken tok, Type type) {
Contract.Requires(tok != null);
Contract.Requires(type != null);
type = type.Normalize();
if (type is BasicType) {
// fine
} else if (type is SetType) {
var argType = ((SetType)type).Arg;
if (!argType.SupportsEquality) {
Error(tok, "set argument type must support equality (got {0}){1}", argType, TypeEqualityErrorMessageHint(argType));
}
CheckEqualityTypes_Type(tok, argType);
} else if (type is MultiSetType) {
var argType = ((MultiSetType)type).Arg;
if (!argType.SupportsEquality) {
Error(tok, "multiset argument type must support equality (got {0}){1}", argType, TypeEqualityErrorMessageHint(argType));
}
CheckEqualityTypes_Type(tok, argType);
} else if (type is MapType) {
var mt = (MapType)type;
if (!mt.Domain.SupportsEquality) {
Error(tok, "map domain type must support equality (got {0}){1}", mt.Domain, TypeEqualityErrorMessageHint(mt.Domain));
}
CheckEqualityTypes_Type(tok, mt.Domain);
CheckEqualityTypes_Type(tok, mt.Range);
} else if (type is SeqType) {
Type argType = ((SeqType)type).Arg;
CheckEqualityTypes_Type(tok, argType);
} else if (type is UserDefinedType) {
var udt = (UserDefinedType)type;
if (udt.ResolvedClass != null) {
Contract.Assert(udt.ResolvedClass.TypeArgs.Count == udt.TypeArgs.Count);
var i = 0;
foreach (var argType in udt.TypeArgs) {
var formalTypeArg = udt.ResolvedClass.TypeArgs[i];
if (formalTypeArg.MustSupportEquality && !argType.SupportsEquality) {
Error(tok, "type parameter {0} ({1}) passed to type {2} must support equality (got {3}){4}", i, formalTypeArg.Name, udt.ResolvedClass.Name, argType, TypeEqualityErrorMessageHint(argType));
}
CheckEqualityTypes_Type(tok, argType);
i++;
}
} else {
Contract.Assert(udt.TypeArgs.Count == 0); // TypeParameters have no type arguments
}
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
}
}
bool CheckTypeInference(Expression e) {
if (e == null) return false;
foreach (Expression se in e.SubExpressions) {
if (CheckTypeInference(se))
return true;
}
if (e.Type is TypeProxy && !(e.Type is InferredTypeProxy || e.Type is ParamTypeProxy || e.Type is ObjectTypeProxy)) {
Error(e.tok, "the type of this expression is underspecified, but it cannot be an arbitrary type.");
return true;
}
return false;
}
void CheckTypeInference(Statement stmt) {
Contract.Requires(stmt != null);
if (stmt is PrintStmt) {
var s = (PrintStmt)stmt;
s.Args.Iter(arg => CheckTypeInference(arg.E));
} else if (stmt is BreakStmt) {
} else if (stmt is ReturnStmt) {
var s = (ReturnStmt)stmt;
if (s.rhss != null) {
s.rhss.Iter(rhs => rhs.SubExpressions.Iter(e => CheckTypeInference(e)));
}
} else if (stmt is AssignStmt) {
AssignStmt s = (AssignStmt)stmt;
CheckTypeInference(s.Lhs);
s.Rhs.SubExpressions.Iter(e => { CheckTypeInference(e); });
} else if (stmt is VarDecl) {
var s = (VarDecl)stmt;
s.SubStatements.Iter(CheckTypeInference);
if (s.Type is TypeProxy && !(s.Type is InferredTypeProxy || s.Type is ParamTypeProxy || s.Type is ObjectTypeProxy)) {
Error(s.Tok, "the type of this expression is underspecified, but it cannot be an arbitrary type.");
}
} else if (stmt is CallStmt) {
var s = (CallStmt)stmt;
CheckTypeInference(s.Receiver);
s.Args.Iter(e => CheckTypeInference(e));
s.Lhs.Iter(e => CheckTypeInference(e));
} else if (stmt is BlockStmt) {
var s = (BlockStmt)stmt;
s.Body.Iter(CheckTypeInference);
} else if (stmt is IfStmt) {
var s = (IfStmt)stmt;
if (s.Guard != null) {
CheckTypeInference(s.Guard);
}
s.SubStatements.Iter(CheckTypeInference);
} else if (stmt is AlternativeStmt) {
var s = (AlternativeStmt)stmt;
foreach (var alt in s.Alternatives) {
CheckTypeInference(alt.Guard);
alt.Body.Iter(CheckTypeInference);
}
} else if (stmt is WhileStmt) {
var s = (WhileStmt)stmt;
if (s.Guard != null) {
CheckTypeInference(s.Guard);
}
CheckTypeInference(s.Body);
} else if (stmt is AlternativeLoopStmt) {
var s = (AlternativeLoopStmt)stmt;
foreach (var alt in s.Alternatives) {
CheckTypeInference(alt.Guard);
alt.Body.Iter(CheckTypeInference);
}
} else if (stmt is ParallelStmt) {
var s = (ParallelStmt)stmt;
CheckTypeInference(s.Range);
CheckTypeInference(s.Body);
} else if (stmt is MatchStmt) {
var s = (MatchStmt)stmt;
CheckTypeInference(s.Source);
foreach (MatchCaseStmt mc in s.Cases) {
mc.Body.Iter(CheckTypeInference);
}
} else if (stmt is ConcreteSyntaxStatement) {
var s = (ConcreteSyntaxStatement)stmt;
s.ResolvedStatements.Iter(CheckTypeInference);
} else if (stmt is PredicateStmt) {
CheckTypeInference(((PredicateStmt)stmt).Expr);
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement
}
}
string TypeEqualityErrorMessageHint(Type argType) {
Contract.Requires(argType != null);
var tp = argType.AsTypeParameter;
if (tp != null) {
return string.Format(" (perhaps try declaring type parameter '{0}' on line {1} as '{0}(==)', which says it can only be instantiated with a type that supports equality)", tp.Name, tp.tok.line);
}
return "";
}
bool InferRequiredEqualitySupport(TypeParameter tp, Type type) {
Contract.Requires(tp != null);
Contract.Requires(type != null);
type = type.Normalize();
if (type is BasicType) {
} else if (type is SetType) {
var st = (SetType)type;
return st.Arg.AsTypeParameter == tp || InferRequiredEqualitySupport(tp, st.Arg);
} else if (type is MultiSetType) {
var ms = (MultiSetType)type;
return ms.Arg.AsTypeParameter == tp || InferRequiredEqualitySupport(tp, ms.Arg);
} else if (type is MapType) {
var mt = (MapType)type;
return mt.Domain.AsTypeParameter == tp || InferRequiredEqualitySupport(tp, mt.Domain) || InferRequiredEqualitySupport(tp, mt.Range);
} else if (type is SeqType) {
var sq = (SeqType)type;
return InferRequiredEqualitySupport(tp, sq.Arg);
} else if (type is UserDefinedType) {
var udt = (UserDefinedType)type;
if (udt.ResolvedClass != null) {
var i = 0;
foreach (var argType in udt.TypeArgs) {
var formalTypeArg = udt.ResolvedClass.TypeArgs[i];
if ((formalTypeArg.MustSupportEquality && argType.AsTypeParameter == tp) || InferRequiredEqualitySupport(tp, argType)) {
return true;
}
i++;
}
} else {
Contract.Assert(udt.TypeArgs.Count == 0); // TypeParameters have no type arguments
}
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
}
return false;
}
ClassDecl currentClass;
Function currentFunction;
readonly Scope/*!*/ allTypeParameters = new Scope();
readonly Scope/*!*/ scope = new Scope();
Scope/*!*/ labeledStatements = new Scope();
List loopStack = new List(); // the enclosing loops (from which it is possible to break out)
readonly Dictionary inSpecOnlyContext = new Dictionary(); // invariant: domain contain union of the domains of "labeledStatements" and "loopStack"
///
/// Assumes type parameters have already been pushed
///
void ResolveClassMemberTypes(ClassDecl/*!*/ cl) {
Contract.Requires(cl != null);
Contract.Requires(currentClass == null);
Contract.Ensures(currentClass == null);
currentClass = cl;
foreach (MemberDecl member in cl.Members) {
member.EnclosingClass = cl;
if (member is Field) {
ResolveType(member.tok, ((Field)member).Type, null, false);
} else if (member is Function) {
Function f = (Function)member;
allTypeParameters.PushMarker();
ResolveTypeParameters(f.TypeArgs, true, f, false);
ResolveFunctionSignature(f);
allTypeParameters.PopMarker();
} else if (member is Method) {
Method m = (Method)member;
allTypeParameters.PushMarker();
ResolveTypeParameters(m.TypeArgs, true, m, false);
ResolveMethodSignature(m);
allTypeParameters.PopMarker();
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected member type
}
}
currentClass = null;
}
///
/// Assumes type parameters have already been pushed, and that all types in class members have been resolved
///
void ResolveClassMemberBodies(ClassDecl cl) {
Contract.Requires(cl != null);
Contract.Requires(currentClass == null);
Contract.Ensures(currentClass == null);
ResolveAttributes(cl.Attributes, false);
currentClass = cl;
foreach (MemberDecl member in cl.Members) {
if (member is Field) {
ResolveAttributes(member.Attributes, false);
// nothing more to do
} else if (member is Function) {
Function f = (Function)member;
allTypeParameters.PushMarker();
ResolveTypeParameters(f.TypeArgs, false, f, false);
ResolveFunction(f);
allTypeParameters.PopMarker();
} else if (member is Method) {
Method m = (Method)member;
allTypeParameters.PushMarker();
ResolveTypeParameters(m.TypeArgs, false, m, false);
ResolveMethod(m);
allTypeParameters.PopMarker();
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected member type
}
}
currentClass = null;
}
///
/// Assumes type parameters have already been pushed
///
void ResolveCtorTypes(DatatypeDecl/*!*/ dt, Graph/*!*/ dependencies) {
Contract.Requires(dt != null);
Contract.Requires(dependencies != null); // more expensive check: Contract.Requires(cce.NonNullElements(dependencies));
foreach (DatatypeCtor ctor in dt.Ctors) {
ctor.EnclosingDatatype = dt;
allTypeParameters.PushMarker();
ResolveCtorSignature(ctor, dt.TypeArgs);
allTypeParameters.PopMarker();
if (dt is IndDatatypeDecl) {
var idt = (IndDatatypeDecl)dt;
dependencies.AddVertex(idt);
foreach (Formal p in ctor.Formals) {
AddDatatypeDependencyEdge(idt, p.Type, dependencies);
}
}
}
}
void AddDatatypeDependencyEdge(IndDatatypeDecl/*!*/ dt, Type/*!*/ tp, Graph/*!*/ dependencies) {
Contract.Requires(dt != null);
Contract.Requires(tp != null);
Contract.Requires(dependencies != null); // more expensive check: Contract.Requires(cce.NonNullElements(dependencies));
var dependee = tp.AsIndDatatype;
if (dependee != null && dt.Module == dependee.Module) {
dependencies.AddEdge((IndDatatypeDecl)dt, dependee);
foreach (var ta in ((UserDefinedType)tp).TypeArgs) {
AddDatatypeDependencyEdge(dt, ta, dependencies);
}
}
}
///
/// Check that the SCC of 'startingPoint' can be carved up into stratospheres in such a way that each
/// datatype has some value that can be constructed from datatypes in lower stratospheres only.
/// The algorithm used here is quadratic in the number of datatypes in the SCC. Since that number is
/// deemed to be rather small, this seems okay.
///
/// As a side effect of this checking, the DefaultCtor field is filled in (for every inductive datatype
/// that passes the check). It may be that several constructors could be used as the default, but
/// only the first one encountered as recorded. This particular choice is slightly more than an
/// implementation detail, because it affects how certain cycles among inductive datatypes (having
/// to do with the types used to instantiate type parameters of datatypes) are used.
///
/// The role of the SCC here is simply to speed up this method. It would still be correct if the
/// equivalence classes in the given SCC were unions of actual SCC's. In particular, this method
/// would still work if "dependencies" consisted of one large SCC containing all the inductive
/// datatypes in the module.
///
void SccStratosphereCheck(IndDatatypeDecl startingPoint, Graph/*!*/ dependencies) {
Contract.Requires(startingPoint != null);
Contract.Requires(dependencies != null); // more expensive check: Contract.Requires(cce.NonNullElements(dependencies));
var scc = dependencies.GetSCC(startingPoint);
int totalCleared = 0;
while (true) {
int clearedThisRound = 0;
foreach (var dt in scc) {
if (dt.DefaultCtor != null) {
// previously cleared
} else if (ComputeDefaultCtor(dt)) {
Contract.Assert(dt.DefaultCtor != null); // should have been set by the successful call to StratosphereCheck)
clearedThisRound++;
totalCleared++;
}
}
if (totalCleared == scc.Count) {
// all is good
return;
} else if (clearedThisRound != 0) {
// some progress was made, so let's keep going
} else {
// whatever is in scc-cleared now failed to pass the test
foreach (var dt in scc) {
if (dt.DefaultCtor == null) {
Error(dt, "because of cyclic dependencies among constructor argument types, no instances of datatype '{0}' can be constructed", dt.Name);
}
}
return;
}
}
}
///
/// Check that the datatype has some constructor all whose argument types can be constructed.
/// Returns 'true' and sets dt.DefaultCtor if that is the case.
///
bool ComputeDefaultCtor(IndDatatypeDecl dt) {
Contract.Requires(dt != null);
Contract.Requires(dt.DefaultCtor == null); // the intention is that this method be called only when DefaultCtor hasn't already been set
Contract.Ensures(!Contract.Result() || dt.DefaultCtor != null);
// Stated differently, check that there is some constuctor where no argument type goes to the same stratum.
foreach (DatatypeCtor ctor in dt.Ctors) {
var typeParametersUsed = new List();
foreach (Formal p in ctor.Formals) {
if (!CheckCanBeConstructed(p.Type, typeParametersUsed)) {
// the argument type (has a component which) is not yet known to be constructable
goto NEXT_OUTER_ITERATION;
}
}
// this constructor satisfies the requirements, so the datatype is allowed
dt.DefaultCtor = ctor;
dt.TypeParametersUsedInConstructionByDefaultCtor = new bool[dt.TypeArgs.Count];
for (int i = 0; i < dt.TypeArgs.Count; i++) {
dt.TypeParametersUsedInConstructionByDefaultCtor[i] = typeParametersUsed.Contains(dt.TypeArgs[i]);
}
return true;
NEXT_OUTER_ITERATION: { }
}
// no constructor satisfied the requirements, so this is an illegal datatype declaration
return false;
}
bool CheckCanBeConstructed(Type tp, List typeParametersUsed) {
var dependee = tp.AsIndDatatype;
if (dependee == null) {
// the type is not an inductive datatype, which means it is always possible to construct it
if (tp.IsTypeParameter) {
typeParametersUsed.Add(((UserDefinedType)tp).ResolvedParam);
}
return true;
} else if (dependee.DefaultCtor == null) {
// the type is an inductive datatype that we don't yet know how to construct
return false;
}
// also check the type arguments of the inductive datatype
Contract.Assert(((UserDefinedType)tp).TypeArgs.Count == dependee.TypeParametersUsedInConstructionByDefaultCtor.Length);
var i = 0;
foreach (var ta in ((UserDefinedType)tp).TypeArgs) { // note, "tp" is known to be a UserDefinedType, because that follows from tp being an inductive datatype
if (dependee.TypeParametersUsedInConstructionByDefaultCtor[i] && !CheckCanBeConstructed(ta, typeParametersUsed)) {
return false;
}
i++;
}
return true;
}
void DetermineEqualitySupport(IndDatatypeDecl startingPoint, Graph/*!*/ dependencies) {
Contract.Requires(startingPoint != null);
Contract.Requires(dependencies != null); // more expensive check: Contract.Requires(cce.NonNullElements(dependencies));
var scc = dependencies.GetSCC(startingPoint);
// First, the simple case: If any parameter of any inductive datatype in the SCC is of a codatatype type, then
// the whole SCC is incapable of providing the equality operation.
foreach (var dt in scc) {
Contract.Assume(dt.EqualitySupport == IndDatatypeDecl.ES.NotYetComputed);
foreach (var ctor in dt.Ctors) {
foreach (var arg in ctor.Formals) {
var anotherIndDt = arg.Type.AsIndDatatype;
if ((anotherIndDt != null && anotherIndDt.EqualitySupport == IndDatatypeDecl.ES.Never) || arg.Type.IsCoDatatype) {
// arg.Type is known never to support equality
// So, go around the entire SCC and record what we learnt
foreach (var ddtt in scc) {
ddtt.EqualitySupport = IndDatatypeDecl.ES.Never;
}
return; // we are done
}
}
}
}
// Now for the more involved case: we need to determine which type parameters determine equality support for each datatype in the SCC
// We start by seeing where each datatype's type parameters are used in a place known to determine equality support.
bool thingsChanged = false;
foreach (var dt in scc) {
if (dt.TypeArgs.Count == 0) {
// if the datatype has no type parameters, we certainly won't find any type parameters being used in the arguments types to the constructors
continue;
}
foreach (var ctor in dt.Ctors) {
foreach (var arg in ctor.Formals) {
var typeArg = arg.Type.AsTypeParameter;
if (typeArg != null) {
typeArg.NecessaryForEqualitySupportOfSurroundingInductiveDatatype = true;
thingsChanged = true;
} else {
var otherDt = arg.Type.AsIndDatatype;
if (otherDt != null && otherDt.EqualitySupport == IndDatatypeDecl.ES.ConsultTypeArguments) { // datatype is in a different SCC
var otherUdt = (UserDefinedType)arg.Type.Normalize();
var i = 0;
foreach (var otherTp in otherDt.TypeArgs) {
if (otherTp.NecessaryForEqualitySupportOfSurroundingInductiveDatatype) {
var tp = otherUdt.TypeArgs[i].AsTypeParameter;
if (tp != null) {
tp.NecessaryForEqualitySupportOfSurroundingInductiveDatatype = true;
thingsChanged = true;
}
}
}
}
}
}
}
}
// Then we propagate this information up through the SCC
while (thingsChanged) {
thingsChanged = false;
foreach (var dt in scc) {
if (dt.TypeArgs.Count == 0) {
// if the datatype has no type parameters, we certainly won't find any type parameters being used in the arguments types to the constructors
continue;
}
foreach (var ctor in dt.Ctors) {
foreach (var arg in ctor.Formals) {
var otherDt = arg.Type.AsIndDatatype;
if (otherDt != null && otherDt.EqualitySupport == IndDatatypeDecl.ES.NotYetComputed) { // otherDt lives in the same SCC
var otherUdt = (UserDefinedType)arg.Type.Normalize();
var i = 0;
foreach (var otherTp in otherDt.TypeArgs) {
if (otherTp.NecessaryForEqualitySupportOfSurroundingInductiveDatatype) {
var tp = otherUdt.TypeArgs[i].AsTypeParameter;
if (tp != null && !tp.NecessaryForEqualitySupportOfSurroundingInductiveDatatype) {
tp.NecessaryForEqualitySupportOfSurroundingInductiveDatatype = true;
thingsChanged = true;
}
}
i++;
}
}
}
}
}
}
// Now that we have computed the .NecessaryForEqualitySupportOfSurroundingInductiveDatatype values, mark the datatypes as ones
// where equality support should be checked by looking at the type arguments.
foreach (var dt in scc) {
dt.EqualitySupport = IndDatatypeDecl.ES.ConsultTypeArguments;
}
}
void ResolveAttributes(Attributes attrs, bool twoState) {
// order does not matter much for resolution, so resolve them in reverse order
for (; attrs != null; attrs = attrs.Prev) {
if (attrs.Args != null) {
ResolveAttributeArgs(attrs.Args, twoState, true);
}
}
}
void ResolveAttributeArgs(List/*!*/ args, bool twoState, bool allowGhosts) {
Contract.Requires(args != null);
foreach (Attributes.Argument aa in args) {
Contract.Assert(aa != null);
if (aa.E != null) {
ResolveExpression(aa.E, twoState);
if (!allowGhosts) {
CheckIsNonGhost(aa.E);
}
}
}
}
void ResolveTypeParameters(List/*!*/ tparams, bool emitErrors, TypeParameter.ParentType/*!*/ parent, bool isToplevel) {
Contract.Requires(tparams != null);
Contract.Requires(parent != null);
// push non-duplicated type parameter names
int index = 0;
foreach (TypeParameter tp in tparams) {
Contract.Assert(tp != null);
if (emitErrors) {
// we're seeing this TypeParameter for the first time
tp.Parent = parent;
tp.PositionalIndex = index;
}
if (!allTypeParameters.Push(tp.Name, tp) && emitErrors) {
Error(tp, "Duplicate type-parameter name: {0}", tp.Name);
}
}
}
///
/// Assumes type parameters have already been pushed
///
void ResolveFunctionSignature(Function f) {
Contract.Requires(f != null);
scope.PushMarker();
if (f.SignatureIsOmitted) {
Error(f, "function signature can be omitted only in refining functions");
}
var defaultTypeArguments = f.TypeArgs.Count == 0 ? f.TypeArgs : null;
foreach (Formal p in f.Formals) {
if (!scope.Push(p.Name, p)) {
Error(p, "Duplicate parameter name: {0}", p.Name);
}
ResolveType(p.tok, p.Type, defaultTypeArguments, true);
}
ResolveType(f.tok, f.ResultType, defaultTypeArguments, true);
scope.PopMarker();
}
///
/// Assumes type parameters have already been pushed
///
void ResolveFunction(Function f) {
Contract.Requires(f != null);
Contract.Requires(currentFunction == null);
Contract.Ensures(currentFunction == null);
scope.PushMarker();
currentFunction = f;
if (f.IsStatic) {
scope.AllowInstance = false;
}
foreach (Formal p in f.Formals) {
scope.Push(p.Name, p);
}
ResolveAttributes(f.Attributes, false);
foreach (Expression r in f.Req) {
ResolveExpression(r, false);
Contract.Assert(r.Type != null); // follows from postcondition of ResolveExpression
if (!UnifyTypes(r.Type, Type.Bool)) {
Error(r, "Precondition must be a boolean (got {0})", r.Type);
}
}
foreach (FrameExpression fr in f.Reads) {
ResolveFrameExpression(fr, "reads");
}
foreach (Expression r in f.Ens) {
ResolveExpression(r, false); // since this is a function, the postcondition is still a one-state predicate
Contract.Assert(r.Type != null); // follows from postcondition of ResolveExpression
if (!UnifyTypes(r.Type, Type.Bool)) {
Error(r, "Postcondition must be a boolean (got {0})", r.Type);
}
}
foreach (Expression r in f.Decreases.Expressions) {
ResolveExpression(r, false);
// any type is fine
}
if (f.Body != null) {
var prevErrorCount = ErrorCount;
List matchVarContext = new List(f.Formals);
ResolveExpression(f.Body, false, matchVarContext);
if (!f.IsGhost) {
CheckIsNonGhost(f.Body);
}
Contract.Assert(f.Body.Type != null); // follows from postcondition of ResolveExpression
if (!UnifyTypes(f.Body.Type, f.ResultType)) {
Error(f, "Function body type mismatch (expected {0}, got {1})", f.ResultType, f.Body.Type);
}
}
currentFunction = null;
scope.PopMarker();
}
void ResolveFrameExpression(FrameExpression fe, string kind) {
Contract.Requires(fe != null);
Contract.Requires(kind != null);
ResolveExpression(fe.E, false);
Type t = fe.E.Type;
Contract.Assert(t != null); // follows from postcondition of ResolveExpression
if (t is CollectionType) {
t = ((CollectionType)t).Arg;
}
if (t is ObjectType) {
// fine, as long as there's no field name
if (fe.FieldName != null) {
Error(fe.E, "type '{0}' does not contain a field named '{1}'", t, fe.FieldName);
}
} else if (UserDefinedType.DenotesClass(t) != null) {
// fine type
if (fe.FieldName != null) {
NonProxyType nptype;
MemberDecl member = ResolveMember(fe.E.tok, t, fe.FieldName, out nptype);
UserDefinedType ctype = (UserDefinedType)nptype; // correctness of cast follows from the DenotesClass test above
if (member == null) {
// error has already been reported by ResolveMember
} else if (!(member is Field)) {
Error(fe.E, "member {0} in type {1} does not refer to a field", fe.FieldName, cce.NonNull(ctype).Name);
} else {
Contract.Assert(ctype != null && ctype.ResolvedClass != null); // follows from postcondition of ResolveMember
fe.Field = (Field)member;
}
}
} else {
Error(fe.E, "a {0}-clause expression must denote an object or a collection of objects (instead got {1})", kind, fe.E.Type);
}
}
///
/// Assumes type parameters have already been pushed
///
void ResolveMethodSignature(Method m) {
Contract.Requires(m != null);
scope.PushMarker();
if (m.SignatureIsOmitted) {
Error(m, "method signature can be omitted only in refining methods");
}
var defaultTypeArguments = m.TypeArgs.Count == 0 ? m.TypeArgs : null;
// resolve in-parameters
foreach (Formal p in m.Ins) {
if (!scope.Push(p.Name, p)) {
Error(p, "Duplicate parameter name: {0}", p.Name);
}
ResolveType(p.tok, p.Type, defaultTypeArguments, true);
}
// resolve out-parameters
foreach (Formal p in m.Outs) {
if (!scope.Push(p.Name, p)) {
Error(p, "Duplicate parameter name: {0}", p.Name);
}
ResolveType(p.tok, p.Type, defaultTypeArguments, true);
}
scope.PopMarker();
}
///
/// Assumes type parameters have already been pushed
///
void ResolveMethod(Method m) {
Contract.Requires(m != null);
// Add in-parameters to the scope, but don't care about any duplication errors, since they have already been reported
scope.PushMarker();
if (m.IsStatic) {
scope.AllowInstance = false;
}
foreach (Formal p in m.Ins) {
scope.Push(p.Name, p);
}
// Start resolving specification...
foreach (MaybeFreeExpression e in m.Req) {
ResolveExpression(e.E, false);
Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression
if (!UnifyTypes(e.E.Type, Type.Bool)) {
Error(e.E, "Precondition must be a boolean (got {0})", e.E.Type);
}
}
foreach (FrameExpression fe in m.Mod.Expressions) {
ResolveFrameExpression(fe, "modifies");
}
foreach (Expression e in m.Decreases.Expressions) {
ResolveExpression(e, false);
// any type is fine
}
// Add out-parameters to a new scope that will also include the outermost-level locals of the body
// Don't care about any duplication errors among the out-parameters, since they have already been reported
scope.PushMarker();
foreach (Formal p in m.Outs) {
scope.Push(p.Name, p);
}
// attributes are allowed to mention both in- and out-parameters
ResolveAttributes(m.Attributes, false);
// ... continue resolving specification
foreach (MaybeFreeExpression e in m.Ens) {
ResolveExpression(e.E, true);
Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression
if (!UnifyTypes(e.E.Type, Type.Bool)) {
Error(e.E, "Postcondition must be a boolean (got {0})", e.E.Type);
}
}
// Resolve body
if (m.Body != null) {
ResolveBlockStatement(m.Body, m.IsGhost, m);
}
scope.PopMarker(); // for the out-parameters and outermost-level locals
scope.PopMarker(); // for the in-parameters
}
void ResolveCtorSignature(DatatypeCtor ctor, List dtTypeArguments) {
Contract.Requires(ctor != null);
Contract.Requires(dtTypeArguments != null);
foreach (Formal p in ctor.Formals) {
ResolveType(p.tok, p.Type, dtTypeArguments, false);
}
}
///
/// If ResolveType encounters a type "T" that takes type arguments but wasn't given any, then:
/// * If "defaultTypeArguments" is non-null and "defaultTypeArgument.Count" equals the number
/// of type arguments that "T" expects, then use these default type arguments as "T"'s arguments.
/// * If "allowAutoTypeArguments" is true, then infer "T"'s arguments.
/// * If "defaultTypeArguments" is non-null AND "allowAutoTypeArguments" is true, then enough
/// type parameters will be added to "defaultTypeArguments" to have at least as many type
/// parameters as "T" expects, and then a prefix of the "defaultTypeArguments" will be supplied
/// as arguments to "T".
///
public void ResolveType(IToken tok, Type type, List defaultTypeArguments, bool allowAutoTypeArguments) {
Contract.Requires(tok != null);
Contract.Requires(type != null);
if (type is BasicType) {
// nothing to resolve
} else if (type is MapType) {
MapType mt = (MapType)type;
ResolveType(tok, mt.Domain, defaultTypeArguments, allowAutoTypeArguments);
ResolveType(tok, mt.Range, defaultTypeArguments, allowAutoTypeArguments);
if (mt.Domain.IsSubrangeType || mt.Range.IsSubrangeType) {
Error(tok, "sorry, cannot instantiate collection type with a subrange type");
}
} else if (type is CollectionType) {
var t = (CollectionType)type;
var argType = t.Arg;
ResolveType(tok, argType, defaultTypeArguments, allowAutoTypeArguments);
if (argType.IsSubrangeType) {
Error(tok, "sorry, cannot instantiate collection type with a subrange type");
}
} else if (type is UserDefinedType) {
UserDefinedType t = (UserDefinedType)type;
foreach (Type tt in t.TypeArgs) {
ResolveType(t.tok, tt, defaultTypeArguments, allowAutoTypeArguments);
if (tt.IsSubrangeType) {
Error(t.tok, "sorry, cannot instantiate type parameter with a subrange type");
}
}
TypeParameter tp = t.Path.Count == 0 ? allTypeParameters.Find(t.Name) : null;
if (tp != null) {
if (t.TypeArgs.Count == 0) {
t.ResolvedParam = tp;
} else {
Error(t.tok, "Type parameter expects no type arguments: {0}", t.Name);
}
} else if (t.ResolvedClass == null) { // this test is because 'array' is already resolved; TODO: an alternative would be to pre-populate 'classes' with built-in references types like 'array' (and perhaps in the future 'string')
TopLevelDecl d = null;
int j = 0;
var sig = moduleInfo;
while (j < t.Path.Count) {
if (sig.FindSubmodule(t.Path[j].val, out sig)) {
j++;
sig = GetSignature(sig);
} else {
Error(t.Path[j], ModuleNotFoundErrorMessage(j, t.Path));
break;
}
}
if (j == t.Path.Count) {
if (!sig.TopLevels.TryGetValue(t.Name, out d)) {
if (j == 0)
Error(t.tok, "Undeclared top-level type or type parameter: {0} (did you forget to qualify a name?)", t.Name);
else
Error(t.tok, "Undeclared type {0} in module {1}", t.Name, t.Path[t.Path.Count - 1].val);
}
} else {
// error has already been reported
}
if (d == null) {
// error has been reported above
} else if (d is AmbiguousTopLevelDecl) {
Error(t.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", t.Name, ((AmbiguousTopLevelDecl)d).ModuleNames());
} else if (d is ArbitraryTypeDecl) {
t.ResolvedParam = ((ArbitraryTypeDecl)d).TheType; // resolve like a type parameter
} else {
// d is a class or datatype, and it may have type parameters
t.ResolvedClass = d;
if (d.TypeArgs.Count != t.TypeArgs.Count && t.TypeArgs.Count == 0) {
if (allowAutoTypeArguments && defaultTypeArguments == null) {
// add type arguments that will be inferred
for (int i = 0; i < d.TypeArgs.Count; i++) {
t.TypeArgs.Add(new InferredTypeProxy());
}
} else if (defaultTypeArguments != null) {
// add specific type arguments, drawn from defaultTypeArguments (which may have to be extended)
if (allowAutoTypeArguments) {
// add to defaultTypeArguments the necessary number of arguments
for (int i = defaultTypeArguments.Count; i < d.TypeArgs.Count; i++) {
defaultTypeArguments.Add(new TypeParameter(t.tok, "_T" + i));
}
}
if (allowAutoTypeArguments || d.TypeArgs.Count == defaultTypeArguments.Count) {
Contract.Assert(d.TypeArgs.Count <= defaultTypeArguments.Count);
// automatically supply a prefix of the arguments from defaultTypeArguments
for (int i = 0; i < d.TypeArgs.Count; i++) {
var typeArg = new UserDefinedType(t.tok, defaultTypeArguments[i].Name, new List(), null);
typeArg.ResolvedParam = defaultTypeArguments[i]; // resolve "typeArg" here
t.TypeArgs.Add(typeArg);
}
}
}
}
// defaults and auto have been applied; check if we now have the right number of arguments
if (d.TypeArgs.Count != t.TypeArgs.Count) {
Error(t.tok, "Wrong number of type arguments ({0} instead of {1}) passed to class/datatype: {2}", t.TypeArgs.Count, d.TypeArgs.Count, t.Name);
}
}
}
} else if (type is TypeProxy) {
TypeProxy t = (TypeProxy)type;
if (t.T != null) {
ResolveType(tok, t.T, defaultTypeArguments, allowAutoTypeArguments);
}
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
}
}
public bool UnifyTypes(Type a, Type b) {
Contract.Requires(a != null);
Contract.Requires(b != null);
while (a is TypeProxy) {
TypeProxy proxy = (TypeProxy)a;
if (proxy.T == null) {
// merge a and b; to avoid cycles, first get to the bottom of b
while (b is TypeProxy && ((TypeProxy)b).T != null) {
b = ((TypeProxy)b).T;
}
return AssignProxy(proxy, b);
} else {
a = proxy.T;
}
}
while (b is TypeProxy) {
TypeProxy proxy = (TypeProxy)b;
if (proxy.T == null) {
// merge a and b (we have already got to the bottom of a)
return AssignProxy(proxy, a);
} else {
b = proxy.T;
}
}
#if !NO_CHEAP_OBJECT_WORKAROUND
if (a is ObjectType || b is ObjectType) { // TODO: remove this temporary hack
var other = a is ObjectType ? b : a;
if (other is BoolType || other is IntType || other is SetType || other is SeqType || other.IsDatatype) {
return false;
}
// allow anything else with object; this is BOGUS
return true;
}
#endif
// Now, a and b are non-proxies and stand for the same things as the original a and b, respectively.
if (a is BoolType) {
return b is BoolType;
} else if (a is IntType) {
return b is IntType;
} else if (a is ObjectType) {
return b is ObjectType;
} else if (a is SetType) {
return b is SetType && UnifyTypes(((SetType)a).Arg, ((SetType)b).Arg);
} else if (a is MultiSetType) {
return b is MultiSetType && UnifyTypes(((MultiSetType)a).Arg, ((MultiSetType)b).Arg);
} else if (a is MapType) {
return b is MapType && UnifyTypes(((MapType)a).Domain, ((MapType)b).Domain) && UnifyTypes(((MapType)a).Range, ((MapType)b).Range);
} else if (a is SeqType) {
return b is SeqType && UnifyTypes(((SeqType)a).Arg, ((SeqType)b).Arg);
} else if (a is UserDefinedType) {
if (!(b is UserDefinedType)) {
return false;
}
UserDefinedType aa = (UserDefinedType)a;
UserDefinedType bb = (UserDefinedType)b;
if (aa.ResolvedClass != null && aa.ResolvedClass == bb.ResolvedClass) {
// these are both resolved class/datatype types
Contract.Assert(aa.TypeArgs.Count == bb.TypeArgs.Count);
bool successSoFar = true;
for (int i = 0; i < aa.TypeArgs.Count; i++) {
if (!UnifyTypes(aa.TypeArgs[i], bb.TypeArgs[i])) {
successSoFar = false;
}
}
return successSoFar;
} else if (aa.ResolvedParam != null && aa.ResolvedParam == bb.ResolvedParam) {
// these are both resolved type parameters
Contract.Assert(aa.TypeArgs.Count == 0 && bb.TypeArgs.Count == 0);
return true;
} else {
// something is wrong; either aa or bb wasn't properly resolved, or they don't unify
return false;
}
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
}
}
bool AssignProxy(TypeProxy proxy, Type t) {
Contract.Requires(proxy != null);
Contract.Requires(t != null);
Contract.Requires(proxy.T == null);
Contract.Requires(!(t is TypeProxy) || ((TypeProxy)t).T == null);
//modifies proxy.T, ((TypeProxy)t).T; // might also change t.T if t is a proxy
Contract.Ensures(Contract.Result() == (proxy == t || proxy.T != null || (t is TypeProxy && ((TypeProxy)t).T != null)));
if (proxy == t) {
// they are already in the same equivalence class
return true;
} else if (proxy is UnrestrictedTypeProxy) {
// it's fine to redirect proxy to t (done below)
} else if (t is UnrestrictedTypeProxy) {
// merge proxy and t by redirecting t to proxy, rather than the other way around
((TypeProxy)t).T = proxy;
return true;
} else if (t is RestrictedTypeProxy) {
// Both proxy and t are restricted type proxies. To simplify unification, order proxy and t
// according to their types.
RestrictedTypeProxy r0 = (RestrictedTypeProxy)proxy;
RestrictedTypeProxy r1 = (RestrictedTypeProxy)t;
if (r0.OrderID <= r1.OrderID) {
return AssignRestrictedProxies(r0, r1);
} else {
return AssignRestrictedProxies(r1, r0);
}
// In the remaining cases, proxy is a restricted proxy and t is a non-proxy
} else if (proxy is DatatypeProxy) {
if (t.IsIndDatatype) {
// all is fine, proxy can be redirected to t
} else {
return false;
}
} else if (proxy is ObjectTypeProxy) {
if (t is ObjectType || UserDefinedType.DenotesClass(t) != null) {
// all is fine, proxy can be redirected to t
} else {
return false;
}
} else if (proxy is CollectionTypeProxy) {
CollectionTypeProxy collProxy = (CollectionTypeProxy)proxy;
if (t is CollectionType) {
if (!UnifyTypes(collProxy.Arg, ((CollectionType)t).Arg)) {
return false;
}
} else {
return false;
}
} else if (proxy is OperationTypeProxy) {
OperationTypeProxy opProxy = (OperationTypeProxy)proxy;
if (t is IntType || t is SetType || t is MultiSetType || (opProxy.AllowSeq && t is SeqType)) {
// this is the expected case
} else {
return false;
}
} else if (proxy is IndexableTypeProxy) {
IndexableTypeProxy iProxy = (IndexableTypeProxy)proxy;
if (t is SeqType) {
if (!UnifyTypes(iProxy.Arg, ((SeqType)t).Arg)) {
return false;
}
if (!UnifyTypes(iProxy.Domain, Type.Int)) {
return false;
}
} else if (t.IsArrayType && (t.AsArrayType).Dims == 1) {
Type elType = UserDefinedType.ArrayElementType(t);
if (!UnifyTypes(iProxy.Arg, elType)) {
return false;
}
if (!UnifyTypes(iProxy.Domain, Type.Int)) {
return false;
}
} else if (t is MapType) {
if (!UnifyTypes(iProxy.Arg, ((MapType)t).Range)) {
return false;
}
if (!UnifyTypes(iProxy.Domain, ((MapType)t).Domain)) {
return false;
}
} else {
return false;
}
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected proxy type
}
// do the merge, but never infer a subrange type
if (t is NatType) {
proxy.T = Type.Int;
} else {
proxy.T = t;
}
return true;
}
bool AssignRestrictedProxies(RestrictedTypeProxy a, RestrictedTypeProxy b) {
Contract.Requires(a != null);
Contract.Requires(b != null);
Contract.Requires(a != b);
Contract.Requires(a.T == null && b.T == null);
Contract.Requires(a.OrderID <= b.OrderID);
//modifies a.T, b.T;
Contract.Ensures(!Contract.Result() || a.T != null || b.T != null);
if (a is DatatypeProxy) {
if (b is DatatypeProxy) {
// all is fine
a.T = b;
return true;
} else {
return false;
}
} else if (a is ObjectTypeProxy) {
if (b is ObjectTypeProxy) {
// all is fine
a.T = b;
return true;
} else if (b is IndexableTypeProxy) {
// the intersection of ObjectTypeProxy and IndexableTypeProxy is an array type
a.T = builtIns.ArrayType(1, ((IndexableTypeProxy)b).Arg);
b.T = a.T;
return true;
} else {
return false;
}
} else if (a is CollectionTypeProxy) {
if (b is CollectionTypeProxy) {
a.T = b;
return UnifyTypes(((CollectionTypeProxy)a).Arg, ((CollectionTypeProxy)b).Arg);
} else if (b is OperationTypeProxy) {
if (((OperationTypeProxy)b).AllowSeq) {
b.T = a; // a is a stronger constraint than b
} else {
// a says set,seq and b says int,set; the intersection is set
a.T = new SetType(((CollectionTypeProxy)a).Arg);
b.T = a.T;
}
return true;
} else if (b is IndexableTypeProxy) {
CollectionTypeProxy pa = (CollectionTypeProxy)a;
IndexableTypeProxy pb = (IndexableTypeProxy)b;
// a and b could be a map or a sequence
return true;
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected restricted-proxy type
}
} else if (a is OperationTypeProxy) {
OperationTypeProxy pa = (OperationTypeProxy)a;
if (b is OperationTypeProxy) {
if (!pa.AllowSeq || ((OperationTypeProxy)b).AllowSeq) {
b.T = a;
} else {
a.T = b; // b has the stronger requirement
}
return true;
} else {
IndexableTypeProxy pb = (IndexableTypeProxy)b; // cast justification: lse we have unexpected restricted-proxy type
if (pa.AllowSeq) {
// strengthen a and b to a sequence type
b.T = new SeqType(pb.Arg);
a.T = b.T;
return true;
} else {
return false;
}
}
} else if (a is IndexableTypeProxy) {
Contract.Assert(b is IndexableTypeProxy); // else we have unexpected restricted-proxy type
a.T = b;
return UnifyTypes(((IndexableTypeProxy)a).Arg, ((IndexableTypeProxy)b).Arg);
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected restricted-proxy type
}
}
///
/// "specContextOnly" means that the statement must be erasable, that is, it should be okay to omit it
/// at run time. That means it must not have any side effects on non-ghost variables, for example.
///
public void ResolveStatement(Statement stmt, bool specContextOnly, Method method) {
Contract.Requires(stmt != null);
Contract.Requires(method != null);
if (stmt is PredicateStmt) {
PredicateStmt s = (PredicateStmt)stmt;
ResolveAttributes(s.Attributes, false);
s.IsGhost = true;
ResolveExpression(s.Expr, true);
Contract.Assert(s.Expr.Type != null); // follows from postcondition of ResolveExpression
if (!UnifyTypes(s.Expr.Type, Type.Bool)) {
Error(s.Expr, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Expr.Type);
}
} else if (stmt is PrintStmt) {
PrintStmt s = (PrintStmt)stmt;
ResolveAttributeArgs(s.Args, false, false);
if (specContextOnly) {
Error(stmt, "print statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)");
}
} else if (stmt is BreakStmt) {
var s = (BreakStmt)stmt;
if (s.TargetLabel != null) {
Statement target = labeledStatements.Find(s.TargetLabel);
if (target == null) {
Error(s, "break label is undefined or not in scope: {0}", s.TargetLabel);
} else {
s.TargetStmt = target;
bool targetIsLoop = target is WhileStmt || target is AlternativeLoopStmt;
if (specContextOnly && !s.TargetStmt.IsGhost && !inSpecOnlyContext[s.TargetStmt]) {
Error(stmt, "ghost-context break statement is not allowed to break out of non-ghost " + (targetIsLoop ? "loop" : "structure"));
}
}
} else {
if (loopStack.Count < s.BreakCount) {
Error(s, "trying to break out of more loop levels than there are enclosing loops");
} else {
Statement target = loopStack[loopStack.Count - s.BreakCount];
if (target.Labels == null) {
// make sure there is a label, because the compiler and translator will want to see a unique ID
target.Labels = new LList