Browse Source

delay evaluation of extractor variable values (closes #5274)

I _think_ this is fine to do because the tree nature of the matcher output ensures that all initialization points are unique. That is, we cannot have something like this:

```haxe
var _hx_tmp;
if (cond) {
    _hx_tmp = init();
}
use(_hx_tmp); // cannot tell if initialized or not, requires run-time support
```
Simon Krajewski 9 years ago
parent
commit
86d5952f84

+ 3 - 1
src/optimization/analyzer.ml

@@ -1128,6 +1128,8 @@ module Run = struct
 
 	let there actx e =
 		if actx.com.debug then add_debug_expr actx "initial" e;
+		let e = with_timer actx "analyzer-var-lazifier" (fun () -> VarLazifier.apply actx.com e) in
+		if actx.com.debug then add_debug_expr actx "after var-lazifier" e;
 		let e = with_timer actx "analyzer-filter-apply" (fun () -> TexprFilter.apply actx.com e) in
 		if actx.com.debug then add_debug_expr actx "after filter-apply" e;
 		let tf,is_real_function = match e.eexpr with
@@ -1151,7 +1153,7 @@ module Run = struct
 		let e = with_timer actx "analyzer-fusion" (fun () -> Fusion.apply actx.com actx.config e) in
 		if actx.com.debug then add_debug_expr actx "after fusion" e;
 		let e = with_timer actx "analyzer-cleanup" (fun () -> Cleanup.apply actx.com e) in
-		if actx.com.debug then add_debug_expr actx "after to-cleanup" e;
+		if actx.com.debug then add_debug_expr actx "after cleanup" e;
 		let e = if is_real_function then
 			e
 		else begin

+ 36 - 0
src/optimization/analyzerTexpr.ml

@@ -269,6 +269,42 @@ module TexprFilter = struct
 		loop e
 end
 
+module VarLazifier = struct
+	let apply com e =
+		let rec loop var_inits e = match e.eexpr with
+			| TVar(v,Some e1) when (Meta.has (Meta.Custom ":extractorVariable") v.v_meta) ->
+				let var_inits,e1 = loop var_inits e1 in
+				let var_inits = PMap.add v.v_id e1 var_inits in
+				var_inits,{e with eexpr = TVar(v,None)}
+			| TLocal v ->
+				begin try
+					let e_init = PMap.find v.v_id var_inits in
+					let e = {e with eexpr = TBinop(OpAssign,e,e_init)} in
+					let e = {e with eexpr = TParenthesis e} in
+					let var_inits = PMap.remove v.v_id var_inits in
+					var_inits,e
+				with Not_found ->
+					var_inits,e
+				end
+			| TIf(e1,e2,eo) ->
+				let var_inits,e1 = loop var_inits e1 in
+				let _,e2 = loop var_inits e2 in
+				let eo = match eo with None -> None | Some e -> Some (snd (loop var_inits e)) in
+				var_inits,{e with eexpr = TIf(e1,e2,eo)}
+			| TSwitch(e1,cases,edef) ->
+				let var_inits,e1 = loop var_inits e1 in
+				let cases = List.map (fun (el,e) ->
+					let _,e = loop var_inits e in
+					el,e
+				) cases in
+				let edef = match edef with None -> None | Some e -> Some (snd (loop var_inits e)) in
+				var_inits,{e with eexpr = TSwitch(e1,cases,edef)}
+			| _ ->
+				Texpr.foldmap loop var_inits e
+		in
+		snd (loop PMap.empty e)
+end
+
 module Fusion = struct
 
 	type interference_kind =

+ 1 - 0
src/typing/matcher.ml

@@ -1033,6 +1033,7 @@ module Compile = struct
 					v,ex_bindings
 				with Not_found ->
 					let v = alloc_var "_hx_tmp" e1.etype e1.epos in
+					v.v_meta <- (Meta.Custom ":extractorVariable",[],v.v_pos) :: v.v_meta;
 					v,(v,e1.epos,e1) :: ex_bindings
 				in
 				let ev = mk (TLocal v) v.v_type e1.epos in

+ 16 - 0
tests/unit/src/unit/issues/Issue5274.hx

@@ -0,0 +1,16 @@
+package unit.issues;
+
+class Issue5274 extends unit.Test {
+	function test() {
+		eq("null", doMatch(null));
+		eq("foobar", doMatch("foo,bar"));
+	}
+
+	static function doMatch(s:String) {
+		return switch (s) {
+			case null: "null";
+			case _.split(",") => [s1, s2]: s1 + s2;
+			default: "default";
+		}
+	}
+}