Przeglądaj źródła

@:semantics (#7463)

* generalize type semantics to using `@:semantics`

* add `@:semantics(value)`, use it on php's NativeIndexedArray

* don't forget about C#'s Out

* [php] NativeArray and NativeAssocArray are value types
Simon Krajewski 6 lat temu
rodzic
commit
f912a38afe

+ 34 - 1
src/core/ast.ml

@@ -1043,4 +1043,37 @@ module Expr = struct
 		in
 		loop' "" e;
 		Buffer.contents buf
-end
+end
+
+let has_meta_option metas meta s =
+	let rec loop ml = match ml with
+		| (meta',el,_) :: ml when meta = meta' ->
+			if List.exists (fun (e,p) ->
+				match e with
+					| EConst(Ident s2) when s = s2 -> true
+					| _ -> false
+			) el then
+				true
+			else
+				loop ml
+		| _ :: ml ->
+			loop ml
+		| [] ->
+			false
+	in
+	loop metas
+
+let get_meta_options metas meta =
+	let rec loop ml = match ml with
+		| (meta',el,_) :: ml when meta = meta' ->
+			ExtList.List.filter_map (fun (e,p) ->
+				match e with
+					| EConst(Ident s2) -> Some s2
+					| _ -> None
+			) el
+		| _ :: ml ->
+			loop ml
+		| [] ->
+			[]
+	in
+	loop metas

+ 2 - 0
src/core/meta.ml

@@ -143,6 +143,7 @@ type strict_meta =
 	| RuntimeValue
 	| Scalar
 	| SelfCall
+	| Semantics
 	| Setter
 	| SkipCtor
 	| SkipReflection
@@ -342,6 +343,7 @@ let get_info = function
 	| RuntimeValue -> ":runtimeValue",("Marks an abstract as being a runtime value",[UsedOn TAbstract])
 	| Scalar -> ":scalar",("Used by hxcpp to mark a custom coreType abstract",[UsedOn TAbstract; Platform Cpp])
 	| SelfCall -> ":selfCall",("Translates method calls into calling object directly",[UsedOn TClassField; Platforms [Js;Lua]])
+	| Semantics -> ":semantics",("The native semantics of the type",[UsedOnEither [TClass;TTypedef;TAbstract];HasParam "value | reference | variable"])
 	| Setter -> ":setter",("Generates a native setter function on the given field",[HasParam "Class field name";UsedOn TClassField;Platform Flash])
 	| SkipCtor -> ":skipCtor",("Used internally to generate a constructor as if it were a native type (no __hx_ctor)",[Platforms [Java;Cs]; UsedInternally])
 	| SkipReflection -> ":skipReflection",("Used internally to annotate a field that shouldn't have its reflection data generated",[Platforms [Java;Cs]; UsedOn TClassField; UsedInternally])

+ 34 - 0
src/core/type.ml

@@ -2725,6 +2725,40 @@ module ExtType = struct
 	let is_void = function
 		| TAbstract({a_path=[],"Void"},_) -> true
 		| _ -> false
+
+	type semantics =
+		| VariableSemantics
+		| ReferenceSemantics
+		| ValueSemantics
+
+	let semantics_name = function
+		| VariableSemantics -> "variable"
+		| ReferenceSemantics -> "reference"
+		| ValueSemantics -> "value"
+
+	let has_semantics t sem =
+		let name = semantics_name sem in
+		let check meta =
+			has_meta_option meta Meta.Semantics name
+		in
+		let rec loop t = match t with
+			| TInst(c,_) -> check c.cl_meta
+			| TEnum(en,_) -> check en.e_meta
+			| TType(t,tl) -> check t.t_meta || (loop (apply_params t.t_params tl t.t_type))
+			| TAbstract(a,_) -> check a.a_meta
+			| TLazy f -> loop (lazy_type f)
+			| TMono r ->
+				(match !r with
+				| Some t -> loop t
+				| _ -> false)
+			| _ ->
+				false
+		in
+		loop t
+
+	let has_variable_semantics t = has_semantics t VariableSemantics
+	let has_reference_semantics t = has_semantics t ReferenceSemantics
+	let has_value_semantics t = has_semantics t ValueSemantics
 end
 
 module StringError = struct

+ 2 - 2
src/optimization/analyzer.ml

@@ -485,7 +485,7 @@ module ConstPropagation = DataFlow(struct
 				if not (type_change_ok ctx.com t e.etype) then raise Not_found;
 				mk (TTypeExpr mt) t e.epos
 		in
-		let is_special_var v = v.v_capture || is_asvar_type v.v_type in
+		let is_special_var v = v.v_capture || ExtType.has_variable_semantics v.v_type in
 		let rec commit e = match e.eexpr with
 			| TLocal v when not (is_special_var v) ->
 				begin try
@@ -638,7 +638,7 @@ module LocalDce = struct
 			Meta.has Meta.Used v.v_meta
 		in
 		let keep v =
-			is_used v || ((match v.v_kind with VUser _ | VInlined -> true | _ -> false) && not ctx.config.local_dce) || is_ref_type v.v_type || v.v_capture || Meta.has Meta.This v.v_meta
+			is_used v || ((match v.v_kind with VUser _ | VInlined -> true | _ -> false) && not ctx.config.local_dce) || ExtType.has_reference_semantics v.v_type || v.v_capture || Meta.has Meta.This v.v_meta
 		in
 		let rec use v =
 			if not (is_used v) then begin

+ 1 - 19
src/optimization/analyzerConfig.ml

@@ -57,25 +57,7 @@ let all_flags =
 	) [] [flag_optimize;flag_const_propagation;flag_copy_propagation;flag_local_dce;flag_fusion;flag_ignore;flag_dot_debug;flag_user_var_fusion]
 
 let has_analyzer_option meta s =
-	try
-		let rec loop ml = match ml with
-			| (Meta.Analyzer,el,_) :: ml ->
-				if List.exists (fun (e,p) ->
-					match e with
-						| EConst(Ident s2) when s = s2 -> true
-						| _ -> false
-				) el then
-					true
-				else
-					loop ml
-			| _ :: ml ->
-				loop ml
-			| [] ->
-				false
-		in
-		loop meta
-	with Not_found ->
-		false
+	Ast.has_meta_option meta Meta.Analyzer s
 
 let is_ignored meta =
 	has_analyzer_option meta flag_ignore

+ 3 - 27
src/optimization/analyzerTexpr.ml

@@ -150,30 +150,6 @@ let is_unbound_call_that_might_have_side_effects s el = match s,el with
 	| "__js__",[{eexpr = TConst (TString s)}] when Str.string_match r s 0 -> false
 	| _ -> true
 
-let is_ref_type = function
-	| TType({t_path = ["cs"],("Ref" | "Out")},_) -> true
-	| TType({t_path = path},_) when path = Genphp7.ref_type_path -> true
-	| TType({t_path = ["cpp"],("Reference")},_) -> true
-	| TAbstract({a_path=["hl";"types"],"Ref"},_) -> true
-	| _ -> false
-
-let rec is_asvar_type t =
-	let check meta =
-		AnalyzerConfig.has_analyzer_option meta "as_var"
-	in
-	match t with
-	| TInst(c,_) -> check c.cl_meta
-	| TEnum(en,_) -> check en.e_meta
-	| TType(t,tl) -> check t.t_meta || (is_asvar_type (apply_params t.t_params tl t.t_type))
-	| TAbstract(a,_) -> check a.a_meta
-	| TLazy f -> is_asvar_type (lazy_type f)
-	| TMono r ->
-		(match !r with
-		| Some t -> is_asvar_type t
-		| _ -> false)
-	| _ ->
-		false
-
 let type_change_ok com t1 t2 =
 	if t1 == t2 then
 		true
@@ -716,7 +692,7 @@ module Fusion = struct
 			let b = num_uses <= 1 &&
 			        num_writes = 0 &&
 			        can_be_used_as_value &&
-					not (is_asvar_type v.v_type) &&
+					not (ExtType.has_variable_semantics v.v_type) &&
 			        (is_compiler_generated || config.optimize && config.fusion && config.user_var_fusion && not has_type_params)
 			in
 			if config.fusion_debug then begin
@@ -868,7 +844,7 @@ module Fusion = struct
 							found := true;
 							if type_change_ok com v1.v_type e1.etype then e1 else mk (TCast(e1,None)) v1.v_type e.epos
 						| TLocal v ->
-							if has_var_write ir v || ((v.v_capture || is_ref_type v.v_type) && (has_state_write ir)) then raise Exit;
+							if has_var_write ir v || ((v.v_capture || ExtType.has_reference_semantics v.v_type) && (has_state_write ir)) then raise Exit;
 							e
 						| TBinop(OpAssign,({eexpr = TLocal v} as e1),e2) ->
 							let e2 = replace e2 in
@@ -1225,7 +1201,7 @@ module Purity = struct
 		let rec check_write e1 =
 			begin match e1.eexpr with
 				| TLocal v ->
-					if is_ref_type v.v_type then taint_raise node; (* Writing to a ref type means impurity. *)
+					if ExtType.has_reference_semantics v.v_type then taint_raise node; (* Writing to a ref type means impurity. *)
 					() (* Writing to locals does not violate purity. *)
 				| TField({eexpr = TConst TThis},_) when is_ctor ->
 					() (* A constructor can write to its own fields without violating purity. *)

+ 2 - 2
src/optimization/analyzerTexprTransformer.ml

@@ -305,11 +305,11 @@ let rec func ctx bb tf t p =
 	and call bb e e1 el =
 		let bb = ref bb in
 		let check e t = match e.eexpr with
-			| TLocal v when is_ref_type t ->
+			| TLocal v when ExtType.has_reference_semantics t ->
 				v.v_capture <- true;
 				e
 			| _ ->
-				if is_asvar_type t then begin
+				if ExtType.has_variable_semantics t then begin
 					let v = alloc_var VGenerated "tmp" t e.epos in
 					let bb',e = bind_to_temp ~v:(Some v) !bb false e in
 					bb := bb';

+ 1 - 1
src/optimization/inline.ml

@@ -415,7 +415,7 @@ class inline_state ctx ethis params cf f p = object(self)
 	method finalize config e tl tret p =
 		let has_params,map_type = match config with Some config -> config | None -> inline_default_config cf ethis.etype in
 		if self#had_side_effect then List.iter (fun (l,e) ->
-			if self#might_be_affected e then l.i_force_temp <- true;
+			if self#might_be_affected e && not (ExtType.has_value_semantics e.etype) then l.i_force_temp <- true;
 		) _inlined_vars;
 		let vars,subst = self#get_substitutions p in
 		let rec inline_params in_call in_assignment e =

+ 1 - 1
std/cpp/ConstPointer.hx

@@ -21,7 +21,7 @@
  */
  package cpp;
 
-@:coreType @:include("cpp/Pointer.h") @:native("cpp.Pointer") @:analyzer(as_var)
+@:coreType @:include("cpp/Pointer.h") @:native("cpp.Pointer") @:semantics(variable)
 extern class ConstPointer<T>
 {
    // ptr actually returns the pointer - not strictly a 'T' - for pointers to smart pointers

+ 1 - 1
std/cpp/Pointer.hx

@@ -24,7 +24,7 @@
 import haxe.extern.AsVar;
 
 @:coreType
-@:analyzer(as_var)
+@:semantics(variable)
 extern class Pointer<T> extends ConstPointer<T> implements ArrayAccess<T>
 {
    public var ref(get,set):Reference<T>;

+ 1 - 0
std/cpp/Reference.hx

@@ -23,6 +23,7 @@ package cpp;
 
 // Allows haxe to type result correctly, and hxcpp can recognise this and prevent
 //  unwanted casting
+@:semantics(reference)
 typedef Reference<T> = T;
 
 

+ 1 - 0
std/cs/Out.hx

@@ -29,4 +29,5 @@ package cs;
 **/
 @:analyzer(no_simplification)
 @:analyzer(no_local_dce)
+@:semantics(reference)
 typedef Out<T> = T;

+ 1 - 0
std/cs/Ref.hx

@@ -28,4 +28,5 @@ package cs;
 	Note: Using this type should be considered a bad practice unless overriding a native function is needed.
 **/
 @:analyzer(no_simplification)
+@:semantics(reference)
 typedef Ref<T> = T;

+ 1 - 1
std/haxe/extern/AsVar.hx

@@ -27,5 +27,5 @@ package haxe.extern;
 	argument expressions are bound to a local variable.
 **/
 @:forward
-@:analyzer(as_var)
+@:semantics(variable)
 abstract AsVar<T>(T) from T to T {}

+ 1 - 0
std/hl/Ref.hx

@@ -21,6 +21,7 @@
  */
 package hl;
 
+@:semantics(reference)
 @:coreType abstract Ref<T> {
 
 	@:from extern public static inline function make<T>( v : T ) {

+ 1 - 1
std/php/NativeArray.hx

@@ -26,7 +26,7 @@ using php.Global;
 /**
 	Native PHP array.
 **/
-@:coreType @:runtimeValue abstract NativeArray {
+@:coreType @:runtimeValue @:semantics(value) abstract NativeArray {
 	public inline function new()
 		this = Syntax.arrayDecl();
 

+ 1 - 0
std/php/NativeAssocArray.hx

@@ -22,6 +22,7 @@
 package php;
 
 @:forward
+@:semantics(value)
 abstract NativeAssocArray<T>(NativeArray) from NativeArray to NativeArray {
 	public inline function new()
 		this = Syntax.arrayDecl();

+ 1 - 0
std/php/NativeIndexedArray.hx

@@ -22,6 +22,7 @@
 package php;
 
 @:forward
+@:semantics(value)
 abstract NativeIndexedArray<T>(NativeArray) from NativeArray to NativeArray {
 	public inline function new()
 		this = Syntax.arrayDecl();

+ 1 - 0
std/php/Ref.hx

@@ -4,4 +4,5 @@ package php;
 	Special type which allows passing function arguments by reference.
 	This type should be used for externs only.
 **/
+@:semantics(reference)
 typedef Ref<T> = T;