Pārlūkot izejas kodu

[typer] document and test

Simon Krajewski 5 gadi atpakaļ
vecāks
revīzija
446ac2774b
3 mainītis faili ar 168 papildinājumiem un 19 dzēšanām
  1. 17 5
      src/typing/calls.ml
  2. 43 14
      src/typing/typerBase.ml
  3. 108 0
      tests/unit/src/unit/issues/Issue9744.hx

+ 17 - 5
src/typing/calls.ml

@@ -530,7 +530,7 @@ class call_dispatcher
 
 object(self)
 
-	method private make_field_call fa el_typed el =
+	method private make_field_call (fa : field_access) (el_typed : texpr list) (el : expr list) =
 		let fcc = unify_field_call ctx fa el_typed el p fa.fa_inline in
 		if has_class_field_flag fcc.fc_field CfAbstract then begin match fa.fa_on.eexpr with
 			| TConst TSuper -> display_error ctx (Printf.sprintf "abstract method %s cannot be accessed directly" fcc.fc_field.cf_name) p;
@@ -538,7 +538,7 @@ object(self)
 		end;
 		fcc.fc_data()
 
-	method private macro_call ethis cf el =
+	method private macro_call (ethis : texpr) (cf : tclass_field) (el : expr list) =
 		if ctx.macro_depth > 300 then error "Stack overflow" p;
 		ctx.macro_depth <- ctx.macro_depth + 1;
 		ctx.with_type_stack <- with_type :: ctx.with_type_stack;
@@ -596,7 +596,9 @@ object(self)
 		!ethis_f();
 		e
 
-	method expr_call e el =
+	(* Calls `e` with arguments `el`. Does not inspect the callee expression, so it should only be
+	   used with actual expression calls and not with something like field calls. *)
+	method expr_call (e : texpr) (el : expr list) =
 		check_assign();
 		let rec loop t = match follow t with
 		| TFun (args,r) ->
@@ -623,12 +625,22 @@ object(self)
 		in
 		loop e.etype
 
-	method resolve_call sea name =
+	(* Calls the resolve method represented by `sea` with an additional string-expression argument `name`. *)
+	method resolve_call (sea : static_extension_access) (name : string) =
 		let eparam = sea.se_this in
 		let e_name = Texpr.Builder.make_string ctx.t name null_pos in
 		self#field_call sea.se_access [eparam;e_name] []
 
-	method field_call fa el_typed el =
+	(* Calls the field represented by `fa` with the typed arguments `el_typed` and the syntactic arguments `el`.
+
+	   This function inspects the nature of the field being called and dispatches the call accordingly:
+
+	   * If the field is `@:generic`, call `type_generic_function`.
+	   * If the field is a non-macro method, call it via `make_field_call`.
+	   * If the field is a property, resolve the accessor (depending on `mode`) and recurse onto it.
+	   * Otherwise, call the field as a normal expression via `expr_call`.
+	*)
+	method field_call (fa : field_access) (el_typed : texpr list) (el : expr list) =
 		match fa.fa_field.cf_kind with
 		| Method (MethNormal | MethInline | MethDynamic) ->
 			check_assign();

+ 43 - 14
src/typing/typerBase.ml

@@ -11,26 +11,43 @@ type field_host =
 	| FHAnon
 
 type field_access = {
-	fa_on : texpr;
-	fa_field : tclass_field;
-	fa_host : field_host;
+	(* The expression on which the field is accessed. For abstracts, this is a type expression
+	   to the implementation class. *)
+	fa_on     : texpr;
+	(* The field being accessed. *)
+	fa_field  : tclass_field;
+	(* The host of the field. *)
+	fa_host   : field_host;
+	(* Whether or not to inline the access. This can be set for non-inline fields via `inline call()` syntax. *)
 	fa_inline : bool;
-	fa_pos : pos;
+	(* The position of the field access expression in syntax. *)
+	fa_pos    : pos;
 }
 
 type static_extension_access = {
-	se_this : texpr;
+	(* The `this` expression which should be passed as first argument. *)
+	se_this   : texpr;
+	(* The field access information. *)
 	se_access : field_access;
 }
 
 type access_kind =
+	(* Access is not possible or allowed. *)
 	| AKNo of string
+	(* Access on arbitrary expression. *)
 	| AKExpr of texpr
+	(* Access on non-property field. *)
 	| AKField of field_access
-	| AKAccessor of field_access (* fa_field is the property, not the accessor *)
+	(* Access on property field. The field is the property, not the accessor. *)
+	| AKAccessor of field_access
+	(* Access via static extension. *)
 	| AKUsingField of static_extension_access
+	(* Access via static extension on property field. The field is the property, not the accessor.
+	   This currently only happens on abstract properties. *)
 	| AKUsingAccessor of static_extension_access
+	(* Access on abstract via array overload. *)
 	| AKAccess of tabstract * tparams * tclass * texpr * texpr
+	(* Access on abstract via resolve method. *)
 	| AKResolve of static_extension_access * string
 
 type object_decl_kind =
@@ -259,36 +276,47 @@ let get_abstract_froms a pl =
 
 module FieldAccess = struct
 	type field_host =
-		| FGet (* get the plain expression with applied field type parameters *)
-		| FCall (* does not apply field type parameters *)
-		| FRead (* actual reading, for FClosure and such *)
-		| FWrite (* used as lhs, no semantic difference to FGet *)
+		(* Get the plain expression with applied field type parameters. *)
+		| FGet
+		(* Does not apply field type parameters. *)
+		| FCall
+		(* Actual reading, for FClosure and such. *)
+		| FRead
+		(* Used as lhs, no semantic difference to FGet. *)
+		| FWrite
 
 	type accessor_resolution =
+		(* Accessor was found. *)
 		| AccessorFound of field_access
+		(* Accessor was not found, but access was made on anonymous structure. *)
 		| AccessorAnon
+		(* Accessor was not found. *)
 		| AccessorNotFound
+		(* Accessor resolution was attempted on a non-property. *)
 		| AccessorInvalid
 
 	let create e cf fh inline p = {
-		fa_on = e;
-		fa_field = cf;
-		fa_host = fh;
+		fa_on     = e;
+		fa_field  = cf;
+		fa_host   = fh;
 		fa_inline = inline;
-		fa_pos = p;
+		fa_pos    = p;
 	}
 
+	(* Creates the `tfield_access` corresponding to this field access, using the provided field. *)
 	let apply_fa cf = function
 		| FHStatic c -> FStatic(c,cf)
 		| FHInstance(c,tl) -> FInstance(c,tl,cf)
 		| FHAbstract(a,tl,c) -> FStatic(c,cf)
 		| FHAnon -> FAnon cf
 
+	(* Returns the mapping function to apply type parameters. *)
 	let get_map_function fa = match fa.fa_host with
 		| FHStatic _ | FHAnon -> (fun t -> t)
 		| FHInstance(c,tl) -> TClass.get_map_function c tl
 		| FHAbstract(a,tl,_) -> apply_params a.a_params tl
 
+	(* Converts the field access to a `TField` node, using the provided `mode`. *)
 	let get_field_expr fa mode =
 		let cf = fa.fa_field in
 		let t = match mode with
@@ -320,6 +348,7 @@ module FieldAccess = struct
 		in
 		mk (TField(fa.fa_on,fa')) t fa.fa_pos
 
+	(* Resolves the accessor on the field access, using the provided `mode`. *)
 	let resolve_accessor fa mode = match fa.fa_field.cf_kind with
 		| Var v ->
 			begin match (match mode with MSet _ -> v.v_write | _ -> v.v_read) with

+ 108 - 0
tests/unit/src/unit/issues/Issue9744.hx

@@ -0,0 +1,108 @@
+package unit.issues;
+
+private abstract Ref<T>(haxe.ds.Vector<T>) {
+	public var value(get, set):T;
+
+	public inline function new() this = new haxe.ds.Vector(1);
+
+	@:to inline function get_value():T return this[0];
+	inline function set_value(param:T) return this[0] = param;
+
+	public function toString():String return '@[' + Std.string(value)+']';
+
+	@:noUsing @:from static inline public function to<A>(v:A):Ref<A> {
+	  var ret = new Ref();
+	  ret.value = v;
+	  return ret;
+	}
+}
+
+private interface IVcs {
+	var available(get, set):Bool;
+}
+
+private class Vcs implements IVcs {
+	@:isVar public var available(get, set):Bool;
+
+	function get_available() {
+		return available;
+	}
+
+	function set_available(v:Bool) {
+		return available = v;
+	}
+
+	public function new(available:Bool) {
+		this.available = available;
+	}
+}
+
+#if eval
+/* We don't care about the actual extern behavior so there's no point in trying to get this test to
+   work on all targets. The only important part here is that the typing doesn't choke on the missing
+   accessor, which will behave the same way on all targets. */
+
+@:native("Issue9744NativeVirtualArray")
+private class NativeVirtualArrayNonExtern {
+	function new() {}
+	var length(get, null):Int;
+
+	function get_length() {
+		return 1;
+	}
+}
+
+@:native("Issue9744NativeVirtualArray")
+private extern class NativeVirtualArray {
+	function new():Void;
+	var length(get, null):Int;
+}
+
+#end
+
+class NadakoBase {
+	function f() {}
+}
+class NadakoA extends NadakoBase {}
+
+@:access(unit.issues.NadakoA)
+class NadakoB {
+	var a:NadakoA;
+	function f() {
+		a.f();
+	}
+}
+
+class Issue9744 extends unit.Test {
+	function testAbstractOverAbstractSelf() {
+		var ref = new Ref();
+		eq(1, ref.value = 1);
+		eq(1, ref.value);
+	}
+
+	function testUnopProperties() {
+		var vcs = new Vcs(true);
+		t(vcs.available);
+		f(!vcs.available);
+		t(!(vcs.available = false));
+
+		var ivcs:IVcs = new Vcs(true);
+		t(ivcs.available);
+		f(!ivcs.available);
+		t(!(ivcs.available = false));
+	}
+
+	#if eval
+	function testExternProperty() {
+		var nva = new NativeVirtualArray();
+		eq(1, nva.length);
+	}
+	#end
+
+	function testUnopResolve() {
+		var doc = Xml.parse('<node attr="1"/>');
+		var access = new haxe.xml.Access(doc.firstElement());
+		f(!access.has.attr);
+		t(!access.has.attra);
+	}
+}