Browse Source

[inline] check finalness of locals and fields when written

see #10845
Simon Krajewski 2 years ago
parent
commit
d7eac8a4ba

+ 17 - 5
src/optimization/inline.ml

@@ -369,11 +369,23 @@ class inline_state ctx ethis params cf f p = object(self)
 			in
 			try loop e; true with Exit -> false
 		in
-		let rec is_writable e =
+		let rec check_write e =
 			match e.eexpr with
-			| TField _ | TEnumParameter _ | TLocal _ | TArray _ -> true
-			| TCast(e1,None) -> is_writable e1
-			| _  -> false
+			| TLocal v when has_var_flag v VFinal ->
+				typing_error "Cannot modify abstract value of final local" p
+			| TField(_,fa) ->
+				begin match extract_field fa with
+				| Some cf when has_class_field_flag cf CfFinal ->
+					typing_error "Cannot modify abstract value of final field" p
+				| _ ->
+					()
+				end
+			| TLocal _ | TEnumParameter _ | TArray _ ->
+				()
+			| TCast(e1,None) ->
+				check_write e1
+			| _  ->
+				typing_error "Cannot modify the abstract value, store it into a local first" p;
 		in
 		let vars = List.fold_left (fun acc (i,e) ->
 			let accept vik =
@@ -387,7 +399,7 @@ class inline_state ctx ethis params cf f p = object(self)
 				(i.i_subst,Some e) :: acc
 			in
 			if i.i_abstract_this && i.i_write then begin
-				if not (is_writable e) then typing_error "Cannot modify the abstract value, store it into a local first" p;
+				check_write e;
 				accept VIInline
 			end else if i.i_force_temp || (i.i_captured && not (is_constant e)) then
 				reject()

+ 47 - 0
tests/misc/projects/Issue10845/Main.hx

@@ -0,0 +1,47 @@
+abstract MyInt(Int) from Int to Int {
+	@:op(A += B)
+	public inline function addEq(x:Int):MyInt {
+		return this += x;
+	}
+
+	@:op(A++)
+	public inline function increment():MyInt {
+		return this++;
+	}
+}
+
+class MyClass {
+	final field:MyInt = 1;
+
+	function func():Void {
+		final local:MyInt = 1;
+
+		// --- overriden operators ---
+
+		field++; // Error: The field or identifier field is not accessible for writing
+		local++;
+
+		field += 1;
+		local += 1; // Error: Cannot assign to final
+
+		// --- raw operators ---
+
+		field--; // Error: The field or identifier field is not accessible for writing
+		local--; // Error: Cannot assign to final
+
+		field -= 1; // Error: Cannot access field or identifier field for writing
+		local -= 1; // Error: Cannot assign to final
+
+		// --- method calls ---
+
+		field.addEq(1);
+		local.addEq(1);
+
+		field.increment();
+		local.increment();
+	}
+}
+
+function main() {
+
+}

+ 2 - 0
tests/misc/projects/Issue10845/compile-fail.hxml

@@ -0,0 +1,2 @@
+--main Main
+--interp

+ 12 - 0
tests/misc/projects/Issue10845/compile-fail.hxml.stderr

@@ -0,0 +1,12 @@
+Main.hx:21: characters 3-10 : The field or identifier field is not accessible for writing
+Main.hx:22: characters 3-10 : Cannot modify abstract value of final local
+Main.hx:24: characters 3-13 : Cannot modify abstract value of final field
+Main.hx:25: characters 3-13 : Cannot modify abstract value of final local
+Main.hx:29: characters 3-10 : The field or identifier field is not accessible for writing
+Main.hx:30: characters 3-8 : Cannot assign to final
+Main.hx:32: characters 3-13 : Cannot access field or identifier field for writing
+Main.hx:33: characters 3-8 : Cannot assign to final
+Main.hx:37: characters 3-17 : Cannot modify abstract value of final field
+Main.hx:38: characters 3-17 : Cannot modify abstract value of final local
+Main.hx:40: characters 3-20 : Cannot modify abstract value of final field
+Main.hx:41: characters 3-20 : Cannot modify abstract value of final local