From: Jean Privat Date: Tue, 10 Mar 2015 13:51:12 +0000 (+0700) Subject: Merge: Abstract attribute X-Git-Tag: v0.7.3~33 X-Git-Url: http://nitlanguage.org?hp=111d93a28c270cd22229d12faef3ba52fb74c476 Merge: Abstract attribute 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 Reviewed-by: Alexandre Terrasa Reviewed-by: Alexis Laferrière --- diff --git a/src/compiler/abstract_compiler.nit b/src/compiler/abstract_compiler.nit index 23927b6..aa57850 100644 --- a/src/compiler/abstract_compiler.nit +++ b/src/compiler/abstract_compiler.nit @@ -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 diff --git a/src/interpreter/naive_interpreter.nit b/src/interpreter/naive_interpreter.nit index bb0b3b3..dd61428 100644 --- a/src/interpreter/naive_interpreter.nit +++ b/src/interpreter/naive_interpreter.nit @@ -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) diff --git a/src/modelize/modelize_property.nit b/src/modelize/modelize_property.nit index 35a50c3..a4d1141 100644 --- a/src/modelize/modelize_property.nit +++ b/src/modelize/modelize_property.nit @@ -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 diff --git a/src/rapid_type_analysis.nit b/src/rapid_type_analysis.nit index f5c1dc2..a6cd32e 100644 --- a/src/rapid_type_analysis.nit +++ b/src/rapid_type_analysis.nit @@ -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" diff --git a/src/semantize/typing.nit b/src/semantize/typing.nit index 343edbd..6a6d943 100644 --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@ -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 index 0000000..34c1e4c --- /dev/null +++ b/tests/base_attr_abstract.nit @@ -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 diff --git a/tests/sav/base_attr5_alt17.res b/tests/sav/base_attr5_alt17.res index 03ea677..86f9e7a 100644 --- a/tests/sav/base_attr5_alt17.res +++ b/tests/sav/base_attr5_alt17.res @@ -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 index 0000000..36f0f1a --- /dev/null +++ b/tests/sav/base_attr_abstract.res @@ -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 index 0000000..6971473 --- /dev/null +++ b/tests/sav/base_attr_abstract_alt1.res @@ -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 index 0000000..1ed3535 --- /dev/null +++ b/tests/sav/base_attr_abstract_alt2.res @@ -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 index 0000000..4052598 --- /dev/null +++ b/tests/sav/base_attr_abstract_alt3.res @@ -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 index 0000000..09d65f9 --- /dev/null +++ b/tests/sav/base_attr_abstract_alt4.res @@ -0,0 +1,6 @@ +Runtime error: Abstract method `a=` called on `FooBar` (alt/base_attr_abstract_alt4.nit:18) +1 +2 +100 +101 +100 diff --git a/tests/sav/error_kern_attr_int.res b/tests/sav/error_kern_attr_int.res index f870a60..004eee0 100644 --- a/tests/sav/error_kern_attr_int.res +++ b/tests/sav/error_kern_attr_int.res @@ -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.