Browse Source

record with constructor with parameters cannot be used in static array

Vladislav Folts 10 years ago
parent
commit
fd1f124fd4

BIN
bin/compiled.zip


+ 7 - 5
src/context.js

@@ -751,8 +751,8 @@ exports.ArrayDecl = HandleSymbolAsType.extend({
             return rtl.makeCharArray(dimensions);
 
         var initializer = type instanceof Type.Array || type instanceof Type.Record
-            ? "function(){return " + type.initializer(this, false, "") + ";}"
-            : type.initializer(this, false, "");
+            ? "function(){return " + type.initializer(this) + ";}"
+            : type.initializer(this);
         return rtl.makeArray(dimensions + ", " + initializer);
     },
     _makeType: function(elementsType, init, length){
@@ -1620,6 +1620,9 @@ exports.VariableDeclaration = HandleSymbolAsType.extend({
             throw new Errors.Error("type '" + msg.id + "' was not declared");
         return HandleSymbolAsType.prototype.handleMessage.call(this, msg);
     },
+    _initCode: function(){
+        return this.__type.initializer(this);
+    },
     endParse: function(){
         var v = Type.makeVariable(this.__type, false);
         var idents = this.__idents;
@@ -1630,8 +1633,7 @@ exports.VariableDeclaration = HandleSymbolAsType.extend({
             if (id.exported())
                 this.checkExport(varName);
             this.currentScope().addSymbol(Symbol.makeSymbol(varName, v), id.exported());
-            var t = v.type();
-            gen.write("var " + varName + " = " + t.initializer(this, false, "") + ";");
+            gen.write("var " + varName + " = " + this._initCode() + ";");
         }
 
         gen.write("\n");
@@ -1769,7 +1771,7 @@ exports.RecordDecl = ChainedContext.extend({
         var ownFields = Type.recordOwnFields(this.__type);
         for(var f in ownFields){
             var fieldType = ownFields[f].type();
-            result += "this." + Type.mangleField(f, fieldType) + " = " + fieldType.initializer(this, false, "") + ";\n";
+            result += "this." + Type.mangleField(f, fieldType) + " = " + fieldType.initializer(this) + ";\n";
         }
         return result;
     },

+ 19 - 9
src/eberon/EberonConstructor.ob

@@ -3,6 +3,7 @@ IMPORT Code, EberonRecord, Errors, LanguageContext, Operator, Procedure, Stream,
 TYPE
     ConstructorCall = RECORD(Procedure.StdCall)
         recordType: EberonRecord.PRecord;
+        resultType: Types.PType;
     END;
     PConstructorCall = POINTER TO ConstructorCall;
 
@@ -30,7 +31,7 @@ END;
 PROCEDURE ConstructorCall.make(args: ARRAY OF Code.PExpression; cx: LanguageContext.PType): Code.PExpression;
 BEGIN
     argCode <- checkArgs(SELF, args, cx);
-    RETURN Code.makeSimpleExpression(SELF.recordType.initializer(cx^, FALSE, argCode), SELF.recordType);
+    RETURN Code.makeSimpleExpression(Types.recordInitializer(cx^, SELF.recordType^, argCode), SELF.resultType);
 END;
 
 PROCEDURE BaseConstructorCall.make(args: ARRAY OF Code.PExpression; cx: LanguageContext.PType): Code.PExpression;
@@ -52,14 +53,16 @@ BEGIN
 END;
 
 PROCEDURE makeCallGenerator(
-    type: EberonRecord.PRecord; 
+    recordType: EberonRecord.PRecord; 
+    resultType: Types.PType;
     cx: LanguageContext.PType;
     call: PConstructorCall
     ): Procedure.PCallGenerator;
 BEGIN
     Procedure.initStdCall(call);
-    call.recordType := type; 
-    cons <- EberonRecord.constructor(type^);
+    call.recordType := recordType; 
+    call.resultType := resultType;
+    cons <- EberonRecord.constructor(recordType^);
     IF cons # NIL THEN
         call.args := cons.args();
     END;
@@ -93,14 +96,21 @@ BEGIN
 END;
 
 PROCEDURE makeConstructorCall*(
-    type: EberonRecord.PRecord; 
-    cx: LanguageContext.PType
+    typeId: Types.PTypeId;
+    cx: LanguageContext.PType;
+    forNew: BOOLEAN
     ): Procedure.PCallGenerator;
 VAR
     call: PConstructorCall;
 BEGIN
     NEW(call);
-    RETURN makeCallGenerator(type, cx, call);
+    resultType <- typeId.type();
+    recordType <- resultType(EberonRecord.PRecord); 
+    EberonRecord.ensureCanBeInstantiated(recordType, forNew);
+    IF forNew THEN
+        resultType := Types.makePointer("", typeId);
+    END;
+    RETURN makeCallGenerator(recordType, resultType, cx, call);
 END;
 
 PROCEDURE makeFieldInitCall*(
@@ -117,7 +127,7 @@ VAR
     BEGIN
         NEW(call);
         call.field := field;
-        RETURN makeCallGenerator(type, cx, call);
+        RETURN makeCallGenerator(type, type, cx, call);
     END;
 
     PROCEDURE initNonRecord(): Procedure.PCallGenerator;
@@ -147,7 +157,7 @@ VAR
     call: POINTER TO BaseConstructorCall;
 BEGIN
     NEW(call);
-    RETURN makeCallGenerator(type, cx, call);
+    RETURN makeCallGenerator(type, type, cx, call);
 END;
 
 END EberonConstructor.

+ 2 - 2
src/eberon/EberonDynamicArray.ob

@@ -46,9 +46,9 @@ BEGIN
     RETURN result
 END arrayDimensionDescription;
 
-PROCEDURE DynamicArray.initializer(cx: Context.Type; forNew: BOOLEAN; code: STRING): STRING;
+PROCEDURE DynamicArray.initializer(cx: Context.Type): STRING;
     RETURN "[]"
-END DynamicArray.initializer;
+END;
 
 PROCEDURE DynamicArray.description(): STRING;
     RETURN Types.arrayDescription(SELF, arrayDimensionDescription)

+ 31 - 16
src/eberon/EberonRecord.ob

@@ -217,21 +217,45 @@ BEGIN
     RETURN result; 
 END;
 
-PROCEDURE Record.initializer(cx: Context.Type; forNew: BOOLEAN; code: STRING): STRING;
+PROCEDURE constructor*(r: Record): Types.PDefinedProcedure;
+BEGIN
+    result <- r.customConstructor;
+    IF (result = NIL) & (r.base # NIL) THEN
+        result := constructor(r.base(PRecord)^);
+    END;
+    RETURN result;
+END;
+
+PROCEDURE hasParameterizedConstructor*(r: Record): BOOLEAN;
+BEGIN
+    c <- constructor(r);
+    RETURN (c # NIL) & (LEN(c.args()) # 0);
+END;
+
+PROCEDURE Record.initializer(cx: Context.Type): STRING;
 BEGIN
     IF SELF.finalized THEN
         ensureNonAbstract(SELF(POINTER));
+    ELSE
+        SELF.instantiated := TRUE;
+    END;
+
+    RETURN SUPER(cx);
+END;
+
+PROCEDURE ensureCanBeInstantiated*(r: PRecord; forNew: BOOLEAN);
+BEGIN
+    IF r.finalized THEN
+        ensureNonAbstract(r);
         IF ~forNew THEN
-            ensureVariableCanBeDeclared(SELF(POINTER));
+            ensureVariableCanBeDeclared(r);
         END;
     ELSE
-        SELF.instantiated := TRUE;
+        r.instantiated := TRUE;
         IF ~forNew THEN
-            SELF.declaredAsVariable := TRUE;
+            r.declaredAsVariable := TRUE;
         END;
     END;
-
-    RETURN SUPER(cx, forNew, code);
 END;
 
 PROCEDURE Record.findSymbol(id: STRING): Types.PField;
@@ -451,7 +475,7 @@ PROCEDURE genFieldInitCode(key: STRING; value: Object.PType; VAR closure: Object
             result := code;
         ELSE
             result := "this." + Types.mangleField(key, type) 
-                    + " = " + type.initializer(closure.cx^, FALSE, "");
+                    + " = " + type.initializer(closure.cx^);
         END;
         closure.code := closure.code + result + ";" + Stream.kCR;
     END;
@@ -479,13 +503,4 @@ BEGIN
     RETURN result;
 END;
 
-PROCEDURE constructor*(r: Record): Types.PDefinedProcedure;
-BEGIN
-    result <- r.customConstructor;
-    IF (result = NIL) & (r.base # NIL) THEN
-        result := constructor(r.base(PRecord)^);
-    END;
-    RETURN result;
-END;
-
 END EberonRecord.

+ 32 - 14
src/eberon/eberon_context.js

@@ -166,14 +166,14 @@ var Identdef = Context.Identdef.extend({
     }
 });
 
-function makeConstructorCall(context, type, call){
+function makeContextCall(context, call){
     var l = context.language();
     var cx = {
         types: l.types, 
         rtl: l.rtl, 
         qualifyScope: context.qualifyScope.bind(context)
         };
-    return call(type, cx);
+    return call(cx);
     }
 
 function OperatorNewMsg(e){
@@ -244,7 +244,10 @@ var Designator = Context.Designator.extend({
     __beginCall: function(){
         var type = this._currentType();
         if (type instanceof Type.TypeId && type.type() instanceof Type.Record){
-            this.__procCall = makeConstructorCall(this, type.type(), EberonConstructor.makeConstructorCall);
+            this.__procCall = makeContextCall(
+                this, 
+                function(cx){ return EberonConstructor.makeConstructorCall(type, cx, false); }
+                );
             this._discardCode();
         }
         else
@@ -282,7 +285,10 @@ var OperatorNew = Context.Chained.extend({
     },
     handleMessage: function(msg){
         if (msg == Context.beginCallMsg){
-            this.__call = makeConstructorCall(this, this.__info.type(), EberonConstructor.makeConstructorCall);
+            this.__call = makeContextCall(
+                this,
+                function(cx){ return EberonConstructor.makeConstructorCall(this.__info, cx, true); }.bind(this)
+                );
             return;
         }
         if (msg == Context.endCallMsg)
@@ -291,11 +297,7 @@ var OperatorNew = Context.Chained.extend({
         return Context.Chained.prototype.handleMessage.call(this, msg);
     },
     endParse: function(){
-        var callExpression = this.__call.end();
-        var e = Code.makeSimpleExpression(
-              callExpression.code()
-            , Type.makePointer("", this.__info));
-        this.handleMessage(new OperatorNewMsg(e));
+        this.handleMessage(new OperatorNewMsg(this.__call.end()));
     }
 });
 
@@ -322,7 +324,7 @@ var InPlaceVariableInit = Context.Chained.extend({
                          : new TypeNarrowVariable(type, false, false);
         this._symbol = Symbol.makeSymbol(this.__id, v);
         if (type instanceof Type.Record){
-            type.initializer(this, false); // checks for abstract etc.
+            EberonRecord.ensureCanBeInstantiated(type, false);
             if (e.designator())
                 this._code += this.language().rtl.cloneRecord(e.code());
             else // do not clone if it is temporary, e.g. constructor call
@@ -439,6 +441,12 @@ var VariableDeclaration = Context.VariableDeclaration.extend({
     handleIdentdef: function(id){
         checkOrdinaryExport(id, "variable");
         Context.VariableDeclaration.prototype.handleIdentdef.call(this, id);
+    },
+    _initCode: function(){
+        var type = this.type();
+        if (type instanceof EberonRecord.Record)
+            EberonRecord.ensureCanBeInstantiated(type, false);
+        return Context.VariableDeclaration.prototype._initCode.call(this);
     }
 });
 
@@ -559,7 +567,14 @@ var BaseInit = Context.Chained.extend({
     handleLiteral: function(s){
         if (s == "SUPER"){
             var ms = this.handleMessage(getConstructorSuperMsg);
-            this.__initCall = makeConstructorCall(this, Type.recordBase(this.type()), EberonConstructor.makeBaseConstructorCall);
+            this.__initCall = makeContextCall(
+                this,
+                function(cx){ 
+                    return EberonConstructor.makeBaseConstructorCall(
+                        Type.recordBase(this.type()), 
+                        cx);
+                    }.bind(this)
+                );
         }
     }
 });
@@ -730,10 +745,9 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
 
         this.__initedFields.push(id);        
         var type = fields[id].type();
-        return makeConstructorCall(
+        return makeContextCall(
             this, 
-            type, 
-            function(type, cx){return EberonConstructor.makeFieldInitCall(type, cx, id);});
+            function(cx){return EberonConstructor.makeFieldInitCall(type, cx, id);});
     }
 });
 
@@ -1142,6 +1156,10 @@ var ArrayDecl = Context.ArrayDecl.extend({
     _makeInit: function(type, dimensions, length){
         if (length == dynamicArrayLength)
             return '[]';
+
+        if (type instanceof EberonRecord.Record && EberonRecord.hasParameterizedConstructor(type))
+            throw new Errors.Error("cannot use '" + Type.typeName(type) + "' as an element of static array because it has constructor with parameters");
+
         return Context.ArrayDecl.prototype._makeInit.call(this, type, dimensions, length);
     },
     _makeType: function(elementsType, init, length){

+ 2 - 2
src/ob/Module.ob

@@ -35,9 +35,9 @@ PROCEDURE AnyType.description(): STRING;
     RETURN "JS.var"
 END AnyType.description;
 
-PROCEDURE AnyType.initializer(cx: Context.Type; forNew: BOOLEAN; code: STRING): STRING;
+PROCEDURE AnyType.initializer(cx: Context.Type): STRING;
     RETURN "undefined"
-END AnyType.initializer;
+END;
 
 PROCEDURE makeCallGenerator(cx: LanguageContext.PType): Procedure.PCallGenerator;
 VAR

+ 1 - 1
src/ob/Procedure.ob

@@ -358,7 +358,7 @@ PROCEDURE makeNew(): Symbols.PSymbol;
             Errors.raise("non-exported RECORD type cannot be used in NEW");
         END;
         RETURN Code.makeSimpleExpression(
-                arg.code() + " = " + baseType.initializer(cx^, TRUE, ""),
+                arg.code() + " = " + baseType.initializer(cx^),
                 NIL)
     END CallImpl.make;
 BEGIN

+ 13 - 9
src/ob/Types.ob

@@ -15,7 +15,7 @@ TYPE
     PType* = POINTER TO Type;
 
     StorageType* = RECORD(Type)    
-        PROCEDURE initializer*(cx: Context.Type; forNew: BOOLEAN; code: STRING): STRING
+        PROCEDURE initializer*(cx: Context.Type): STRING
     END;
     PStorageType* = POINTER TO StorageType;
 
@@ -360,7 +360,7 @@ PROCEDURE BasicType.description(): STRING;
     RETURN SELF.name
 END BasicType.description;
 
-PROCEDURE BasicType.initializer(cx: Context.Type; forNew: BOOLEAN; code: STRING): STRING;
+PROCEDURE BasicType.initializer(cx: Context.Type): STRING;
     RETURN SELF.mInitializer
 END BasicType.initializer;
 
@@ -407,9 +407,13 @@ BEGIN
     RETURN result
 END Record.description;
 
-PROCEDURE Record.initializer(cx: Context.Type; forNew: BOOLEAN; code: STRING): STRING;
-    RETURN "new " + cx.qualifyScope(SELF.scope) + SELF.cons + "(" + code + ")"
-END Record.initializer;
+PROCEDURE recordInitializer*(cx: Context.Type; r: Record; args: STRING): STRING;
+    RETURN "new " + cx.qualifyScope(r.scope) + r.cons + "(" + args + ")";
+END;
+
+PROCEDURE Record.initializer(cx: Context.Type): STRING;
+    RETURN recordInitializer(cx, SELF, "");
+END;
 
 PROCEDURE Record.addField(f: PField);
 BEGIN
@@ -492,7 +496,7 @@ BEGIN
     RETURN result
 END Pointer.description;
 
-PROCEDURE Pointer.initializer(cx: Context.Type; forNew: BOOLEAN; code: STRING): STRING;
+PROCEDURE Pointer.initializer(cx: Context.Type): STRING;
     RETURN "null"
 END Pointer.initializer;
 
@@ -559,11 +563,11 @@ BEGIN
     RETURN NIL
 END NamedType.denote;
 
-PROCEDURE OpenArray.initializer(cx: Context.Type; forNew: BOOLEAN; code: STRING): STRING;
+PROCEDURE OpenArray.initializer(cx: Context.Type): STRING;
     RETURN ""
 END OpenArray.initializer;
 
-PROCEDURE StaticArray.initializer(cx: Context.Type; forNew: BOOLEAN; code: STRING): STRING;
+PROCEDURE StaticArray.initializer(cx: Context.Type): STRING;
     RETURN SELF.mInitializer
 END StaticArray.initializer;
 
@@ -590,7 +594,7 @@ PROCEDURE StaticArray.length(): INTEGER;
     RETURN SELF.len
 END StaticArray.length;
 
-PROCEDURE Procedure.initializer(cx: Context.Type; forNew: BOOLEAN; code: STRING): STRING;
+PROCEDURE Procedure.initializer(cx: Context.Type): STRING;
     RETURN "null"
 END Procedure.initializer;
 

+ 18 - 0
test/test_unit_eberon.js

@@ -1231,6 +1231,24 @@ exports.suite = {
               ),
         pass("Derived(123)"),
         fail(["Derived()", "1 argument(s) expected, got 0"])
+        ),
+    "ARRAY OF record with constructor": testWithContext(
+        context(grammar.declarationSequence,
+                "TYPE NoParams = RECORD PROCEDURE NoParams(); END;"
+              + "WithParams = RECORD PROCEDURE WithParams(i: INTEGER); END;"
+              + "PROCEDURE NoParams.NoParams(); END;"
+              + "PROCEDURE WithParams.WithParams(i: INTEGER); END;"
+              ),
+        pass("TYPE T = ARRAY * OF NoParams;",
+             "TYPE T = ARRAY 3 OF NoParams;",
+             "TYPE T = ARRAY * OF WithParams;",
+             "VAR a: ARRAY 3 OF NoParams;",
+             "VAR a: ARRAY * OF WithParams;",
+             "PROCEDURE p(); VAR a: ARRAY * OF WithParams; BEGIN a.add(WithParams(123)); END;"
+            ),
+        fail(["TYPE T = ARRAY 3 OF WithParams;", "cannot use 'WithParams' as an element of static array because it has constructor with parameters"],
+             ["VAR a: ARRAY 3 OF WithParams;", "cannot use 'WithParams' as an element of static array because it has constructor with parameters"]
+            )
         )
     },
 "operator NEW": testWithContext(