From: Jean Privat Date: Thu, 9 Apr 2015 05:24:37 +0000 (+0700) Subject: Merge: new `with` statement X-Git-Tag: v0.7.4~35 X-Git-Url: http://nitlanguage.org?hp=-c Merge: new `with` statement Introduce the `with` statement that is analogous to the `with` of Python or the `uning` of C#. ~~~nit with x = something do x.work end ~~~ That is equivalent with ~~~nit do var x = something x.start x.work x.finish end ~~~ Note that an alternative syntax, without the local variable exists: ~~~nit with something do work end ~~~ that is used when we want some automatic control or whatever. Eg resource allocation or critical section. ~~~nit do var tmp = something tmp.start work tmp.stop end ~~~ Most real-world examples could be ~~~nit with file = path.create do file.write("Bla") end ~~~ and ~~~nit with a_mutex do work end ~~~ ## Names I reused the `finish` method from Iterator. I introduced the antonym `start` for the other side. Note that the `start` is important because it helps the object to know it has to prepare itself for a `finish`. I did not introduce an interface, mainly because I have no idea for a name. C# names it `IDisposable` and the `finish` method is called `Dispose`. There is no `start` because C# designers are lunatic sometime. In python there is no interface (obviously) and the method are called `__enter__` and `__exit__` because Python likes identifiers that resemble words eaten by boas. Note that the returned variable is not the original expression but the result of `__enter__`. With the Python semantic, but the Nit syntax, the first example will be equivalent to ~~~nit do var tmp = something var x = tmp.start x.work x.finish end ~~~ I am not sure of the benefits of the additional complexity, but the the more I thing about it the more I think this could be useful, ex for the control case the `start` can return the handler to `finish`. The issue is that proposed syntax: `with x = something` does not make sense (because `tmp=something` and `x=tmp.start`) thus should be replaced with something else; Python uses `with something as x:` that we can reuse but I do not want because it is ugly. An other alternative I tough of was to reuse the `for` keyword. but It is convoluted. The two previous real world examples will then be ~~~nit for file = path.create do file.write("Bla") end ~~~ and ~~~nit for a_mutex do work end ~~~ Pull-Request: #1243 Reviewed-by: Alexis Laferrière Reviewed-by: Etienne M. Gagnon Reviewed-by: Lucas Bajolet --- 90df56f73c160350d61a2a641271c6dca5904fb0 diff --combined src/interpreter/naive_interpreter.nit index 8818874,21118ab..272219f --- a/src/interpreter/naive_interpreter.nit +++ b/src/interpreter/naive_interpreter.nit @@@ -1016,9 -1016,6 +1016,9 @@@ redef class AMethPropde return v.int_instance(res) else if pname == "atof" then return v.float_instance(recvval.to_f) + else if pname == "fast_cstring" then + var ns = recvval.to_cstring.to_s.substring_from(args[1].to_i) + return v.native_string_instance(ns) end else if cname == "String" then var cs = v.send(v.force_get_primitive_method("to_cstring", args.first.mtype), [args.first]) @@@ -1273,14 -1270,16 +1273,16 @@@ redef class ABlockExp end redef class AVardeclExpr - redef fun stmt(v) + redef fun expr(v) do var ne = self.n_expr if ne != null then var i = v.expr(ne) - if i == null then return + if i == null then return null v.write_variable(self.variable.as(not null), i) + return i end + return null end end @@@ -1465,6 -1464,19 +1467,19 @@@ redef class AForExp end end + redef class AWithExpr + redef fun stmt(v) + do + var expr = v.expr(self.n_expr) + if expr == null then return + + v.callsite(method_start, [expr]) + v.stmt(self.n_block) + v.is_escape(self.break_mark) # Clear the break + v.callsite(method_finish, [expr]) + end + end + redef class AAssertExpr redef fun stmt(v) do diff --combined src/semantize/typing.nit index 38a689a,15d38a2..cf8bbda --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@@ -115,12 -115,7 +115,12 @@@ private class TypeVisito #node.debug("Unsafe typing: expected {sup}, got {sub}") return sup end - self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}") + if sub.need_anchor then + var u = anchor_to(sub) + self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}: {u}") + else + self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}") + end return null end @@@ -199,34 -194,6 +199,34 @@@ return sup end + # Can `mtype` be null (up to the current knowledge)? + fun can_be_null(mtype: MType): Bool + do + if mtype isa MNullableType or mtype isa MNullType then return true + if mtype isa MFormalType then + var x = anchor_to(mtype) + if x isa MNullableType or x isa MNullType then return true + end + return false + end + + # Check that `mtype` can be null (up to the current knowledge). + # + # If not then display a `useless-null-test` warning on node and return false. + # Else return true. + fun check_can_be_null(anode: ANode, mtype: MType): Bool + do + if can_be_null(mtype) then return true + + if mtype isa MFormalType then + var res = anchor_to(mtype) + modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}: {res}`.") + else + modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}`.") + end + return false + end + # Special verification on != and == for null # Return true fun null_test(anode: ABinopExpr) @@@ -238,24 -205,24 +238,24 @@@ if not mtype2 isa MNullType then return + if mtype isa MNullType then return + # Check of useless null - if not mtype isa MNullableType then - if not anchor_to(mtype) isa MNullableType then - modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}`.") - end - return - end + if not check_can_be_null(anode.n_expr, mtype) then return + + mtype = mtype.as_notnull # Check for type adaptation var variable = anode.n_expr.its_variable if variable == null then return + # One is null (mtype2 see above) the other is not null if anode isa AEqExpr then anode.after_flow_context.when_true.set_var(variable, mtype2) - anode.after_flow_context.when_false.set_var(variable, mtype.mtype) + anode.after_flow_context.when_false.set_var(variable, mtype) else if anode isa ANeExpr then anode.after_flow_context.when_false.set_var(variable, mtype2) - anode.after_flow_context.when_true.set_var(variable, mtype.mtype) + anode.after_flow_context.when_true.set_var(variable, mtype) else abort end @@@ -279,7 -246,10 +279,7 @@@ fun get_mclass(node: ANode, name: String): nullable MClass do - var mclass = modelbuilder.try_get_mclass_by_name(node, mmodule, name) - if mclass == null then - self.modelbuilder.error(node, "Type Error: missing primitive class `{name}'.") - end + var mclass = modelbuilder.get_mclass_by_name(node, mmodule, name) return mclass end @@@ -298,9 -268,7 +298,9 @@@ if recvtype isa MNullType then # `null` only accepts some methods of object. if name == "==" or name == "!=" or name == "is_same_instance" then - unsafe_type = mmodule.object_type.as_nullable + var objclass = get_mclass(node, "Object") + if objclass == null then return null # Forward error + unsafe_type = objclass.mclass_type else self.error(node, "Error: Method '{name}' call on 'null'.") return null @@@ -360,14 -328,13 +360,14 @@@ end - var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null) + var msignature = mpropdef.new_msignature or else mpropdef.msignature + if msignature == null then return null # skip error msignature = resolve_for(msignature, recvtype, recv_is_self).as(MSignature) var erasure_cast = false var rettype = mpropdef.msignature.return_mtype if not recv_is_self and rettype != null then - rettype = rettype.as_notnullable + rettype = rettype.undecorate if rettype isa MParameterType then var erased_rettype = msignature.return_mtype assert erased_rettype != null @@@ -483,7 -450,7 +483,7 @@@ var found = true for t2 in col do if t2 == null then continue # return null - if t2 isa MNullableType or t2 isa MNullType then + if can_be_null(t2) and not can_be_null(t1) then t1 = t1.as_nullable end if not is_subtype(t2, t1) then found = false @@@ -602,18 -569,14 +602,18 @@@ redef class AMethPropde var nblock = self.n_block if nblock == null then return - var mpropdef = self.mpropdef.as(not null) + var mpropdef = self.mpropdef + if mpropdef == null then return # skip error + var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef) self.selfvariable = v.selfvariable var mmethoddef = self.mpropdef.as(not null) - for i in [0..mmethoddef.msignature.arity[ do - var mtype = mmethoddef.msignature.mparameters[i].mtype - if mmethoddef.msignature.vararg_rank == i then + var msignature = mmethoddef.msignature + if msignature == null then return # skip error + for i in [0..msignature.arity[ do + var mtype = msignature.mparameters[i].mtype + if msignature.vararg_rank == i then var arrayclass = v.get_mclass(self.n_signature.n_params[i], "Array") if arrayclass == null then return # Skip error mtype = arrayclass.get_mtype([mtype]) @@@ -624,7 -587,7 +624,7 @@@ end v.visit_stmt(nblock) - if not nblock.after_flow_context.is_unreachable and mmethoddef.msignature.return_mtype != null then + if not nblock.after_flow_context.is_unreachable and msignature.return_mtype != null then # We reach the end of the function without having a return, it is bad v.error(self, "Control error: Reached end of function (a 'return' with a value was expected).") end @@@ -636,9 -599,7 +636,9 @@@ redef class AAttrPropde do if not has_value then return - var mpropdef = self.mpropdef.as(not null) + var mpropdef = self.mpropdef + if mpropdef == null then return # skip error + var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef) self.selfvariable = v.selfvariable @@@ -737,9 -698,7 +737,9 @@@ redef class AVardeclExp var decltype = mtype if mtype == null or mtype isa MNullType then - decltype = v.get_mclass(self, "Object").mclass_type.as_nullable + var objclass = v.get_mclass(self, "Object") + if objclass == null then return # skip error + decltype = objclass.mclass_type.as_nullable if mtype == null then mtype = decltype end @@@ -748,6 -707,7 +748,7 @@@ #debug("var {variable}: {mtype}") + self.mtype = mtype self.is_typed = true end end @@@ -1043,7 -1003,7 +1044,7 @@@ redef class AForExp # anchor formal and virtual types if mtype.need_anchor then mtype = v.anchor_to(mtype) - mtype = mtype.as_notnullable + mtype = mtype.undecorate self.coltype = mtype.as(MClassType) # get methods is_ok, next, item @@@ -1106,6 -1066,24 +1107,24 @@@ end end + redef class AWithExpr + var method_start: nullable CallSite + var method_finish: nullable CallSite + + redef fun accept_typing(v: TypeVisitor) + do + var mtype = v.visit_expr(n_expr) + if mtype == null then return + + method_start = v.get_method(self, mtype, "start", n_expr isa ASelfExpr) + method_finish = v.get_method(self, mtype, "finish", n_expr isa ASelfExpr) + + v.visit_stmt(n_block) + self.mtype = n_block.mtype + self.is_typed = true + end + end + redef class AAssertExpr redef fun accept_typing(v) do @@@ -1162,18 -1140,12 +1181,18 @@@ redef class AOrElseExp return # Skip error end - t1 = t1.as_notnullable + if t1 isa MNullType then + v.error(n_expr, "Type error: or else on null") + else if v.check_can_be_null(n_expr, t1) then + t1 = t1.as_notnull + end var t = v.merge_types(self, [t1, t2]) if t == null then - t = v.mmodule.object_type - if t2 isa MNullableType then + var c = v.get_mclass(self, "Object") + if c == null then return # forward error + t = c.mclass_type + if v.can_be_null(t2) then t = t.as_nullable end #v.error(self, "Type Error: ambiguous type {t1} vs {t2}") @@@ -1238,11 -1210,8 +1257,11 @@@ redef class ASuperstringExp var mclass = v.get_mclass(self, "String") if mclass == null then return # Forward error self.mtype = mclass.mclass_type + var objclass = v.get_mclass(self, "Object") + if objclass == null then return # Forward error + var objtype = objclass.mclass_type for nexpr in self.n_exprs do - v.visit_expr_subtype(nexpr, v.mmodule.object_type) + v.visit_expr_subtype(nexpr, objtype) end end end @@@ -1405,12 -1374,22 +1424,12 @@@ redef class AAsNotnullExp v.error(self, "Type error: as(not null) on null") return end - if mtype isa MNullableType then - self.mtype = mtype.mtype - return - end - self.mtype = mtype - if mtype isa MClassType then - v.modelbuilder.warning(self, "useless-type-test", "Warning: expression is already not null, since it is a `{mtype}`.") - return - end - assert mtype.need_anchor - var u = v.anchor_to(mtype) - if not u isa MNullableType then - v.modelbuilder.warning(self, "useless-type-test", "Warning: expression is already not null, since it is a `{mtype}: {u}`.") - return + if v.check_can_be_null(n_expr, mtype) then + mtype = mtype.as_notnull end + + self.mtype = mtype end end @@@ -1857,8 -1836,7 +1876,8 @@@ redef class AAttrFormExp var mpropdefs = mproperty.lookup_definitions(v.mmodule, unsafe_type) assert mpropdefs.length == 1 var mpropdef = mpropdefs.first - var attr_type = mpropdef.static_mtype.as(not null) + var attr_type = mpropdef.static_mtype + if attr_type == null then return # skip error attr_type = v.resolve_for(attr_type, recvtype, self.n_expr isa ASelfExpr) self.attr_type = attr_type end