浏览代码

[typer] support write-mode `@:op(a.b)`

Simon Krajewski 7 年之前
父节点
当前提交
c27156a950

+ 1 - 0
extra/CHANGES.txt

@@ -2,6 +2,7 @@ XXXX-XX-XX:
 
 	General improvements and optimizations:
 
+	all : added support for write-mode `@:op(a.b)`
 	all : made `final` on structure fields invariant (#7039)
 	all : [breaking] disallowed static variables that have no type-hint and expression (#6440)
 	php : Optimized haxe.ds.Vector (VectorData is not Array anymore)

+ 7 - 4
src/context/display/deprecationCheck.ml

@@ -7,15 +7,18 @@ let curclass = ref null_class
 
 let warned_positions = Hashtbl.create 0
 
+let warn_deprecation com s p_usage =
+	if not (Hashtbl.mem warned_positions p_usage) then begin
+		Hashtbl.replace warned_positions p_usage true;
+		com.warning s p_usage;
+	end
+
 let print_deprecation_message com meta s p_usage =
 	let s = match meta with
 		| _,[EConst(String s),_],_ -> s
 		| _ -> Printf.sprintf "Usage of this %s is deprecated" s
 	in
-	if not (Hashtbl.mem warned_positions p_usage) then begin
-		Hashtbl.replace warned_positions p_usage true;
-		com.warning s p_usage;
-	end
+	warn_deprecation com s p_usage
 
 let check_meta com meta s p_usage =
 	try

+ 2 - 1
src/core/json/genjson.ml

@@ -643,7 +643,8 @@ let generate_abstract ctx a =
 		"from",generate_casts a.a_from_field a.a_from;
 		"to",generate_casts a.a_to_field a.a_to;
 		"array",jlist (classfield_ref ctx) a.a_array;
-		"resolve",jopt (classfield_ref ctx) a.a_resolve;
+		"read",jopt (classfield_ref ctx) a.a_read;
+		"write",jopt (classfield_ref ctx) a.a_write;
 	]
 
 let generate_module_type ctx mt =

+ 6 - 3
src/core/type.ml

@@ -312,7 +312,8 @@ and tabstract = {
 	mutable a_to : t list;
 	mutable a_to_field : (t * tclass_field) list;
 	mutable a_array : tclass_field list;
-	mutable a_resolve : tclass_field option;
+	mutable a_read : tclass_field option;
+	mutable a_write : tclass_field option;
 }
 
 and module_type =
@@ -517,7 +518,8 @@ let null_abstract = {
 	a_to = [];
 	a_to_field = [];
 	a_array = [];
-	a_resolve = None;
+	a_read = None;
+	a_write = None;
 }
 
 let add_dependency m mdep =
@@ -1508,7 +1510,8 @@ module Printer = struct
 			"a_from_field",s_list ", " (fun (t,cf) -> Printf.sprintf "%s: %s" (s_type_kind t) cf.cf_name) a.a_from_field;
 			"a_to_field",s_list ", " (fun (t,cf) -> Printf.sprintf "%s: %s" (s_type_kind t) cf.cf_name) a.a_to_field;
 			"a_array",s_list ", " (fun cf -> cf.cf_name) a.a_array;
-			"a_resolve",s_opt (fun cf -> cf.cf_name) a.a_resolve;
+			"a_read",s_opt (fun cf -> cf.cf_name) a.a_read;
+			"a_write",s_opt (fun cf -> cf.cf_name) a.a_write;
 		]
 
 	let s_tvar_extra (tl,eo) =

+ 2 - 1
src/macro/macroApi.ml

@@ -953,7 +953,8 @@ and encode_tabstract a =
 		"from", encode_array ((List.map (fun t -> encode_obj OAbstractType_from [ "t",encode_type t; "field",vnull]) a.a_from) @ (List.map (fun (t,cf) -> encode_obj OAbstractType_from [ "t",encode_type t; "field",encode_cfield cf]) a.a_from_field));
 		"to", encode_array ((List.map (fun t -> encode_obj OAbstractType_to [ "t",encode_type t; "field",vnull]) a.a_to) @ (List.map (fun (t,cf) -> encode_obj OAbstractType_to [ "t",encode_type t; "field",encode_cfield cf]) a.a_to_field));
 		"array", encode_array (List.map encode_cfield a.a_array);
-		"resolve", (match a.a_resolve with None -> vnull | Some cf -> encode_cfref cf)
+		"resolve", (match a.a_read with None -> vnull | Some cf -> encode_cfref cf);
+		"resolveWrite", (match a.a_write with None -> vnull | Some cf -> encode_cfref cf)
 	]
 
 and encode_efield f =

+ 2 - 2
src/typing/calls.ml

@@ -407,7 +407,7 @@ let rec acc_get ctx g p =
 	match g with
 	| AKNo f -> error ("Field " ^ f ^ " cannot be accessed for reading") p
 	| AKExpr e -> e
-	| AKSet _ | AKAccess _ -> assert false
+	| AKSet _ | AKAccess _ | AKFieldSet _ -> assert false
 	| AKUsing (et,c,cf,e) when ctx.in_display ->
 		(* Generate a TField node so we can easily match it for position/usage completion (issue #1968) *)
 		let ec = type_module_type ctx (TClassDecl c) None p in
@@ -607,7 +607,7 @@ let rec build_call ctx acc el (with_type:with_type) p =
 		ctx.on_error <- old;
 		!ethis_f();
 		e
-	| AKNo _ | AKSet _ | AKAccess _ ->
+	| AKNo _ | AKSet _ | AKAccess _ | AKFieldSet _ ->
 		ignore(acc_get ctx acc p);
 		assert false
 	| AKExpr e ->

+ 17 - 7
src/typing/fields.ml

@@ -527,14 +527,24 @@ let rec type_field ?(resume=false) ctx e i p mode =
 			| FunMemberAbstract, TConst (TThis) -> type_field ctx {e with etype = apply_params a.a_params pl a.a_this} i p mode;
 			| _ -> raise Not_found)
 		with Not_found -> try
-			let c,cf = match a.a_impl,a.a_resolve with
-				| Some c,Some cf -> c,cf
-				| _ -> raise Not_found
+			let get_resolve is_write =
+				let c,cf = match a.a_impl,(if is_write then a.a_write else a.a_read) with
+					| Some c,Some cf -> c,cf
+					| _ -> raise Not_found
+				in
+				let et = type_module_type ctx (TClassDecl c) None p in
+				let t = apply_params a.a_params pl (field_type ctx c [] cf p) in
+				let ef = mk (TField (et,FStatic (c,cf))) t p in
+				let r = match follow t with
+					| TFun(_,r) -> r
+					| _ -> assert false
+				in
+				if is_write then
+					AKFieldSet(e,ef,i,r)
+				else
+					AKExpr ((!build_call_ref) ctx (AKUsing(ef,c,cf,e)) [EConst (String i),p] NoValue p)
 			in
-			let et = type_module_type ctx (TClassDecl c) None p in
-			let t = apply_params a.a_params pl (field_type ctx c [] cf p) in
-			let ef = mk (TField (et,FStatic (c,cf))) t p in
-			AKExpr ((!build_call_ref) ctx (AKUsing(ef,c,cf,e)) [EConst (String i),p] NoValue p)
+			get_resolve (mode = MSet)
 		with Not_found ->
 			if !static_abstract_access_through_instance then error ("Invalid call to static function " ^ i ^ " through abstract instance") p
 			else no_field())

+ 13 - 6
src/typing/typeloadFields.ml

@@ -883,18 +883,25 @@ let check_abstract (ctx,cctx,fctx) c cf fd t ret p =
 					end;
 					loop ml
 				| ((Meta.Resolve,_,_) | (Meta.Op,[EField _,_],_)) :: _ ->
-					if a.a_resolve <> None then error "Multiple resolve methods are not supported" cf.cf_pos;
 					let targ = if fctx.is_abstract_member then tthis else ta in
+					let check_fun t1 t2 =
+						if not fctx.is_macro then begin
+							if not (type_iseq targ t1) then error ("First argument type must be " ^ (s_type (print_context()) targ)) cf.cf_pos;
+							if not (type_iseq ctx.t.tstring t2) then error ("Second argument type must be String") cf.cf_pos
+						end
+					in
 					begin match follow t with
 						| TFun([(_,_,t1);(_,_,t2)],_) ->
-							if not fctx.is_macro then begin
-								if not (type_iseq targ t1) then error ("First argument type must be " ^ (s_type (print_context()) targ)) cf.cf_pos;
-								if not (type_iseq ctx.t.tstring t2) then error ("Second argument type must be String") cf.cf_pos
-							end
+							if a.a_read <> None then error "Multiple resolve-read methods are not supported" cf.cf_pos;
+							check_fun t1 t2;
+							a.a_read <- Some cf;
+						| TFun([(_,_,t1);(_,_,t2);(_,_,t3)],_) ->
+							if a.a_write <> None then error "Multiple resolve-write methods are not supported" cf.cf_pos;
+							check_fun t1 t2;
+							a.a_write <- Some cf;
 						| _ ->
 							error ("Field type of resolve must be " ^ (s_type (print_context()) targ) ^ " -> String -> T") cf.cf_pos
 					end;
-					a.a_resolve <- Some cf;
 				| _ :: ml ->
 					loop ml
 				| [] ->

+ 2 - 1
src/typing/typeloadModule.ml

@@ -285,7 +285,8 @@ let module_pass_1 ctx m tdecls loadp =
 				a_impl = None;
 				a_array = [];
 				a_this = mk_mono();
-				a_resolve = None;
+				a_read = None;
+				a_write = None;
 			} in
 			decls := (TAbstractDecl a, decl) :: !decls;
 			match d.d_data with

+ 11 - 1
src/typing/typer.ml

@@ -488,7 +488,7 @@ let rec type_binop ctx op e1 e2 is_assign_op with_type p =
 	match op with
 	| OpAssign ->
 		let e1 = type_access ctx (fst e1) (snd e1) MSet in
-		let tt = (match e1 with AKNo _ | AKInline _ | AKUsing _ | AKMacro _ | AKAccess _ -> Value | AKSet(_,t,_) -> WithType t | AKExpr e -> WithType e.etype) in
+		let tt = (match e1 with AKNo _ | AKInline _ | AKUsing _ | AKMacro _ | AKAccess _ -> Value | AKFieldSet(_,_,_,t) | AKSet(_,t,_) -> WithType t | AKExpr e -> WithType e.etype) in
 		let e2 = type_expr ctx e2 tt in
 		(match e1 with
 		| AKNo s -> error ("Cannot access field or identifier " ^ s ^ " for writing") p
@@ -506,6 +506,12 @@ let rec type_binop ctx op e1 e2 is_assign_op with_type p =
 			make_call ctx (mk (TField (e,quick_field_dynamic e.etype ("set_" ^ cf.cf_name))) (tfun [t] t) p) [e2] t p
 		| AKAccess(a,tl,c,ebase,ekey) ->
 			mk_array_set_call ctx (AbstractCast.find_array_access ctx a tl ekey (Some e2) p) c ebase p
+		| AKFieldSet(ethis,e1,fname,t) ->
+			begin match follow e1.etype with
+				| TFun([_;_;(_,_,t)],_) -> unify ctx e2.etype t e2.epos;
+				| _ -> assert false
+			end;
+			make_call ctx e1 [ethis;Texpr.Builder.make_string ctx.t fname null_pos;e2] t p
 		| AKUsing(ef,_,_,et) ->
 			(* this must be an abstract setter *)
 			let e2,ret = match follow ef.etype with
@@ -652,6 +658,8 @@ let rec type_binop ctx op e1 e2 is_assign_op with_type p =
 			in
 			save();
 			e
+		| AKFieldSet _ ->
+			error "Invalid operation" p
 		| AKInline _ | AKMacro _ ->
 			assert false)
 	| _ ->
@@ -1089,6 +1097,8 @@ and type_unop ctx op flag e p =
 			end
 		| AKInline _ | AKUsing _ | AKMacro _ ->
 			error "This kind of operation is not supported" p
+		| AKFieldSet _ ->
+			error "Invalid operation" p
 		| AKSet (e,t,cf) ->
 			let l = save_locals ctx in
 			let v = gen_local ctx e.etype p in

+ 2 - 0
src/typing/typerBase.ml

@@ -17,6 +17,7 @@ type access_kind =
 	| AKMacro of texpr * tclass_field
 	| AKUsing of texpr * tclass * tclass_field * texpr
 	| AKAccess of tabstract * tparams * tclass * texpr * texpr
+	| AKFieldSet of texpr * texpr * string * t
 
 type object_decl_kind =
 	| ODKWithStructure of tanon
@@ -145,6 +146,7 @@ let s_access_kind acc =
 	| AKMacro(e,cf) -> Printf.sprintf "AKMacro(%s, %s)" (se e) cf.cf_name
 	| AKUsing(e1,c,cf,e2) -> Printf.sprintf "AKMacro(%s, %s, %s, %s)" (se e1) (s_type_path c.cl_path) cf.cf_name (se e2)
 	| AKAccess(a,tl,c,e1,e2) -> Printf.sprintf "AKAccess(%s, [%s], %s, %s, %s)" (s_type_path a.a_path) (String.concat ", " (List.map st tl)) (s_type_path c.cl_path) (se e1) (se e2)
+	| AKFieldSet(_) -> ""
 
 let has_constructible_constraint ctx tl el p =
 	let ct = (tfun (List.map (fun e -> e.etype) el) ctx.t.tvoid) in

+ 6 - 0
std/haxe/macro/Type.hx

@@ -531,6 +531,12 @@ typedef AbstractType = BaseType & {
 	**/
 	@:require(haxe_ver >= 3.3)
 	var resolve : Null<ClassField>;
+
+	/**
+		The method used for resolving unknown field access, if available.
+	**/
+	@:require(haxe_ver >= 4.0)
+	var resolveWrite : Null<ClassField>;
 }
 
 /**