Pārlūkot izejas kodu

@: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 gadi atpakaļ
vecāks
revīzija
f912a38afe

+ 34 - 1
src/core/ast.ml

@@ -1043,4 +1043,37 @@ module Expr = struct
 		in
 		in
 		loop' "" e;
 		loop' "" e;
 		Buffer.contents buf
 		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
 	| RuntimeValue
 	| Scalar
 	| Scalar
 	| SelfCall
 	| SelfCall
+	| Semantics
 	| Setter
 	| Setter
 	| SkipCtor
 	| SkipCtor
 	| SkipReflection
 	| SkipReflection
@@ -342,6 +343,7 @@ let get_info = function
 	| RuntimeValue -> ":runtimeValue",("Marks an abstract as being a runtime value",[UsedOn TAbstract])
 	| 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])
 	| 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]])
 	| 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])
 	| 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])
 	| 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])
 	| 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
 	let is_void = function
 		| TAbstract({a_path=[],"Void"},_) -> true
 		| TAbstract({a_path=[],"Void"},_) -> true
 		| _ -> false
 		| _ -> 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
 end
 
 
 module StringError = struct
 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;
 				if not (type_change_ok ctx.com t e.etype) then raise Not_found;
 				mk (TTypeExpr mt) t e.epos
 				mk (TTypeExpr mt) t e.epos
 		in
 		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
 		let rec commit e = match e.eexpr with
 			| TLocal v when not (is_special_var v) ->
 			| TLocal v when not (is_special_var v) ->
 				begin try
 				begin try
@@ -638,7 +638,7 @@ module LocalDce = struct
 			Meta.has Meta.Used v.v_meta
 			Meta.has Meta.Used v.v_meta
 		in
 		in
 		let keep v =
 		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
 		in
 		let rec use v =
 		let rec use v =
 			if not (is_used v) then begin
 			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]
 	) [] [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 =
 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 =
 let is_ignored meta =
 	has_analyzer_option meta flag_ignore
 	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
 	| "__js__",[{eexpr = TConst (TString s)}] when Str.string_match r s 0 -> false
 	| _ -> true
 	| _ -> 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 =
 let type_change_ok com t1 t2 =
 	if t1 == t2 then
 	if t1 == t2 then
 		true
 		true
@@ -716,7 +692,7 @@ module Fusion = struct
 			let b = num_uses <= 1 &&
 			let b = num_uses <= 1 &&
 			        num_writes = 0 &&
 			        num_writes = 0 &&
 			        can_be_used_as_value &&
 			        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)
 			        (is_compiler_generated || config.optimize && config.fusion && config.user_var_fusion && not has_type_params)
 			in
 			in
 			if config.fusion_debug then begin
 			if config.fusion_debug then begin
@@ -868,7 +844,7 @@ module Fusion = struct
 							found := true;
 							found := true;
 							if type_change_ok com v1.v_type e1.etype then e1 else mk (TCast(e1,None)) v1.v_type e.epos
 							if type_change_ok com v1.v_type e1.etype then e1 else mk (TCast(e1,None)) v1.v_type e.epos
 						| TLocal v ->
 						| 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
 							e
 						| TBinop(OpAssign,({eexpr = TLocal v} as e1),e2) ->
 						| TBinop(OpAssign,({eexpr = TLocal v} as e1),e2) ->
 							let e2 = replace e2 in
 							let e2 = replace e2 in
@@ -1225,7 +1201,7 @@ module Purity = struct
 		let rec check_write e1 =
 		let rec check_write e1 =
 			begin match e1.eexpr with
 			begin match e1.eexpr with
 				| TLocal v ->
 				| 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. *)
 					() (* Writing to locals does not violate purity. *)
 				| TField({eexpr = TConst TThis},_) when is_ctor ->
 				| TField({eexpr = TConst TThis},_) when is_ctor ->
 					() (* A constructor can write to its own fields without violating purity. *)
 					() (* 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 =
 	and call bb e e1 el =
 		let bb = ref bb in
 		let bb = ref bb in
 		let check e t = match e.eexpr with
 		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;
 				v.v_capture <- true;
 				e
 				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 v = alloc_var VGenerated "tmp" t e.epos in
 					let bb',e = bind_to_temp ~v:(Some v) !bb false e in
 					let bb',e = bind_to_temp ~v:(Some v) !bb false e in
 					bb := bb';
 					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 =
 	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
 		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#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;
 		) _inlined_vars;
 		let vars,subst = self#get_substitutions p in
 		let vars,subst = self#get_substitutions p in
 		let rec inline_params in_call in_assignment e =
 		let rec inline_params in_call in_assignment e =

+ 1 - 1
std/cpp/ConstPointer.hx

@@ -21,7 +21,7 @@
  */
  */
  package cpp;
  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>
 extern class ConstPointer<T>
 {
 {
    // ptr actually returns the pointer - not strictly a 'T' - for pointers to smart pointers
    // 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;
 import haxe.extern.AsVar;
 
 
 @:coreType
 @:coreType
-@:analyzer(as_var)
+@:semantics(variable)
 extern class Pointer<T> extends ConstPointer<T> implements ArrayAccess<T>
 extern class Pointer<T> extends ConstPointer<T> implements ArrayAccess<T>
 {
 {
    public var ref(get,set):Reference<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
 // Allows haxe to type result correctly, and hxcpp can recognise this and prevent
 //  unwanted casting
 //  unwanted casting
+@:semantics(reference)
 typedef Reference<T> = T;
 typedef Reference<T> = T;
 
 
 
 

+ 1 - 0
std/cs/Out.hx

@@ -29,4 +29,5 @@ package cs;
 **/
 **/
 @:analyzer(no_simplification)
 @:analyzer(no_simplification)
 @:analyzer(no_local_dce)
 @:analyzer(no_local_dce)
+@:semantics(reference)
 typedef Out<T> = T;
 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.
 	Note: Using this type should be considered a bad practice unless overriding a native function is needed.
 **/
 **/
 @:analyzer(no_simplification)
 @:analyzer(no_simplification)
+@:semantics(reference)
 typedef Ref<T> = T;
 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.
 	argument expressions are bound to a local variable.
 **/
 **/
 @:forward
 @:forward
-@:analyzer(as_var)
+@:semantics(variable)
 abstract AsVar<T>(T) from T to T {}
 abstract AsVar<T>(T) from T to T {}

+ 1 - 0
std/hl/Ref.hx

@@ -21,6 +21,7 @@
  */
  */
 package hl;
 package hl;
 
 
+@:semantics(reference)
 @:coreType abstract Ref<T> {
 @:coreType abstract Ref<T> {
 
 
 	@:from extern public static inline function make<T>( v : 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.
 	Native PHP array.
 **/
 **/
-@:coreType @:runtimeValue abstract NativeArray {
+@:coreType @:runtimeValue @:semantics(value) abstract NativeArray {
 	public inline function new()
 	public inline function new()
 		this = Syntax.arrayDecl();
 		this = Syntax.arrayDecl();
 
 

+ 1 - 0
std/php/NativeAssocArray.hx

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

+ 1 - 0
std/php/NativeIndexedArray.hx

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

+ 1 - 0
std/php/Ref.hx

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