Skip to content

Commit

Permalink
[typer] fix forwarded static extension priority
Browse files Browse the repository at this point in the history
  • Loading branch information
vonagam committed Jul 20, 2020
1 parent f32be2b commit cca7cae
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 43 deletions.
104 changes: 67 additions & 37 deletions src/typing/fields.ml
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,29 @@ module TypeFieldConfig = struct
type t = {
allow_resolve : bool;
do_resume : bool;
forwarder : forwarder option;
}

and forwarder = {
using_field: (unit -> access_kind) ref;
get_resolve: (unit -> access_kind) ref;
}

let allow_resolve cfg = cfg.allow_resolve

let do_resume cfg = cfg.do_resume

let forwarder cfg = cfg.forwarder

let default = {
allow_resolve = true;
do_resume = false;
forwarder = None;
}

let create resume = {
allow_resolve = true;
do_resume = resume;
}
let create resume = {default with do_resume = resume}

let resolveless resume = {default with do_resume = resume; allow_resolve = false}

let with_resume cfg = {cfg with do_resume = true}
end
Expand Down Expand Up @@ -57,6 +65,13 @@ let remove_constant_flag t callb =
restore();
raise e

let find_once f =
let rec find = ref (fun () ->
find := (fun () -> raise Not_found);
f()
) in
find

let enum_field_type ctx en ef p =
let tl_en = Monomorph.spawn_constrained_monos (fun t -> t) en.e_params in
let map = apply_params en.e_params tl_en in
Expand Down Expand Up @@ -247,7 +262,7 @@ let field_access ctx mode f fmode t e p =
let class_field ctx c tl name p =
raw_class_field (fun f -> field_type ctx c tl f p) c tl name

let rec using_field ctx mode e i p =
let rec using_field cfg ctx mode e i p =
let is_set = match mode with MSet _ -> true | _ -> false in
if is_set then raise Not_found;
(* do not try to find using fields if the type is a monomorph, which could lead to side-effects *)
Expand Down Expand Up @@ -283,6 +298,11 @@ let rec using_field ctx mode e i p =
loop l
in
try
(* forwarder's using *)
match TypeFieldConfig.forwarder cfg with
| Some(forwarder) -> !(forwarder.using_field)()
| _ -> raise Not_found
with Not_found -> try
(* module using from `using Path` *)
loop ctx.m.module_using
with Not_found -> try
Expand All @@ -298,7 +318,7 @@ let rec using_field ctx mode e i p =
acc
with Not_found ->
if not !check_constant_struct then raise Not_found;
remove_constant_flag e.etype (fun ok -> if ok then using_field ctx mode e i p else raise Not_found)
remove_constant_flag e.etype (fun ok -> if ok then using_field cfg ctx mode e i p else raise Not_found)

let check_field_access ctx c f stat p =
if not ctx.untyped && not (can_access ctx c f stat) then
Expand Down Expand Up @@ -402,7 +422,7 @@ let rec type_field cfg ctx e i p mode (with_type : WithType.t) =
with Not_found -> try
begin match e.eexpr with
| TConst TSuper -> raise Not_found
| _ -> using_field ctx mode e i p
| _ -> using_field cfg ctx mode e i p
end
with Not_found -> try
loop_dyn c params
Expand Down Expand Up @@ -431,7 +451,7 @@ let rec type_field cfg ctx e i p mode (with_type : WithType.t) =
no_field())
| TDynamic t ->
(try
using_field ctx mode e i p
using_field cfg ctx mode e i p
with Not_found ->
AKExpr (mk (TField (e,FDynamic i)) t p))
| TAnon a ->
Expand Down Expand Up @@ -476,7 +496,7 @@ let rec type_field cfg ctx e i p mode (with_type : WithType.t) =
raise Not_found
with Not_found ->
try
using_field ctx mode e i p
using_field cfg ctx mode e i p
with Not_found ->
no_field()
)
Expand All @@ -501,7 +521,7 @@ let rec type_field cfg ctx e i p mode (with_type : WithType.t) =
with Not_found ->
if not is_open then
try
using_field ctx mode e i p
using_field cfg ctx mode e i p
with Not_found ->
no_field()
else begin
Expand Down Expand Up @@ -532,6 +552,32 @@ let rec type_field cfg ctx e i p mode (with_type : WithType.t) =
end
| TAbstract (a,pl) ->
let static_abstract_access_through_instance = ref false in
let using_field_ref = find_once (fun () ->
using_field cfg ctx mode e i p
) in
let get_resolve_ref = find_once (fun () ->
try
(match TypeFieldConfig.forwarder cfg with
| Some(forwarder) -> !(forwarder.get_resolve)()
| _ -> raise Not_found)
with Not_found ->
if not (TypeFieldConfig.allow_resolve cfg) then raise Not_found;
let c,cf = match a.a_impl,(if is_set 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
| _ -> die "" __LOC__
in
if is_set then
AKFieldSet(e,ef,i,r)
else
AKExpr ((!build_call_ref) ctx (AKUsing(ef,c,cf,e,false)) [EConst (String(i,SDoubleQuotes)),p] NoValue p)
) in
(try
let c = (match a.a_impl with None -> raise Not_found | Some c -> c) in
let f = PMap.find i c.cl_statics in
Expand Down Expand Up @@ -590,41 +636,25 @@ let rec type_field cfg ctx e i p mode (with_type : WithType.t) =
with Not_found -> try
if does_forward a false then
let underlying_type = Abstract.get_underlying_type ~return_first:true a pl in
type_field (TypeFieldConfig.with_resume cfg) ctx {e with etype = underlying_type} i p mode with_type
let cfg = {cfg with
do_resume = true;
forwarder = Some({
using_field = using_field_ref;
get_resolve = get_resolve_ref;
});
} in
type_field cfg ctx {e with etype = underlying_type} i p mode with_type
else
raise Not_found
with Not_found -> try
using_field ctx mode e i p
(* TODO: not sure what this is/was doing (see #9680) *)
(* with Not_found -> try
(match ctx.curfun, e.eexpr with
| FunMemberAbstract, TConst (TThis) -> type_field cfg ctx {e with etype = apply_params a.a_params pl a.a_this} i p mode;
| _ -> raise Not_found) *)
!using_field_ref()
with Not_found -> try
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
| _ -> die "" __LOC__
in
if is_write then
AKFieldSet(e,ef,i,r)
else
AKExpr ((!build_call_ref) ctx (AKUsing(ef,c,cf,e,false)) [EConst (String(i,SDoubleQuotes)),p] NoValue p)
in
if not (TypeFieldConfig.allow_resolve cfg) then raise Not_found;
get_resolve (is_set)
!get_resolve_ref()
with Not_found ->
if !static_abstract_access_through_instance then error ("Invalid call to static function " ^ i ^ " through abstract instance") pfield
else no_field())
| _ ->
try using_field ctx mode e i p with Not_found -> no_field()
try using_field cfg ctx mode e i p with Not_found -> no_field()

let type_field_default_cfg = type_field TypeFieldConfig.default

Expand Down
9 changes: 3 additions & 6 deletions src/typing/forLoop.ml
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,7 @@ module IterationKind = struct
it_expr : texpr;
}

let type_field_config = {
Fields.TypeFieldConfig.do_resume = true;
allow_resolve = false;
}
let type_field_config = TypeFieldConfig.resolveless true

let get_next_array_element arr iexpr pt p =
(mk (TArray (arr,iexpr)) pt p)
Expand Down Expand Up @@ -119,14 +116,14 @@ module IterationKind = struct
)
in
try
let acc = type_field ({do_resume = true;allow_resolve = false}) ctx e s e.epos (MCall []) (WithType.with_type t) in
let acc = type_field (TypeFieldConfig.resolveless true) ctx e s e.epos (MCall []) (WithType.with_type t) in
try_acc acc;
with Not_found ->
try_last_resort (fun () ->
match !dynamic_iterator with
| Some e -> e
| None ->
let acc = type_field ({do_resume = resume;allow_resolve = false}) ctx e s e.epos (MCall []) (WithType.with_type t) in
let acc = type_field (TypeFieldConfig.resolveless resume) ctx e s e.epos (MCall []) (WithType.with_type t) in
try_acc acc
)
in
Expand Down
36 changes: 36 additions & 0 deletions tests/unit/src/unit/issues/Issue9680.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package unit.issues;

class Issue9680 extends Test {
function test() {
var foo: Foo = 0;
eq('Foo using', foo.ext());
eq('Foo resolve', foo.baz);

var bar: Bar = foo;
eq('Bar using', bar.ext());
eq('Bar resolve', bar.baz);
}
}

@:using(Issue9680.Issue9680_FooTools)
private abstract Foo(Int) from Int {
@:op(a.b) function resolve(name:String)
return 'Foo resolve';
}

@:using(Issue9680.Issue9680_BarTools)
@:forward
private abstract Bar(Foo) from Foo {
@:op(a.b) function resolve(name:String)
return 'Bar resolve';
}

class Issue9680_FooTools {
public static function ext(that: Foo)
return 'Foo using';
}

class Issue9680_BarTools {
public static function ext(that: Bar)
return 'Bar using';
}

0 comments on commit cca7cae

Please sign in to comment.