Browse Source

type promotion fixes

Vladislav Folts 11 years ago
parent
commit
1ae27c1011
3 changed files with 125 additions and 70 deletions
  1. BIN
      bin/compiled.zip
  2. 67 29
      src/eberon/eberon_context.js
  3. 58 41
      test/test_unit_eberon.js

BIN
bin/compiled.zip


+ 67 - 29
src/eberon/eberon_context.js

@@ -13,6 +13,12 @@ var Symbol = require("js/Symbols.js");
 var Procedure = require("js/Procedure.js");
 var Type = require("js/Types.js");
 
+/*
+function log(s){
+    console.info(s);
+}
+*/
+
 function methodCallGenerator(context, id, type){
     return new Procedure.makeProcCallGenerator(context, id, type);
 }
@@ -23,6 +29,15 @@ function superMethodCallGenerator(context, id, type){
     return Procedure.makeProcCallGeneratorWithCustomArgs(context, id, type, args);
 }
 
+function resetPromotedTypes(types){
+    //log("reset promoted: " + types.length);
+    for(var i = 0; i < types.length; ++i){
+        var info = types[i];
+        //log("\t" + info.type.type().name + "->" + info.restore.name);
+        info.type.resetPromotion(info.restore);
+    }
+}
+
 var MethodType = Type.Procedure.extend({
     init: function EberonContext$MethodType(id, type, callGenerator){
         Type.Procedure.prototype.init.call(this);
@@ -113,22 +128,26 @@ var ResultVariable = Type.Variable.extend({
 
 var TempVariable = Type.Variable.extend({
     init: function TempVariable(type){
-        this.__originalType = type;
         this.__type = type;
-        this.__invert = false;
+        this.__invertedType = type;
     },
     type: function(){
-        return this.__invert ? this.__originalType : this.__type;
+        return this.__type;
     },
     isReadOnly: function(){return true;},
     idType: function(){return "temporary variable";},
     promoteType: function(t){
+        var result = this.__type;
         this.__type = t;
+        return result;
+    },
+    invertType: function(){
+        var type = this.__type;
+        this.__type = this.__invertedType;
+        this.__invertedType = type;
     },
-    invertType: function(){this.__invert = !this.__invert;},
-    inverted: function(){return this.__invert;},
-    resetPromotion: function(){
-        this.__type = this.__originalType;
+    resetPromotion: function(type){
+        this.__type = type;
     }
 });
 
@@ -548,9 +567,7 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
 
         // reset type promotion made in separate statement
         if (msg instanceof TransferPromotedTypesMsg){
-            var types = msg.types;
-            for(var i = 0; i < types.length; ++i)
-                types[i].resetPromotion();
+            resetPromotedTypes(msg.types);
             return;
         }
 
@@ -664,8 +681,9 @@ function resetTypePromotionMsg(){}
 function resetInvertedPromotedTypesMsg(){}
 function invertTypePromotionMsg(){}
 
-function TransferPromotedTypesMsg(types){
+function TransferPromotedTypesMsg(types, invertTypes){
     this.types = types;
+    this.invertTypes = invertTypes;
 }
 
 var RelationOps = Context.RelationOps.extend({
@@ -785,19 +803,26 @@ var While = Context.While.extend({
     }
 });
 
+//var id = 0;
+
 var TypePromotionHandler = Class.extend({
     init: function EberonContext$TypePromotionHandler(){
         this.__promotedTypes = [];
+        this.__invertPromotedTypes = [];
+        //this.__id = id++;
     },
     handleMessage: function(msg){
         if (msg instanceof PromoteTypeMsg){
             var promoted = msg.info;
-            promoted.promoteType(msg.type);
-            this.__promotedTypes.push(promoted);
+            var restore = promoted.promoteType(msg.type);
+            //log("promote: " + restore.name + "->" + msg.type.name + ", " + this.__id);
+            this.__promotedTypes.push({type: promoted, restore: restore});
             return true;
         }
         if (msg instanceof TransferPromotedTypesMsg){
+            //log("transfer, " + this.__id);
             Array.prototype.push.apply(this.__promotedTypes, msg.types);
+            Array.prototype.push.apply(this.__invertPromotedTypes, msg.invertTypes);
             return true;
         }
         switch (msg){
@@ -814,28 +839,28 @@ var TypePromotionHandler = Class.extend({
     return false;
     },
     invert: function(){
-        for(var i = 0; i < this.__promotedTypes.length; ++i)
-            this.__promotedTypes[i].invertType();
+        var all = this.__promotedTypes.concat(this.__invertPromotedTypes);
+        //log("invert: " + all.length + ", " + this.__id);
+        for(var i = 0; i < all.length; ++i){
+            //log("\t" + all[i].type.type().name + ", " + this.__id);
+            all[i].type.invertType();
+        }
+
+        var swap = this.__promotedTypes;
+        this.__promotedTypes = this.__invertPromotedTypes;
+        this.__invertPromotedTypes = swap;
     },
     reset: function(){
-        for(var i = 0; i < this.__promotedTypes.length; ++i)
-            this.__promotedTypes[i].resetPromotion();
+        resetPromotedTypes(this.__promotedTypes);
         this.__promotedTypes = [];
     },
     resetInverted: function(inverted){
-        var left = [];
-        for(var i = 0; i < this.__promotedTypes.length; ++i){
-            var t = this.__promotedTypes[i];
-            if (t.inverted())
-                t.resetPromotion();
-            else
-                left.push(t);
-        }
-        this.__promotedTypes = left;
+        resetPromotedTypes(this.__invertPromotedTypes);
+        this.__invertPromotedTypes = [];
     },
     transferTo: function(context){
-        if (this.__promotedTypes.length)
-            context.handleMessage(new TransferPromotedTypesMsg(this.__promotedTypes));
+        if (this.__promotedTypes.length || this.__invertPromotedTypes.length)
+            context.handleMessage(new TransferPromotedTypesMsg(this.__promotedTypes, this.__invertPromotedTypes));
     }
 });
 
@@ -844,9 +869,18 @@ var If = Context.If.extend({
         Context.If.prototype.init.call(this, context);
         this.__scope = undefined;
         this.__typePromotion = new TypePromotionHandler();
+        this.__typePromotions = [this.__typePromotion];
         this.__newScope();
     },
     handleMessage: function(msg){
+        /*
+        if (msg instanceof TransferPromotedTypesMsg){
+            log("suppress transfer");
+            resetPromotedTypes(msg.types);
+            return;
+        }
+        */
+
         if (this.__typePromotion.handleMessage(msg))
             return;
 
@@ -856,6 +890,8 @@ var If = Context.If.extend({
         Context.If.prototype.handleLiteral.call(this, s);
         if (s == "ELSIF" || s == "ELSE"){
             this.__typePromotion.invert();
+            this.__typePromotion = new TypePromotionHandler();
+            this.__typePromotions.push(this.__typePromotion);
             this.__newScope();
         }
     },
@@ -868,7 +904,9 @@ var If = Context.If.extend({
         this.pushScope(this.__scope);
     },
     endParse: function(){
-        this.__typePromotion.reset();
+        for(var i = 0; i < this.__typePromotions.length; ++i){
+            this.__typePromotions[i].reset();
+        }
         this.popScope();
         Context.If.prototype.endParse.call(this);
     }

+ 58 - 41
test/test_unit_eberon.js

@@ -26,22 +26,39 @@ var typePromotion = {
         grammar.declarationSequence,
         "TYPE Base = RECORD END;"
         + "Derived = RECORD (Base) flag: BOOLEAN END;"
+        + "Derived2 = RECORD (Derived) flag2: BOOLEAN END;"
         + "PDerived = POINTER TO Derived;"
+        + "PDerived2 = POINTER TO Derived2;"
         + "VAR pBase: POINTER TO Base; bVar: BOOLEAN;"
         + "PROCEDURE proc(b: BOOLEAN): BOOLEAN; RETURN b END proc;"),
-    expression: function(e){
+    __expression: function(e){
         return "PROCEDURE p(); BEGIN b <- pBase; b2 <- pBase; ASSERT(" + e + "); END p;";
     },
+    __condition: function(e){
+        return "PROCEDURE p(); BEGIN b <- pBase; b2 <- pBase; " + e + " END p;";
+    },
     passExpressions: function(){
+        return this.__pass(this.__expression.bind(this), arguments);
+    },
+    passConditions: function(){
+        return this.__pass(this.__condition.bind(this), arguments);
+    },
+    failExpressions: function(){
+        return this.__fail(this.__expression.bind(this), arguments);
+    },
+    failConditions: function(){
+        return this.__fail(this.__condition.bind(this), arguments);
+    },
+    __pass: function(make, cases){
         var result = [];
-        for(var i = 0; i < arguments.length; ++i)
-            result.push(this.expression(arguments[i]));
+        for(var i = 0; i < cases.length; ++i)
+            result.push(make(cases[i]));
         return pass.apply(this, result);
     },
-    failExpressions: function(){
+    __fail: function(make, cases){
         var result = [];
-        for(var i = 0; i < arguments.length; ++i)
-            result.push([this.expression(arguments[i]), "type 'Base' has no 'flag' field"]);
+        for(var i = 0; i < cases.length; ++i)
+            result.push([make(cases[i]), "type 'Base' has no 'flag' field"]);
         return fail.apply(this, result);
     }
 };
@@ -426,7 +443,8 @@ exports.suite = {
              "(b IS PDerived) & proc(TRUE) & b.flag",
              "(b IS PDerived) & ~proc(TRUE) & b.flag",
              "~(~(b IS PDerived)) & b.flag",
-             "~~(b IS PDerived) & b.flag"
+             "~~(b IS PDerived) & b.flag",
+             "(b IS PDerived) & ((b IS PDerived2) OR bVar) & b.flag"
              //TODO: "((b IS PDerived) = TRUE) & b.flag); END p;",
              ),
         typePromotion.failExpressions(
@@ -463,43 +481,42 @@ exports.suite = {
             )
         ),
     "type promotion in condition": testWithContext(
-        context(grammar.declarationSequence,
-                "TYPE Base = RECORD END;"
-                + "Derived = RECORD (Base) flag: BOOLEAN END;"
-                + "Derived2 = RECORD (Derived) flag2: BOOLEAN END;"
-                + "PDerived = POINTER TO Derived;"
-                + "PDerived2 = POINTER TO Derived2;"
-                + "VAR pBase: POINTER TO Base; bVar: BOOLEAN;"
-                + "PROCEDURE proc(b: BOOLEAN): BOOLEAN; RETURN b END proc;"
-               ),
-        pass("PROCEDURE p(); BEGIN b <- pBase; IF b IS PDerived THEN b.flag := FALSE; END; END p;",
-             "PROCEDURE p(); BEGIN b <- pBase; IF (b IS PDerived) & bVar THEN b.flag := FALSE; END; END p;",
-             "PROCEDURE p(); BEGIN b <- pBase; IF FALSE THEN ELSIF b IS PDerived THEN b.flag := FALSE; END; END p;",
-             "PROCEDURE p(); BEGIN b <- pBase; IF b IS PDerived THEN bVar := (b IS PDerived2) & b.flag2; b.flag := FALSE; END; END p;"
-             ),
-        fail(["PROCEDURE p(); BEGIN b <- pBase; IF (b IS PDerived) OR bVar THEN b.flag := FALSE; END; END p;",
-              "type 'Base' has no 'flag' field"],
-             ["PROCEDURE p(); BEGIN b <- pBase; IF b IS PDerived THEN END; b.flag := FALSE; END p;",
-              "type 'Base' has no 'flag' field"],
-             ["PROCEDURE p(); BEGIN b <- pBase; IF b IS PDerived THEN ELSE b.flag := FALSE; END; END p;",
-              "type 'Base' has no 'flag' field"],
-             ["PROCEDURE p(); BEGIN b <- pBase; IF b IS PDerived THEN ELSIF TRUE THEN b.flag := FALSE; END; END p;",
-              "type 'Base' has no 'flag' field"],
-             ["PROCEDURE p(); BEGIN b <- pBase; IF b IS PDerived THEN bVar := b IS PDerived; b.flag := FALSE; END; END p;",
-              "invalid type test: 'Derived' is not an extension of 'Derived'"]
+        typePromotion.context,
+        typePromotion.passConditions(
+            "IF b IS PDerived THEN b.flag := FALSE; END;",
+            "IF (b IS PDerived) & bVar THEN b.flag := FALSE; END;",
+            "IF FALSE THEN ELSIF b IS PDerived THEN b.flag := FALSE; END;",
+            "IF b IS PDerived THEN bVar := (b IS PDerived2) & b.flag2; b.flag := FALSE; END;"
+            ),
+        typePromotion.failConditions(
+            "IF (b IS PDerived) OR bVar THEN b.flag := FALSE; END",
+            "IF b IS PDerived THEN END; b.flag := FALSE",
+            "IF ~(b IS PDerived) THEN END; b.flag := FALSE",
+            "IF ~(b IS PDerived) THEN ELSIF bVar THEN END; b.flag := FALSE",
+            "IF ~(b IS PDerived) THEN ELSIF bVar THEN ELSE END; b.flag := FALSE",
+            "IF bVar THEN ELSIF b IS PDerived THEN ELSE END; b.flag := FALSE",
+            "IF b IS PDerived THEN ELSE b.flag := FALSE; END",
+            "IF b IS PDerived THEN ELSIF TRUE THEN b.flag := FALSE; END"
+             //TODO: separate test
+             //["PROCEDURE p(); BEGIN b <- pBase; IF b IS PDerived THEN bVar := b IS PDerived; b.flag := FALSE; END; END p;",
+             // "invalid type test: 'Derived' is not an extension of 'Derived'"]
              )
         ),
     "invert type promotion in condition": testWithContext(
-        context(grammar.declarationSequence,
-                "TYPE Base = RECORD END;"
-                + "Derived = RECORD (Base) flag: BOOLEAN END;"
-                + "PDerived = POINTER TO Derived;"
-                + "VAR pBase: POINTER TO Base; bVar: BOOLEAN;"
-                + "PROCEDURE proc(b: BOOLEAN): BOOLEAN; RETURN b END proc;"
-               ),
-        pass("PROCEDURE p(); BEGIN b <- pBase; IF ~(b IS PDerived) THEN ELSE b.flag := FALSE; END; END p;"
-             ),
-        fail()
+        typePromotion.context,
+        typePromotion.passConditions(
+            "IF ~(b IS PDerived) THEN ELSE b.flag := FALSE; END;",
+            "IF ~(b IS PDerived) THEN ELSIF bVar THEN b.flag := FALSE; ELSE b.flag := FALSE; END;",
+            "IF ~(b IS PDerived) THEN ELSIF ~(b2 IS PDerived) THEN b.flag := FALSE; ELSE b.flag := FALSE; b2.flag := FALSE; END;",
+            "IF ~(b IS PDerived) OR bVar THEN ELSE b.flag := FALSE; END;",
+            "IF ~(b IS PDerived) OR b.flag THEN ELSE b.flag := FALSE; END;",
+            "IF ~(b IS PDerived) THEN bVar := b IS PDerived; ELSE b.flag := FALSE; END;",
+            "IF ~(b IS PDerived) THEN ASSERT((b IS PDerived) & b.flag); ELSE b.flag := FALSE; END;"
+            ),
+        typePromotion.failConditions(
+            "IF ~(b IS PDerived) & bVar THEN ELSE b.flag := FALSE; END; END p;",
+            "IF ~(b IS PDerived) THEN ELSIF ~(b2 IS PDerived) THEN b2.flag := FALSE; END;"
+            )
         )
     }
 };