Browse Source

[dce] keep constructor if any instance field is kept (fixes #6062) (#6690)

* [dce] keep constructor if any instance field is kept (fixes #6062)

* [dce] keep constructor if super constructor is kept (fixes #6062 even more)

* improved test case (#6062)

* mark constructor as kept if instance field is marked (#6062)

* removed obsolete check from keep_field (#6062)

* and obsolete function

* comment [skip ci]

* cleanup
Alexander Kuzmenko 8 years ago
parent
commit
6d807caa85
2 changed files with 46 additions and 7 deletions
  1. 21 7
      src/optimization/dce.ml
  2. 25 0
      tests/unit/src/unit/issues/Issue6062.hx

+ 21 - 7
src/optimization/dce.ml

@@ -67,12 +67,22 @@ let keep_whole_enum dce en =
 	Meta.has Meta.Keep en.e_meta
 	|| not (dce.full || is_std_file dce en.e_module.m_extra.m_file || has_meta Meta.Dce en.e_meta)
 
-(* check if a field is kept *)
-let keep_field dce cf =
+(*
+	Check if a field is kept.
+	`keep_field` is checked to determine the DCE entry points, i.e. all fields that have `@:keep` or kept for other reasons.
+	And then it is used at the end to check which fields can be filtered from their classes.
+*)
+let rec keep_field dce cf c =
 	Meta.has Meta.Keep cf.cf_meta
 	|| Meta.has Meta.Used cf.cf_meta
 	|| cf.cf_name = "__init__"
 	|| is_extern_field cf
+	|| (
+		cf.cf_name = "new"
+		&& match c.cl_super with (* parent class kept constructor *)
+			| Some ({ cl_constructor = Some ctor } as csup, _) -> keep_field dce ctor csup
+			| _ -> false
+	)
 
 (* marking *)
 
@@ -117,7 +127,11 @@ and mark_field dce c cf stat =
 			| None -> add cf
 			| Some (c,_) -> mark_field dce c cf stat
 		end else
-			add cf
+			add cf;
+		if not stat then
+			match c.cl_constructor with
+				| None -> ()
+				| Some ctor -> mark_field dce c ctor false
 	end
 
 let rec update_marked_class_fields dce c =
@@ -662,7 +676,7 @@ let run com main full =
 		| TClassDecl c ->
 			let keep_class = keep_whole_class dce c && (not c.cl_extern || c.cl_interface) in
 			let loop stat cf =
-				if keep_class || keep_field dce cf then mark_field dce c cf stat
+				if keep_class || keep_field dce cf c then mark_field dce c cf stat
 			in
 			List.iter (loop true) c.cl_ordered_statics;
 			List.iter (loop false) c.cl_ordered_fields;
@@ -747,7 +761,7 @@ let run com main full =
 			(* add :keep so subsequent filter calls do not process class fields again *)
 			c.cl_meta <- (Meta.Keep,[],c.cl_pos) :: c.cl_meta;
  			c.cl_ordered_statics <- List.filter (fun cf ->
-				let b = keep_field dce cf in
+				let b = keep_field dce cf c in
 				if not b then begin
 					if dce.debug then print_endline ("[DCE] Removed field " ^ (s_type_path c.cl_path) ^ "." ^ (cf.cf_name));
 					check_property cf true;
@@ -756,7 +770,7 @@ let run com main full =
 				b
 			) c.cl_ordered_statics;
 			c.cl_ordered_fields <- List.filter (fun cf ->
-				let b = keep_field dce cf in
+				let b = keep_field dce cf c in
 				if not b then begin
 					if dce.debug then print_endline ("[DCE] Removed field " ^ (s_type_path c.cl_path) ^ "." ^ (cf.cf_name));
 					check_property cf false;
@@ -764,7 +778,7 @@ let run com main full =
 				end;
 				b
 			) c.cl_ordered_fields;
-			(match c.cl_constructor with Some cf when not (keep_field dce cf) -> c.cl_constructor <- None | _ -> ());
+			(match c.cl_constructor with Some cf when not (keep_field dce cf c) -> c.cl_constructor <- None | _ -> ());
 			let inef cf = not (is_extern_field cf) in
 			let has_non_extern_fields = List.exists inef c.cl_ordered_fields || List.exists inef c.cl_ordered_statics in
 			(* we keep a class if it was used or has a used field *)

+ 25 - 0
tests/unit/src/unit/issues/Issue6062.hx

@@ -0,0 +1,25 @@
+package unit.issues;
+
+class Issue6062 extends unit.Test {
+	function test() {
+		var child = Type.createInstance(Child, []);
+		t(child.parentFn());
+		t(Parent.tmp);
+	}
+}
+
+private class Parent {
+	static public var tmp:Bool = false;
+
+	public var parentFn = function() return true;
+
+	function new() {
+		tmp = OnlyParentNewReferencesMe.field;
+	}
+}
+
+private class Child extends Parent {}
+
+private class OnlyParentNewReferencesMe {
+	static public var field = true;
+}