Browse Source

Allow ++/-- on member fields of abstracts (#8932)

Aleksandr Kuzmenko 6 years ago
parent
commit
61fbb56ad7
3 changed files with 86 additions and 6 deletions
  1. 6 6
      src/typing/calls.ml
  2. 42 0
      src/typing/typer.ml
  3. 38 0
      tests/unit/src/unit/issues/Issue8930.hx

+ 6 - 6
src/typing/calls.ml

@@ -118,6 +118,12 @@ let mk_array_set_call ctx (cf,tf,r,e1,e2o) c ebase p =
 			let ef = mk (TField(et,(FStatic(c,cf)))) tf p in
 			make_call ctx ef [ebase;e1;evalue] r p
 
+let rec needs_temp_var e =
+	match e.eexpr with
+	| TLocal _ | TTypeExpr _ | TConst _ -> false
+	| TField (e, _) | TParenthesis e -> needs_temp_var e
+	| _ -> true
+
 let call_to_string ctx ?(resume=false) e =
 	let gen_to_string e =
 		(* Ignore visibility of the toString field. *)
@@ -129,12 +135,6 @@ let call_to_string ctx ?(resume=false) e =
 	if ctx.com.config.pf_static && not (is_nullable e.etype) then
 		gen_to_string e
 	else begin (* generate `if(e == null) 'null' else e.toString()` *)
-		let rec needs_temp_var e =
-			match e.eexpr with
-			| TLocal _ | TTypeExpr _ | TConst _ -> false
-			| TField (e, _) | TParenthesis e -> needs_temp_var e
-			| _ -> true
-		in
 		let string_null = mk (TConst (TString "null")) ctx.t.tstring e.epos in
 		if needs_temp_var e then
 			let tmp = alloc_var VGenerated "tmp" e.etype e.epos in

+ 42 - 0
src/typing/typer.ml

@@ -1122,6 +1122,48 @@ and type_unop ctx op flag e p =
 				let e = mk_array_get_call ctx (AbstractCast.find_array_access ctx a tl ekey None p) c ebase p in
 				loop (AKExpr e)
 			end
+		| AKUsing (emethod,cl,cf,etarget,force_inline) when (op = Decrement || op = Increment) && has_meta Meta.Impl cf.cf_meta ->
+			let l = save_locals ctx in
+			let init_tmp,etarget,eget =
+				match needs_temp_var etarget, fst e with
+				| true, EField (_, field_name) ->
+					let tmp = gen_local ctx etarget.etype p in
+					let tmp_ident = (EConst (Ident tmp.v_name), p) in
+					(
+						mk (TVar (tmp, Some etarget)) ctx.t.tvoid p,
+						mk (TLocal tmp) tmp.v_type p,
+						(EField (tmp_ident,field_name), p)
+					)
+				| _ -> (mk (TBlock []) ctx.t.tvoid p, etarget, e)
+			in
+			let op = (match op with Increment -> OpAdd | Decrement -> OpSub | _ -> assert false) in
+			let one = (EConst (Int "1"),p) in
+			(match follow cf.cf_type with
+			| TFun (_, t) ->
+				(match flag with
+				| Prefix ->
+					let get = type_binop ctx op eget one false WithType.value p in
+					unify ctx get.etype t p;
+					l();
+					let call_setter = make_call ctx emethod [etarget; get] t ~force_inline p in
+					mk (TBlock [init_tmp; call_setter]) t p
+				| Postfix ->
+					let get = type_expr ctx eget WithType.value in
+					let tmp_value = gen_local ctx t p in
+					let plusone = type_binop ctx op (EConst (Ident tmp_value.v_name),p) one false WithType.value p in
+					unify ctx get.etype t p;
+					l();
+					mk (TBlock [
+						init_tmp;
+						mk (TVar (tmp_value,Some get)) ctx.t.tvoid p;
+						make_call ctx emethod [etarget; plusone] t ~force_inline p;
+						mk (TLocal tmp_value) t p;
+					]) t p
+				)
+			| _ ->
+				l();
+				assert false
+			)
 		| AKInline _ | AKUsing _ | AKMacro _ ->
 			error "This kind of operation is not supported" p
 		| AKFieldSet _ ->

+ 38 - 0
tests/unit/src/unit/issues/Issue8930.hx

@@ -0,0 +1,38 @@
+package unit.issues;
+
+class Issue8930 extends Test {
+	var v:Bar = {x:10};
+
+	function test() {
+		eq(11, ++v.x);
+		eq(11, v.x);
+		eq(11, v.x++);
+		eq(12, v.x);
+
+		eq(11, --v.x);
+		eq(11, v.x);
+		eq(11, v.x--);
+		eq(10, v.x);
+
+		#if !as3
+		var cnt = 0;
+		function sideEffect() {
+			cnt++;
+			return v;
+		}
+		++sideEffect().x;
+		sideEffect().x++;
+		eq(2, cnt);
+		#end
+	}
+}
+
+private typedef Foo = {
+	x: Int
+}
+
+private abstract Bar(Foo) from Foo to Foo {
+	public var x(get, set):Int;
+	public inline function get_x() return this.x;
+	public inline function set_x(value) return this.x = value;
+}