Skip to content

Commit

Permalink
Expose queries to the variance map
Browse files Browse the repository at this point in the history
Summary:
The plumming efforts are not yet done. We need to expose queries, such as get_class_summary to the variable map.

After this, we are able to work on the attached unit test as a starting point.

Note:  I was not sure how to expose the variable_map in the typeOrderTest, so I copied the implementation. This is not ideal, but variable_map requires globalResolution. It's unclear how we can get that in this test. Happy to get any suggestions on this point!

Reviewed By: stroxler

Differential Revision: D63615975

fbshipit-source-id: 779f56b3572828c023cfa278c2000915c5b48aaa
  • Loading branch information
Zeina Migeed authored and facebook-github-bot committed Oct 1, 2024
1 parent b7c9434 commit 76c8490
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 50 deletions.
82 changes: 47 additions & 35 deletions source/analysis/attributeResolution.ml
Original file line number Diff line number Diff line change
Expand Up @@ -779,40 +779,6 @@ let callable_call_special_cases
| _ -> None


(* Creates a function from generic type parameters for a given class to post variance inference *)
let infer_variance_for_one_param ~generic_type_param =
let from_pre_to_post variance =
match variance with
| Type.Record.PreInferenceVariance.P_Covariant -> Type.Record.Variance.Covariant
| Type.Record.PreInferenceVariance.P_Contravariant -> Type.Record.Variance.Contravariant
| Type.Record.PreInferenceVariance.P_Invariant -> Type.Record.Variance.Invariant
| Type.Record.PreInferenceVariance.P_Undefined -> Type.Record.Variance.Invariant
in

let find_variance =
match generic_type_param with
| Type.GenericParameter.GpTypeVar { variance; _ } -> from_pre_to_post variance
| _ -> Invariant
in
find_variance


let infer_variance_for_all_params parameters =
let add_to_lookup so_far = function
| Type.GenericParameter.GpTypeVar { name; _ } as gp ->
let variance = infer_variance_for_one_param ~generic_type_param:gp in
Map.set so_far ~key:name ~data:variance
| _ -> so_far
in
List.fold parameters ~f:add_to_lookup ~init:Identifier.Map.empty


(* We will expose this interface as the variance map *)
let variance_map ~class_name ~parameters =
let _class_name = class_name in
infer_variance_for_all_params parameters


class base ~queries:(Queries.{ controls; _ } as queries) =
object (self)
(* Given a `Type.t`, recursively search for parameteric forms with invalid
Expand Down Expand Up @@ -2910,6 +2876,40 @@ class base ~queries:(Queries.{ controls; _ } as queries) =
fields >>| fun fields -> { Type.TypedDictionary.fields; name = class_name })
| _ -> None

(* We will expose this interface as the variance map *)
method variance_map ~class_name ~parameters =
(* Creates a function from generic type parameters for a given class to post variance
inference *)
let infer_variance_for_one_param ~generic_type_param =
let from_pre_to_post variance =
match variance with
| Type.Record.PreInferenceVariance.P_Covariant -> Type.Record.Variance.Covariant
| Type.Record.PreInferenceVariance.P_Contravariant -> Type.Record.Variance.Contravariant
| Type.Record.PreInferenceVariance.P_Invariant -> Type.Record.Variance.Invariant
| Type.Record.PreInferenceVariance.P_Undefined -> Type.Record.Variance.Invariant
in

let find_variance =
match generic_type_param with
| Type.GenericParameter.GpTypeVar { variance; _ } -> from_pre_to_post variance
| _ -> Invariant
in
find_variance
in

let infer_variance_for_all_params parameters =
let add_to_lookup so_far = function
| Type.GenericParameter.GpTypeVar { name; _ } as gp ->
let variance = infer_variance_for_one_param ~generic_type_param:gp in
Map.set so_far ~key:name ~data:variance
| _ -> so_far
in
List.fold parameters ~f:add_to_lookup ~init:Identifier.Map.empty
in

let _class_name = class_name in
infer_variance_for_all_params parameters

(* Construct a ConstraintsSet.order representing the lattice of types for a
* project. The order object is just a record of callbacks providing access to
* shared memory for all cases where type order and constraint solving operations
Expand Down Expand Up @@ -3026,7 +3026,7 @@ class base ~queries:(Queries.{ controls; _ } as queries) =
get_typed_dictionary = self#get_typed_dictionary ~cycle_detections;
get_named_tuple_fields;
metaclass;
variance_map;
variance_map = self#variance_map;
}

(* Given an expression, produce a Type.t where literal type information - that is,
Expand Down Expand Up @@ -4191,6 +4191,18 @@ module ReadOnly = struct
|> fun method_ -> method_ ~cycle_detections:empty_cycle_detections


let variance_map read_only =
let queries =
create_queries
~dependency:None
~class_metadata_environment:(class_metadata_environment read_only)
in

let implementation = new base ~queries in

implementation#variance_map


let instantiate_attribute =
add_all_caches_and_empty_cycle_detections (fun o ->
o#instantiate_attribute ?apply_descriptors:None)
Expand Down
11 changes: 6 additions & 5 deletions source/analysis/attributeResolution.mli
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ module AttributeReadOnly : sig
right:Type.t ->
bool

val variance_map
: t ->
class_name:string ->
parameters:Type.GenericParameter.t list ->
Type.Record.Variance.t Identifier.Map.t

val global : t -> ?dependency:DependencyKey.registered -> Reference.t -> Global.t option

module Testing : sig
Expand All @@ -170,11 +176,6 @@ module AttributeReadOnly : sig
end
end

val variance_map
: class_name:'a ->
parameters:Type.GenericParameter.t list ->
Type.Record.Variance.t Identifier.Map.t

include
Environment.S
with module ReadOnly = AttributeReadOnly
Expand Down
7 changes: 6 additions & 1 deletion source/analysis/globalResolution.ml
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@ let constraints_solution_exists ({ dependency; _ } as resolution) =
(attribute_resolution resolution)


let variance_map resolution =
AttributeResolution.ReadOnly.variance_map (attribute_resolution resolution)


let uninstantiated_attributes
({ dependency; _ } as resolution)
?(transitive = false)
Expand Down Expand Up @@ -362,7 +366,8 @@ let is_invariance_mismatch resolution ~left ~right =
| Type.GenericParameter.GpTypeVar { name; _ } ->
let variance =
Map.find
(AttributeResolution.variance_map
(variance_map
resolution
~parameters:generic_parameters
~class_name:left_name)
name
Expand Down
6 changes: 6 additions & 0 deletions source/analysis/globalResolution.mli
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ val constraints_solution_exists
right:Type.t ->
bool

val variance_map
: t ->
class_name:string ->
parameters:Type.GenericParameter.t list ->
Type.Record.Variance.t Identifier.Map.t

val uninstantiated_attributes
: t ->
?transitive:bool ->
Expand Down
6 changes: 3 additions & 3 deletions source/analysis/test/constraintsSetTest.ml
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ let make_assert_functions context =
get_typed_dictionary;
get_named_tuple_fields;
metaclass;
variance_map = AttributeResolution.variance_map;
variance_map = GlobalResolution.variance_map (GlobalResolution.create environment);
}
in
let attributes annotation ~cycle_detections =
Expand Down Expand Up @@ -1555,7 +1555,7 @@ let test_instantiate_protocol_parameters context =
get_typed_dictionary;
get_named_tuple_fields;
metaclass = (fun _ ~cycle_detections:_ -> Some (Type.Primitive "type"));
variance_map = AttributeResolution.variance_map;
variance_map = GlobalResolution.variance_map (GlobalResolution.create environment);
}
in
assert_equal
Expand Down Expand Up @@ -1730,7 +1730,7 @@ let test_mark_escaped_as_escaped context =
get_typed_dictionary;
get_named_tuple_fields;
metaclass = (fun _ ~cycle_detections:_ -> Some (Type.Primitive "type"));
variance_map = AttributeResolution.variance_map;
variance_map = GlobalResolution.variance_map (GlobalResolution.create environment);
}
in
TypeOrder.OrderedConstraintsSet.add_and_simplify
Expand Down
22 changes: 22 additions & 0 deletions source/analysis/test/integration/typeVariableTest.ml
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,28 @@ let test_type_variable_scoping =
x2: str = foo.f("foo")
|}
[];
(* The errors about __len__ happen in legacy syntax too. Those errors need further
investigation. *)
labeled_test_case __FUNCTION__ __LINE__
@@ assert_type_errors
{|
from typing import Sequence
class ShouldBeCovariant2[T](Sequence[T]):
pass
vco2_1: ShouldBeCovariant2[float] = ShouldBeCovariant2[int]() # OK
vco2_2: ShouldBeCovariant2[int] = ShouldBeCovariant2[float]() # E
|}
[
"Incompatible variable type [9]: vco2_1 is declared to have type \
`ShouldBeCovariant2[float]` but is used as type `ShouldBeCovariant2[int]`.";
"Invalid class instantiation [45]: Cannot instantiate abstract class \
`ShouldBeCovariant2` with abstract method `__len__`.";
"Incompatible variable type [9]: vco2_2 is declared to have type \
`ShouldBeCovariant2[int]` but is used as type `ShouldBeCovariant2[float]`.";
"Invalid class instantiation [45]: Cannot instantiate abstract class \
`ShouldBeCovariant2` with abstract method `__len__`.";
];
]
Expand Down
41 changes: 38 additions & 3 deletions source/analysis/test/typeOrderTest.ml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,41 @@ let attribute_from_attributes attributes =
attribute


(* TODO migeedz: duplicate implementation since I need global resolution to be able to invoke the
variable map. Is there a way to obtain it here? we need the source and the test context. *)
let variance_map ~class_name ~parameters =
(* Creates a function from generic type parameters for a given class to post variance inference *)
let infer_variance_for_one_param ~generic_type_param =
let from_pre_to_post variance =
match variance with
| Type.Record.PreInferenceVariance.P_Covariant -> Type.Record.Variance.Covariant
| Type.Record.PreInferenceVariance.P_Contravariant -> Type.Record.Variance.Contravariant
| Type.Record.PreInferenceVariance.P_Invariant -> Type.Record.Variance.Invariant
| Type.Record.PreInferenceVariance.P_Undefined -> Type.Record.Variance.Invariant
in

let find_variance =
match generic_type_param with
| Type.GenericParameter.GpTypeVar { variance; _ } -> from_pre_to_post variance
| _ -> Invariant
in
find_variance
in

let infer_variance_for_all_params parameters =
let add_to_lookup so_far = function
| Type.GenericParameter.GpTypeVar { name; _ } as gp ->
let variance = infer_variance_for_one_param ~generic_type_param:gp in
Map.set so_far ~key:name ~data:variance
| _ -> so_far
in
List.fold parameters ~f:add_to_lookup ~init:Identifier.Map.empty
in

let _class_name = class_name in
infer_variance_for_all_params parameters


let less_or_equal
?(attributes = fun _ ~cycle_detections:_ -> None)
?(is_protocol = fun _ -> false)
Expand All @@ -161,7 +196,7 @@ let less_or_equal
get_typed_dictionary;
get_named_tuple_fields;
metaclass = (fun _ ~cycle_detections:_ -> Some (Type.Primitive "type"));
variance_map = AttributeResolution.variance_map;
variance_map;
}
Expand All @@ -181,7 +216,7 @@ let join ?(attributes = fun _ ~cycle_detections:_ -> None) handler =
get_typed_dictionary;
get_named_tuple_fields;
metaclass = (fun _ ~cycle_detections:_ -> Some (Type.Primitive "type"));
variance_map = AttributeResolution.variance_map;
variance_map;
}
Expand All @@ -201,7 +236,7 @@ let meet handler =
get_typed_dictionary;
get_named_tuple_fields;
metaclass = (fun _ ~cycle_detections:_ -> Some (Type.Primitive "type"));
variance_map = AttributeResolution.variance_map;
variance_map;
}
Expand Down
11 changes: 8 additions & 3 deletions source/analysis/typeCheck.ml
Original file line number Diff line number Diff line change
Expand Up @@ -6240,7 +6240,8 @@ module State (Context : Context) = struct
|> Option.value ~default:[]
in
let look_up_this_class_variance =
AttributeResolution.variance_map
GlobalResolution.variance_map
global_resolution
~parameters:generic_parameters
~class_name:this_class_name
in
Expand All @@ -6258,7 +6259,10 @@ module State (Context : Context) = struct
Type.GenericParameter.GpTypeVar { name = base_name; _ } ) -> (
let base_variance =
Map.find
(AttributeResolution.variance_map ~parameters ~class_name:base_class_name)
(GlobalResolution.variance_map
global_resolution
~parameters
~class_name:base_class_name)
base_name
|> Option.value ~default:Type.Record.Variance.Invariant
in
Expand Down Expand Up @@ -6566,7 +6570,8 @@ module State (Context : Context) = struct
GlobalResolution.generic_parameters global_resolution current_class_name
|> Option.value ~default:[]
in
AttributeResolution.variance_map
GlobalResolution.variance_map
global_resolution
~parameters:generic_parameters
~class_name:current_class_name
| None -> Identifier.Map.empty
Expand Down

0 comments on commit 76c8490

Please sign in to comment.