//----------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All Rights Reserved. // //----------------------------------------------------------------------------- // This file contains the transformations that are applied to a module that is // constructed as a refinement of another. It is invoked during program resolution, // so the transformation is done syntactically. Upon return from the RefinementTransformer, // the caller is expected to resolve the resulting module. // // As for now (and perhaps this is always the right thing to do), attributes do // not survive the transformation. //----------------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Numerics; using System.Diagnostics.Contracts; using IToken = Microsoft.Boogie.IToken; namespace Microsoft.Dafny { public class RefinementToken : TokenWrapper { public readonly ModuleDefinition InheritingModule; public RefinementToken(IToken tok, ModuleDefinition m) : base(tok) { Contract.Requires(tok != null); Contract.Requires(m != null); this.InheritingModule = m; } public static bool IsInherited(IToken tok, ModuleDefinition m) { while (tok is NestedToken) { var n = (NestedToken)tok; // check Outer var r = n.Outer as RefinementToken; if (r == null || r.InheritingModule != m) { return false; } // continue to check Inner tok = n.Inner; } var rtok = tok as RefinementToken; return rtok != null && rtok.InheritingModule == m; } public override string filename { get { return WrappedToken.filename + "[" + InheritingModule.Name + "]"; } set { throw new NotSupportedException(); } } } public class RefinementTransformer : IRewriter { ResolutionErrorReporter reporter; public RefinementTransformer(ResolutionErrorReporter reporter) { Contract.Requires(reporter != null); this.reporter = reporter; } private ModuleDefinition moduleUnderConstruction; // non-null for the duration of Construct calls private Queue postTasks = new Queue(); // empty whenever moduleUnderConstruction==null, these tasks are for the post-resolve phase of module moduleUnderConstruction public void PreResolve(ModuleDefinition m) { if (m.RefinementBase == null) return; if (moduleUnderConstruction != null) { postTasks.Clear(); } moduleUnderConstruction = m; var prev = m.RefinementBase; // Create a simple name-to-decl dictionary. Ignore any duplicates at this time. var declaredNames = new Dictionary(); for (int i = 0; i < m.TopLevelDecls.Count; i++) { var d = m.TopLevelDecls[i]; if (!declaredNames.ContainsKey(d.Name)) { declaredNames.Add(d.Name, i); } } // Merge the declarations of prev into the declarations of m foreach (var d in prev.TopLevelDecls) { int index; if (!declaredNames.TryGetValue(d.Name, out index)) { m.TopLevelDecls.Add(CloneDeclaration(d, m)); } else { var nw = m.TopLevelDecls[index]; if (d is ModuleDecl) { if (!(nw is ModuleDecl)) { reporter.Error(nw, "a module ({0}) must refine another module", nw.Name); } else if (!(d is AbstractModuleDecl)) { reporter.Error(nw, "a module ({0}) can only refine abstract modules", nw.Name); } else { ModuleSignature original = ((AbstractModuleDecl)d).OriginalSignature; ModuleSignature derived = null; if (nw is AliasModuleDecl) { derived = ((AliasModuleDecl)nw).Signature; } else if (nw is AbstractModuleDecl) { derived = ((AbstractModuleDecl)nw).Signature; } else { reporter.Error(nw, "a module ({0}) can only be refined by alias or abstract modules", d.Name); } if (derived != null) { // check that the new module refines the previous declaration while (derived != null) { if (derived == original) break; derived = derived.Refines; } if (derived != original) { reporter.Error(nw, "a module ({0}) can only be replaced by a refinement of the original module", d.Name); } } } } else if (d is ArbitraryTypeDecl) { if (nw is ModuleDecl) { reporter.Error(nw, "a module ({0}) must refine another module", nw.Name); } else { bool dDemandsEqualitySupport = ((ArbitraryTypeDecl)d).MustSupportEquality; if (nw is ArbitraryTypeDecl) { if (dDemandsEqualitySupport != ((ArbitraryTypeDecl)nw).MustSupportEquality) { reporter.Error(nw, "type declaration '{0}' is not allowed to change the requirement of supporting equality", nw.Name); } } else if (dDemandsEqualitySupport) { if (nw is ClassDecl) { // fine, as long as "nw" does not take any type parameters if (nw.TypeArgs.Count != 0) { reporter.Error(nw, "arbitrary type '{0}' is not allowed to be replaced by a class that takes type parameters", nw.Name); } } else if (nw is CoDatatypeDecl) { reporter.Error(nw, "a type declaration that requires equality support cannot be replaced by a codatatype"); } else { Contract.Assert(nw is IndDatatypeDecl); if (nw.TypeArgs.Count != 0) { reporter.Error(nw, "arbitrary type '{0}' is not allowed to be replaced by a datatype that takes type parameters", nw.Name); } else { // Here, we need to figure out if the new type supports equality. But we won't know about that until resolution has // taken place, so we defer it until the PostResolve phase. var udt = new UserDefinedType(nw.tok, nw.Name, nw, new List()); postTasks.Enqueue(delegate() { if (!udt.SupportsEquality) { reporter.Error(udt.tok, "datatype '{0}' is used to refine an arbitrary type with equality support, but '{0}' does not support equality", udt.Name); } }); } } } } } else if (nw is ArbitraryTypeDecl) { reporter.Error(nw, "an arbitrary type declaration ({0}) in a refining module cannot replace a more specific type declaration in the refinement base", nw.Name); } else if (nw is DatatypeDecl) { reporter.Error(nw, "a datatype declaration ({0}) in a refinement module can only replace an arbitrary-type declaration", nw.Name); } else { Contract.Assert(nw is ClassDecl); if (d is DatatypeDecl) { reporter.Error(nw, "a class declaration ({0}) in a refining module cannot replace a datatype declaration in the refinement base", nw.Name); } else { m.TopLevelDecls[index] = MergeClass((ClassDecl)nw, (ClassDecl)d); } } } } Contract.Assert(moduleUnderConstruction == m); // this should be as it was set earlier in this method } public void PostResolve(ModuleDefinition m) { if (m == moduleUnderConstruction) { while (this.postTasks.Count != 0) { var a = postTasks.Dequeue(); a(); } } else { postTasks.Clear(); } moduleUnderConstruction = null; } IToken Tok(IToken tok) { if (moduleUnderConstruction == null) { return tok; } else { return new RefinementToken(tok, moduleUnderConstruction); } } // -------------------------------------------------- Cloning --------------------------------------------------------------- // Clone a toplevel, specifying that its parent module should be m (i.e. it will be added to m.TopLevelDecls). TopLevelDecl CloneDeclaration(TopLevelDecl d, ModuleDefinition m) { Contract.Requires(d != null); Contract.Requires(m != null); if (d is ArbitraryTypeDecl) { var dd = (ArbitraryTypeDecl)d; return new ArbitraryTypeDecl(Tok(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(Tok(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(Tok(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); var cl = new ClassDecl(Tok(dd.tok), dd.Name, m, tps, mm, null); return cl; } 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); alias.ModuleReference = a.ModuleReference; alias.Signature = a.Signature; return alias; } else if (d is AbstractModuleDecl) { var a = (AbstractModuleDecl)d; var abs = new AbstractModuleDecl(a.Path, a.tok, m); abs.Signature = a.Signature; abs.OriginalSignature = a.OriginalSignature; return abs; } else { Contract.Assert(false); // unexpected declaration return null; // to please compiler } } else { Contract.Assert(false); // unexpected declaration return null; // to please compiler } } DatatypeCtor CloneCtor(DatatypeCtor ct) { return new DatatypeCtor(Tok(ct.tok), ct.Name, ct.Formals.ConvertAll(CloneFormal), null); } TypeParameter CloneTypeParam(TypeParameter tp) { return new TypeParameter(Tok(tp.tok), tp.Name, tp.EqualitySupport); } MemberDecl CloneMember(MemberDecl member) { if (member is Field) { var f = (Field)member; return new Field(Tok(f.tok), f.Name, f.IsGhost, f.IsMutable, CloneType(f.Type), null); } else if (member is Function) { var f = (Function)member; return CloneFunction(Tok(f.tok), f, f.IsGhost, null, null, null); } else { var m = (Method)member; return CloneMethod(m, null, null); } } 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(Tok(tt.tok), tt.Name, tt.TypeArgs.ConvertAll(CloneType), tt.Path.ConvertAll(x => Tok(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 } } Formal CloneFormal(Formal formal) { return new Formal(Tok(formal.tok), formal.Name, CloneType(formal.Type), formal.InParam, formal.IsGhost); } BoundVar CloneBoundVar(BoundVar bv) { return new BoundVar(Tok(bv.tok), bv.Name, CloneType(bv.Type)); } 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(CloneExpr(frame.E), frame.FieldName); } Attributes.Argument CloneAttrArg(Attributes.Argument aa) { if (aa.E != null) { return new Attributes.Argument(Tok(aa.Tok), CloneExpr(aa.E)); } else { return new Attributes.Argument(Tok(aa.Tok), aa.S); } } MaybeFreeExpression CloneMayBeFreeExpr(MaybeFreeExpression expr) { return new MaybeFreeExpression(CloneExpr(expr.E), expr.IsFree); } 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(Tok(e.tok)); } else if (e.Value is bool) { return new LiteralExpr(Tok(e.tok), (bool)e.Value); } else { return new LiteralExpr(Tok(e.tok), (BigInteger)e.Value); } } else if (expr is ThisExpr) { if (expr is ImplicitThisExpr) { return new ImplicitThisExpr(Tok(expr.tok)); } else { return new ThisExpr(Tok(expr.tok)); } } else if (expr is IdentifierExpr) { var e = (IdentifierExpr)expr; return new IdentifierExpr(Tok(e.tok), e.Name); } else if (expr is DatatypeValue) { var e = (DatatypeValue)expr; return new DatatypeValue(Tok(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(Tok(e.tok), e.Elements.ConvertAll(CloneExpr)); } else if (expr is MultiSetDisplayExpr) { return new MultiSetDisplayExpr(Tok(e.tok), e.Elements.ConvertAll(CloneExpr)); } else { Contract.Assert(expr is SeqDisplayExpr); return new SeqDisplayExpr(Tok(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(Tok(expr.tok), pp); } else if (expr is ExprDotName) { var e = (ExprDotName)expr; return new ExprDotName(Tok(e.tok), CloneExpr(e.Obj), e.SuffixName); } else if (expr is FieldSelectExpr) { var e = (FieldSelectExpr)expr; return new FieldSelectExpr(Tok(e.tok), CloneExpr(e.Obj), e.FieldName); } else if (expr is SeqSelectExpr) { var e = (SeqSelectExpr)expr; return new SeqSelectExpr(Tok(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(Tok(e.tok), CloneExpr(e.Array), e.Indices.ConvertAll(CloneExpr)); } else if (expr is SeqUpdateExpr) { var e = (SeqUpdateExpr)expr; return new SeqUpdateExpr(Tok(e.tok), CloneExpr(e.Seq), CloneExpr(e.Index), CloneExpr(e.Value)); } else if (expr is FunctionCallExpr) { var e = (FunctionCallExpr)expr; return new FunctionCallExpr(Tok(e.tok), e.Name, CloneExpr(e.Receiver), e.OpenParen == null ? null : Tok(e.OpenParen), e.Args.ConvertAll(CloneExpr)); } else if (expr is OldExpr) { var e = (OldExpr)expr; return new OldExpr(Tok(e.tok), CloneExpr(e.E)); } else if (expr is MultiSetFormingExpr) { var e = (MultiSetFormingExpr)expr; return new MultiSetFormingExpr(Tok(e.tok), CloneExpr(e.E)); } else if (expr is FreshExpr) { var e = (FreshExpr)expr; return new FreshExpr(Tok(e.tok), CloneExpr(e.E)); } else if (expr is AllocatedExpr) { var e = (AllocatedExpr)expr; return new AllocatedExpr(Tok(e.tok), CloneExpr(e.E)); } else if (expr is UnaryExpr) { var e = (UnaryExpr)expr; return new UnaryExpr(Tok(e.tok), e.Op, CloneExpr(e.E)); } else if (expr is BinaryExpr) { var e = (BinaryExpr)expr; return new BinaryExpr(Tok(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(Tok(e.tok), e.Vars.ConvertAll(CloneBoundVar), e.RHSs.ConvertAll(CloneExpr), CloneExpr(e.Body)); } else if (expr is NamedExpr) { var e = (NamedExpr) expr; return new NamedExpr(Tok(e.tok), e.Name, CloneExpr(e.Body)); } else if (expr is ComprehensionExpr) { var e = (ComprehensionExpr)expr; var tk = Tok(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(Tok(expr.tok)); } else if (expr is PredicateExpr) { var e = (PredicateExpr)expr; if (e is AssertExpr) { return new AssertExpr(Tok(e.tok), CloneExpr(e.Guard), CloneExpr(e.Body)); } else { Contract.Assert(e is AssumeExpr); return new AssumeExpr(Tok(e.tok), CloneExpr(e.Guard), CloneExpr(e.Body)); } } else if (expr is ITEExpr) { var e = (ITEExpr)expr; return new ITEExpr(Tok(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 => Tok(tk)), e.OpenParen == null ? null : Tok(e.OpenParen), aa); } else if (expr is MatchExpr) { var e = (MatchExpr)expr; return new MatchExpr(Tok(e.tok), CloneExpr(e.Source), e.Cases.ConvertAll(c => new MatchCaseExpr(Tok(c.tok), c.Id, c.Arguments.ConvertAll(CloneBoundVar), CloneExpr(c.Body)))); } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression } } AssignmentRhs CloneRHS(AssignmentRhs rhs) { if (rhs is ExprRhs) { var r = (ExprRhs)rhs; return new ExprRhs(CloneExpr(r.Expr)); } else if (rhs is HavocRhs) { return new HavocRhs(Tok(rhs.Tok)); } else { var r = (TypeRhs)rhs; if (r.ArrayDimensions != null) { return new TypeRhs(Tok(r.Tok), CloneType(r.EType), r.ArrayDimensions.ConvertAll(CloneExpr)); } else if (r.InitCall != null) { return new TypeRhs(Tok(r.Tok), CloneType(r.EType), (CallStmt)CloneStmt(r.InitCall)); } else { return new TypeRhs(Tok(r.Tok), CloneType(r.EType)); } } } BlockStmt CloneBlockStmt(BlockStmt stmt) { if (stmt == null) { return null; } else { return new BlockStmt(Tok(stmt.Tok), stmt.Body.ConvertAll(CloneStmt)); } } Statement CloneStmt(Statement stmt) { if (stmt == null) { return null; } Statement r; if (stmt is AssertStmt) { var s = (AssertStmt)stmt; r = new AssertStmt(Tok(s.Tok), CloneExpr(s.Expr), null); } else if (stmt is AssumeStmt) { var s = (AssumeStmt)stmt; r = new AssumeStmt(Tok(s.Tok), CloneExpr(s.Expr), null); } else if (stmt is PrintStmt) { var s = (PrintStmt)stmt; r = new PrintStmt(Tok(s.Tok), s.Args.ConvertAll(CloneAttrArg)); } else if (stmt is BreakStmt) { var s = (BreakStmt)stmt; if (s.TargetLabel != null) { r = new BreakStmt(Tok(s.Tok), s.TargetLabel); } else { r = new BreakStmt(Tok(s.Tok), s.BreakCount); } } else if (stmt is ReturnStmt) { var s = (ReturnStmt)stmt; r = new ReturnStmt(Tok(s.Tok), s.rhss == null ? null : s.rhss.ConvertAll(CloneRHS)); } else if (stmt is AssignStmt) { var s = (AssignStmt)stmt; r = new AssignStmt(Tok(s.Tok), CloneExpr(s.Lhs), CloneRHS(s.Rhs)); } else if (stmt is VarDecl) { var s = (VarDecl)stmt; r = new VarDecl(Tok(s.Tok), s.Name, CloneType(s.OptionalType), s.IsGhost); } else if (stmt is CallStmt) { var s = (CallStmt)stmt; r = new CallStmt(Tok(s.Tok), s.Lhs.ConvertAll(CloneExpr), CloneExpr(s.Receiver), s.MethodName, s.Args.ConvertAll(CloneExpr)); } else if (stmt is BlockStmt) { r = CloneBlockStmt((BlockStmt)stmt); } else if (stmt is IfStmt) { var s = (IfStmt)stmt; r = new IfStmt(Tok(s.Tok), CloneExpr(s.Guard), CloneBlockStmt(s.Thn), CloneStmt(s.Els)); } else if (stmt is AlternativeStmt) { var s = (AlternativeStmt)stmt; r = new AlternativeStmt(Tok(s.Tok), s.Alternatives.ConvertAll(CloneGuardedAlternative)); } else if (stmt is WhileStmt) { var s = (WhileStmt)stmt; r = new WhileStmt(Tok(s.Tok), CloneExpr(s.Guard), s.Invariants.ConvertAll(CloneMayBeFreeExpr), CloneSpecExpr(s.Decreases), CloneSpecFrameExpr(s.Mod), CloneBlockStmt(s.Body)); } else if (stmt is AlternativeLoopStmt) { var s = (AlternativeLoopStmt)stmt; r = new AlternativeLoopStmt(Tok(s.Tok), s.Invariants.ConvertAll(CloneMayBeFreeExpr), CloneSpecExpr(s.Decreases), CloneSpecFrameExpr(s.Mod), s.Alternatives.ConvertAll(CloneGuardedAlternative)); } else if (stmt is ParallelStmt) { var s = (ParallelStmt)stmt; r = new ParallelStmt(Tok(s.Tok), s.BoundVars.ConvertAll(CloneBoundVar), null, CloneExpr(s.Range), s.Ens.ConvertAll(CloneMayBeFreeExpr), CloneStmt(s.Body)); } else if (stmt is MatchStmt) { var s = (MatchStmt)stmt; r = new MatchStmt(Tok(s.Tok), CloneExpr(s.Source), s.Cases.ConvertAll(c => new MatchCaseStmt(Tok(c.tok), c.Id, c.Arguments.ConvertAll(CloneBoundVar), c.Body.ConvertAll(CloneStmt)))); } else if (stmt is AssignSuchThatStmt) { var s = (AssignSuchThatStmt)stmt; r = new AssignSuchThatStmt(Tok(s.Tok), s.Lhss.ConvertAll(CloneExpr), CloneExpr(s.Expr), s.AssumeToken == null ? null : Tok(s.AssumeToken)); } else if (stmt is UpdateStmt) { var s = (UpdateStmt)stmt; r = new UpdateStmt(Tok(s.Tok), s.Lhss.ConvertAll(CloneExpr), s.Rhss.ConvertAll(CloneRHS), s.CanMutateKnownState); } else if (stmt is VarDeclStmt) { var s = (VarDeclStmt)stmt; r = new VarDeclStmt(Tok(s.Tok), s.Lhss.ConvertAll(c => (VarDecl)CloneStmt(c)), (ConcreteUpdateStatement)CloneStmt(s.Update)); } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement } // add labels to the cloned statement AddStmtLabels(r, stmt.Labels); return r; } void AddStmtLabels(Statement s, LList