Merge: Abstract attribute
authorJean Privat <jean@pryen.org>
Tue, 10 Mar 2015 13:51:12 +0000 (20:51 +0700)
committerJean Privat <jean@pryen.org>
Tue, 10 Mar 2015 13:51:12 +0000 (20:51 +0700)
Add the annotation `abstract` on attributes.

It is just a syntactic sugar to define a couple of abstract getter-setters with a shared documentation without an associated slot in the instance so it can be used in interfaces.

~~~nit
interface Foo
var a: Object is abstract
end

class Bar
super Foo
         # A concrete attribute that redefine the abstract one
redef var a
end

class Baz
super Foo
var real_a: Object
         # A pair of concrete methods that redefine the abstract attribute
redef fun a do return real_a
redef fun a=(x) do real_a = x
end
~~~

The visibility rules are unchanged with regard to concrete attributes, so by default the writer is private.

Needed cleaning (and a bugfix) are included in the PR

Pull-Request: #1177
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>

13 files changed:
src/compiler/abstract_compiler.nit
src/interpreter/naive_interpreter.nit
src/modelize/modelize_property.nit
src/rapid_type_analysis.nit
src/semantize/typing.nit
tests/base_attr_abstract.nit [new file with mode: 0644]
tests/sav/base_attr5_alt17.res
tests/sav/base_attr_abstract.res [new file with mode: 0644]
tests/sav/base_attr_abstract_alt1.res [new file with mode: 0644]
tests/sav/base_attr_abstract_alt2.res [new file with mode: 0644]
tests/sav/base_attr_abstract_alt3.res [new file with mode: 0644]
tests/sav/base_attr_abstract_alt4.res [new file with mode: 0644]
tests/sav/error_kern_attr_int.res

index 23927b6..aa57850 100644 (file)
@@ -1834,6 +1834,15 @@ redef class MMethodDef
                var modelbuilder = v.compiler.modelbuilder
                var val = constant_value
                var node = modelbuilder.mpropdef2node(self)
+
+               if is_abstract then
+                       var cn = v.class_name_string(arguments.first)
+                       v.current_node = node
+                       v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
+                       v.add_raw_abort
+                       return null
+               end
+
                if node isa APropdef then
                        var oldnode = v.current_node
                        v.current_node = node
@@ -1893,13 +1902,6 @@ end
 redef class AMethPropdef
        redef fun compile_to_c(v, mpropdef, arguments)
        do
-               if mpropdef.is_abstract then
-                       var cn = v.class_name_string(arguments.first)
-                       v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
-                       v.add_raw_abort
-                       return
-               end
-
                # Call the implicit super-init
                var auto_super_inits = self.auto_super_inits
                if auto_super_inits != null then
index bb0b3b3..dd61428 100644 (file)
@@ -413,6 +413,14 @@ class NaiveInterpreter
                var val = mpropdef.constant_value
 
                var node = modelbuilder.mpropdef2node(mpropdef)
+               if mpropdef.is_abstract then
+                       if node != null then
+                               self.frames.unshift new Frame(node, mpropdef, args)
+                       end
+                       fatal("Abstract method `{mpropdef.mproperty.name}` called on `{args.first.mtype}`")
+                       abort
+               end
+
                if node isa APropdef then
                        self.parameter_check(node, mpropdef, args)
                        return node.call(self, mpropdef, args)
@@ -725,11 +733,6 @@ redef class AMethPropdef
                        v.write_variable(variable, arguments[i+1])
                end
 
-               if mpropdef.is_abstract then
-                       v.fatal("Abstract method `{mpropdef.mproperty.name}` called on `{arguments.first.mtype}`")
-                       abort
-               end
-
                # Call the implicit super-init
                var auto_super_inits = self.auto_super_inits
                if auto_super_inits != null then
@@ -1163,7 +1166,9 @@ redef class AAttrPropdef
                        evaluate_expr(v, recv)
                        return
                end
-               var mtype = self.mpropdef.static_mtype.as(not null)
+               var mpropdef = self.mpropdef
+               if mpropdef == null then return
+               var mtype = mpropdef.static_mtype.as(not null)
                mtype = mtype.anchor_to(v.mainmodule, recv.mtype.as(MClassType))
                if mtype isa MNullableType then
                        v.write_attribute(self.mpropdef.mproperty, recv, v.null_instance)
index 35a50c3..a4d1141 100644 (file)
@@ -955,32 +955,31 @@ redef class AAttrPropdef
        redef fun build_property(modelbuilder, mclassdef)
        do
                var mclass = mclassdef.mclass
+               var nid2 = n_id2
+               var name = nid2.text
+
+               var atabstract = self.get_single_annotation("abstract", modelbuilder)
+               if atabstract == null then
+                       if mclass.kind == interface_kind then
+                               modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.")
+                       else if mclass.kind == enum_kind then
+                               modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.")
+                       else if mclass.kind == extern_kind then
+                               modelbuilder.error(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.")
+                       end
 
-               var name: String
-               name = self.n_id2.text
-
-               if mclass.kind == interface_kind or mclassdef.mclass.kind == enum_kind then
-                       modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.")
-               else if mclass.kind == enum_kind then
-                       modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.")
-               else if mclass.kind == extern_kind then
-                       modelbuilder.error(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.")
+                       var mprop = new MAttribute(mclassdef, "_" + name, private_visibility)
+                       var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
+                       self.mpropdef = mpropdef
+                       modelbuilder.mpropdef2npropdef[mpropdef] = self
                end
 
-               # New attribute style
-               var nid2 = self.n_id2
-               var mprop = new MAttribute(mclassdef, "_" + name, private_visibility)
-               var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
-               self.mpropdef = mpropdef
-               modelbuilder.mpropdef2npropdef[mpropdef] = self
-
                var readname = name
                var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod)
                if mreadprop == null then
                        var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
                        mreadprop = new MMethod(mclassdef, readname, mvisibility)
                        if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mreadprop) then return
-                       mreadprop.deprecation = mprop.deprecation
                else
                        if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, true, mreadprop) then return
                        check_redef_property_visibility(modelbuilder, self.n_visibility, mreadprop)
@@ -991,10 +990,16 @@ redef class AAttrPropdef
                self.mreadpropdef = mreadpropdef
                modelbuilder.mpropdef2npropdef[mreadpropdef] = self
                set_doc(mreadpropdef, modelbuilder)
-               mpropdef.mdoc = mreadpropdef.mdoc
+               if mpropdef != null then mpropdef.mdoc = mreadpropdef.mdoc
+               if atabstract != null then mreadpropdef.is_abstract = true
 
                has_value = n_expr != null or n_block != null
 
+               if atabstract != null and has_value then
+                       modelbuilder.error(atabstract, "Error: `abstract` attributes cannot have an initial value")
+                       return
+               end
+
                var atnoinit = self.get_single_annotation("noinit", modelbuilder)
                if atnoinit == null then atnoinit = self.get_single_annotation("noautoinit", modelbuilder)
                if atnoinit != null then
@@ -1003,6 +1008,10 @@ redef class AAttrPropdef
                                modelbuilder.error(atnoinit, "Error: `noautoinit` attributes cannot have an initial value")
                                return
                        end
+                       if atabstract != null then
+                               modelbuilder.error(atnoinit, "Error: `noautoinit` attributes cannot be abstract")
+                               return
+                       end
                end
 
                var atlazy = self.get_single_annotation("lazy", modelbuilder)
@@ -1054,7 +1063,7 @@ redef class AAttrPropdef
                        end
                        mwriteprop = new MMethod(mclassdef, writename, mvisibility)
                        if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef, false, mwriteprop) then return
-                       mwriteprop.deprecation = mprop.deprecation
+                       mwriteprop.deprecation = mreadprop.deprecation
                else
                        if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef or else n_kwredef, true, mwriteprop) then return
                        if atwritable != null then
@@ -1066,18 +1075,19 @@ redef class AAttrPropdef
                var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
                self.mwritepropdef = mwritepropdef
                modelbuilder.mpropdef2npropdef[mwritepropdef] = self
-               mwritepropdef.mdoc = mpropdef.mdoc
+               mwritepropdef.mdoc = mreadpropdef.mdoc
+               if atabstract != null then mwritepropdef.is_abstract = true
        end
 
        redef fun build_signature(modelbuilder)
        do
+               var mreadpropdef = self.mreadpropdef
                var mpropdef = self.mpropdef
-               if mpropdef == null then return # Error thus skipped
-               var mclassdef = mpropdef.mclassdef
+               if mreadpropdef == null then return # Error thus skipped
+               var mclassdef = mreadpropdef.mclassdef
                var mmodule = mclassdef.mmodule
                var mtype: nullable MType = null
 
-               var mreadpropdef = self.mreadpropdef
 
                var ntype = self.n_type
                if ntype != null then
@@ -1087,7 +1097,7 @@ redef class AAttrPropdef
 
                var inherited_type: nullable MType = null
                # Inherit the type from the getter (usually an abstract getter)
-               if mreadpropdef != null and not mreadpropdef.is_intro then
+               if not mreadpropdef.is_intro then
                        var msignature = mreadpropdef.mproperty.intro.msignature
                        if msignature == null then return # Error, thus skipped
                        inherited_type = msignature.return_mtype
@@ -1122,7 +1132,7 @@ redef class AAttrPropdef
                                        var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
                                        if cla != null then mtype = cla.mclass_type
                                else
-                                       modelbuilder.error(self, "Error: Untyped attribute {mpropdef}. Implicit typing allowed only for literals and new.")
+                                       modelbuilder.error(self, "Error: Untyped attribute {mreadpropdef}. Implicit typing allowed only for literals and new.")
                                end
 
                                if mtype == null then return
@@ -1137,13 +1147,15 @@ redef class AAttrPropdef
                end
 
                if mtype == null then
-                       modelbuilder.error(self, "Error: Untyped attribute {mpropdef}")
+                       modelbuilder.error(self, "Error: Untyped attribute {mreadpropdef}")
                        return
                end
 
-               mpropdef.static_mtype = mtype
+               if mpropdef != null then
+                       mpropdef.static_mtype = mtype
+               end
 
-               if mreadpropdef != null then
+               do
                        var msignature = new MSignature(new Array[MParameter], mtype)
                        mreadpropdef.msignature = msignature
                end
index f5c1dc2..a6cd32e 100644 (file)
@@ -389,7 +389,6 @@ class RapidTypeAnalysis
                if mproperty.mpropdefs.length <= 1 then return
                # If all definitions of a method are live, we can remove the definition of the totry set
                for d in mproperty.mpropdefs do
-                       if d.is_abstract then continue
                        if not live_methoddefs.has(d) then return
                end
                #print "full property: {mpropdef.mproperty} for {mpropdef.mproperty.mpropdefs.length} definitions"
index 343edbd..6a6d943 100644 (file)
@@ -597,6 +597,8 @@ end
 redef class AAttrPropdef
        redef fun do_typing(modelbuilder: ModelBuilder)
        do
+               if not has_value then return
+
                var mpropdef = self.mpropdef.as(not null)
                var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef)
                self.selfvariable = v.selfvariable
diff --git a/tests/base_attr_abstract.nit b/tests/base_attr_abstract.nit
new file mode 100644 (file)
index 0000000..34c1e4c
--- /dev/null
@@ -0,0 +1,50 @@
+# 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
+
+interface Foo
+       var a: Object is abstract
+       #alt1#var b = 1 is abstract
+       #alt2#var b is abstract, noautoinit
+end
+
+class Bar
+       super Foo
+       redef var a
+end
+
+class Baz
+       super Foo
+       redef fun a do return 100
+       redef fun a=(x) do (101).output
+end
+
+class FooBar
+       super Foo
+end
+
+var f: Foo = new Bar(1)
+f.a.output
+f.a = 2
+f.a.output
+
+f = new Baz
+f.a.output
+f.a = 3
+f.a.output
+
+f = new FooBar
+#alt3#f.a.output
+#alt4#f.a = 4
index 03ea677..86f9e7a 100644 (file)
@@ -1,2 +1 @@
 alt/base_attr5_alt17.nit:47,12--14: Error: No property B::bar is inherited. Remove the redef keyword to define a new property.
-alt/base_attr5_alt17.nit:47,12--14: Error: Untyped attribute base_attr5_alt17#B#_bar
diff --git a/tests/sav/base_attr_abstract.res b/tests/sav/base_attr_abstract.res
new file mode 100644 (file)
index 0000000..36f0f1a
--- /dev/null
@@ -0,0 +1,5 @@
+1
+2
+100
+101
+100
diff --git a/tests/sav/base_attr_abstract_alt1.res b/tests/sav/base_attr_abstract_alt1.res
new file mode 100644 (file)
index 0000000..6971473
--- /dev/null
@@ -0,0 +1 @@
+alt/base_attr_abstract_alt1.nit:19,15--22: Error: `abstract` attributes cannot have an initial value
diff --git a/tests/sav/base_attr_abstract_alt2.res b/tests/sav/base_attr_abstract_alt2.res
new file mode 100644 (file)
index 0000000..1ed3535
--- /dev/null
@@ -0,0 +1,2 @@
+alt/base_attr_abstract_alt2.nit:20,6: Error: Untyped attribute base_attr_abstract_alt2#Foo#b
+alt/base_attr_abstract_alt2.nit:20,21--30: Error: `noautoinit` attributes cannot be abstract
diff --git a/tests/sav/base_attr_abstract_alt3.res b/tests/sav/base_attr_abstract_alt3.res
new file mode 100644 (file)
index 0000000..4052598
--- /dev/null
@@ -0,0 +1,6 @@
+Runtime error: Abstract method `a` called on `FooBar` (alt/base_attr_abstract_alt3.nit:18)
+1
+2
+100
+101
+100
diff --git a/tests/sav/base_attr_abstract_alt4.res b/tests/sav/base_attr_abstract_alt4.res
new file mode 100644 (file)
index 0000000..09d65f9
--- /dev/null
@@ -0,0 +1,6 @@
+Runtime error: Abstract method `a=` called on `FooBar` (alt/base_attr_abstract_alt4.nit:18)
+1
+2
+100
+101
+100
index f870a60..004eee0 100644 (file)
@@ -1 +1 @@
-error_kern_attr_int.nit:18,6--9: Error: Attempt to define attribute toto in the interface Int.
+error_kern_attr_int.nit:18,6--9: Error: Attempt to define attribute toto in the enum class Int.