소스 검색

Fix @:op(A += B) on properties with `never` setter (#8351)

Aleksandr Kuzmenko 6 년 전
부모
커밋
51d32c3a60
2개의 변경된 파일44개의 추가작업 그리고 22개의 파일을 삭제
  1. 30 22
      src/typing/typer.ml
  2. 14 0
      tests/unit/src/unit/issues/Issue8341.hx

+ 30 - 22
src/typing/typer.ml

@@ -472,6 +472,26 @@ let unify_int ctx e k =
 		true
 
 let rec type_binop ctx op e1 e2 is_assign_op with_type p =
+	let type_non_assign_op abstract_overload_only =
+		(* If the with_type is an abstract which has exactly one applicable @:op method, we can promote it
+		   to the individual arguments (issue #2786). *)
+		let wt = match with_type with
+			| WithType.WithType(t,_) ->
+				begin match follow t with
+					| TAbstract(a,_) ->
+						begin match List.filter (fun (o,_) -> o = OpAssignOp(op) || o == op) a.a_ops with
+							| [_] -> with_type
+							| _ -> WithType.value
+						end
+					| _ ->
+						WithType.value
+				end
+			| _ ->
+				WithType.value
+		in
+		let e1 = type_expr ctx e1 wt in
+		type_binop2 ~abstract_overload_only ctx op e1 e2 is_assign_op wt p
+	in
 	match op with
 	| OpAssign ->
 		let e1 = type_access ctx (fst e1) (snd e1) MSet in
@@ -519,7 +539,11 @@ let rec type_binop ctx op e1 e2 is_assign_op with_type p =
 		error "The operators ||= and &&= are not supported" p
 	| OpAssignOp op ->
 		(match type_access ctx (fst e1) (snd e1) MSet with
-		| AKNo s -> error ("Cannot access field or identifier " ^ s ^ " for writing") p
+		| AKNo s ->
+			(* try abstract operator overloading *)
+			(try type_non_assign_op true
+			with Not_found -> error ("Cannot access field or identifier " ^ s ^ " for writing") p
+			)
 		| AKExpr e ->
 			let save = save_locals ctx in
 			let v = gen_local ctx e.etype e.epos in
@@ -656,26 +680,9 @@ let rec type_binop ctx op e1 e2 is_assign_op with_type p =
 		| AKInline _ | AKMacro _ ->
 			assert false)
 	| _ ->
-		(* If the with_type is an abstract which has exactly one applicable @:op method, we can promote it
-		   to the individual arguments (issue #2786). *)
-		let wt = match with_type with
-			| WithType.WithType(t,_) ->
-				begin match follow t with
-					| TAbstract(a,_) ->
-						begin match List.filter (fun (o,_) -> o = OpAssignOp(op) || o == op) a.a_ops with
-							| [_] -> with_type
-							| _ -> WithType.value
-						end
-					| _ ->
-						WithType.value
-				end
-			| _ ->
-				WithType.value
-		in
-		let e1 = type_expr ctx e1 wt in
-		type_binop2 ctx op e1 e2 is_assign_op wt p
+		type_non_assign_op false
 
-and type_binop2 ctx op (e1 : texpr) (e2 : Ast.expr) is_assign_op wt p =
+and type_binop2 ?(abstract_overload_only=false) ctx op (e1 : texpr) (e2 : Ast.expr) is_assign_op wt p =
 	let with_type = match op with
 		| OpEq | OpNotEq | OpLt | OpLte | OpGt | OpGte -> WithType.with_type e1.etype
 		| _ -> wt
@@ -913,7 +920,7 @@ and type_binop2 ctx op (e1 : texpr) (e2 : Ast.expr) is_assign_op wt p =
 		in
 		(* special case for == and !=: if the second type is a monomorph, assume that we want to unify
 		   it with the first type to preserve comparison semantics. *)
-	let is_eq_op = match op with OpEq | OpNotEq -> true | _ -> false in
+		let is_eq_op = match op with OpEq | OpNotEq -> true | _ -> false in
 		if is_eq_op then begin match follow e1.etype,follow e2.etype with
 			| TMono _,_ | _,TMono _ ->
 				Type.unify e1.etype e2.etype
@@ -1003,7 +1010,8 @@ and type_binop2 ctx op (e1 : texpr) (e2 : Ast.expr) is_assign_op wt p =
 			| _ -> raise Not_found
 		end
 	with Not_found ->
-		make e1 e2
+		if abstract_overload_only then raise Not_found
+		else make e1 e2
 
 and type_unop ctx op flag e p =
 	let set = (op = Increment || op = Decrement) in

+ 14 - 0
tests/unit/src/unit/issues/Issue8341.hx

@@ -0,0 +1,14 @@
+package unit.issues;
+
+class Issue8341 extends unit.Test {
+	static var list(default, never):CustomArray = [];
+
+	function test() {
+		list += 10;
+		aeq([10], list);
+	}
+}
+
+abstract CustomArray(Array<Int>) from Array<Int> to Array<Int> {
+	@:op(A+=B) inline function add(value:Int) this.push(value);
+}