Merge: Intern new
authorJean Privat <jean@pryen.org>
Tue, 14 Apr 2015 09:33:32 +0000 (16:33 +0700)
committerJean Privat <jean@pryen.org>
Tue, 14 Apr 2015 09:33:32 +0000 (16:33 +0700)
A small short PR that enables a `new`-factory to instantiate itself.

previously there was no way to do that. The following code will stack-overflow:

~~~nit
class A
   new do
      var res = new A
      res.do_thigns
      return res
   end
end
var a = new A # infinite recursion of `new A`
~~~

Note: this is a very bad example as what is done in the previous `new` should be done in an `init` instead since it is related to the initialization of the object. A `new` factory should be exclusively used for some factory-concern like returning a more specialized object or returning an already existing object.

With this PR, the primitive constructor is available and is called `intern`, as the annotation that indicates things that cannot be programmed in Nit but are provided by the execution engine.

So in the previous example, just use this primitive constructor instead of doing a recursive call:

~~~
      var res = new A.intern
~~~

This intern constructor just do the allocation and the initialization of attributes with default values.
`init` is not called.

Maybe it should not do the initialization of attributes neither but if I skip them, there is no way for the programmer to ask for them manually. whereas `init` in not automatically called but can be called manually.

note: this PR is a small step toward the conclusion of the constructor saga, more will come soon (I hope)

Pull-Request: #1252
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>

src/compiler/abstract_compiler.nit
src/interpreter/naive_interpreter.nit
src/semantize/typing.nit
tests/base_new_intern.nit [new file with mode: 0644]
tests/sav/base_new_intern.res [new file with mode: 0644]

index 059961f..3116924 100644 (file)
@@ -3026,7 +3026,9 @@ redef class ANewExpr
 
                var recv = v.init_instance_or_extern(mtype)
 
-               var callsite = self.callsite.as(not null)
+               var callsite = self.callsite
+               if callsite == null then return recv
+
                var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
                var res2 = v.compile_callsite(callsite, args)
                if res2 != null then
index 4cab2e5..3168c76 100644 (file)
@@ -1807,6 +1807,9 @@ redef class ANewExpr
                var mtype = v.unanchor_type(self.recvtype.as(not null))
                var recv: Instance = new MutableInstance(mtype)
                v.init_instance(recv)
+               var callsite = self.callsite
+               if callsite == null then return recv
+
                var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
                if args == null then return null
                var res2 = v.callsite(callsite, args)
index e51269f..2bbc78b 100644 (file)
@@ -1844,6 +1844,7 @@ redef class ANewExpr
                end
 
                self.recvtype = recvtype
+               var kind = recvtype.mclass.kind
 
                var name: String
                var nid = self.n_id
@@ -1852,11 +1853,24 @@ redef class ANewExpr
                else
                        name = "new"
                end
+               if name == "intern" then
+                       if kind != concrete_kind then
+                               v.error(self, "Type Error: Cannot instantiate {kind} {recvtype}.")
+                               return
+                       end
+                       if n_args.n_exprs.not_empty then
+                               v.error(n_args, "Type Error: the intern constructor expects no arguments.")
+                               return
+                       end
+                       # Our job is done
+                       self.mtype = recvtype
+                       return
+               end
+
                var callsite = v.get_method(self, recvtype, name, false)
                if callsite == null then return
 
                if not callsite.mproperty.is_new then
-                       var kind = recvtype.mclass.kind
                        if kind != concrete_kind then
                                v.error(self, "Type Error: Cannot instantiate {kind} {recvtype}.")
                                return
diff --git a/tests/base_new_intern.nit b/tests/base_new_intern.nit
new file mode 100644 (file)
index 0000000..76819f0
--- /dev/null
@@ -0,0 +1,30 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import kernel
+
+class A
+       var i: Int
+       new do
+               var res = new A.intern
+               res.i = 1
+               return res
+       end
+       init do
+               0.output # not called
+       end
+end
+
+var a = new A
+a.i.output
diff --git a/tests/sav/base_new_intern.res b/tests/sav/base_new_intern.res
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1