Skip to content

Commit

Permalink
[inferpython][specialization types] adding a new type kind for import…
Browse files Browse the repository at this point in the history
…_from

Summary: we want to avoid a need for specialization when a function use an other function `fun` obtained by a `from .. import .. as fun`. This is not trivial because the closure type of `fun` is not computable without searching inside the Tenv, while we generate specialization types at capture time. We introduce a *delayed* name that will be resolve later at analysis time (see next diff).

Reviewed By: skcho

Differential Revision:
D67139193

Privacy Context Container: L1208441

fbshipit-source-id: fe46377766fe2a4fdb033460cd747b42606e65e1
  • Loading branch information
davidpichardie authored and facebook-github-bot committed Dec 13, 2024
1 parent 13b983b commit 1bcba18
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 49 deletions.
49 changes: 49 additions & 0 deletions infer/src/python/PyIR2Textual.ml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ module Typ = struct
Textual.(Typ.Ptr (Typ.Struct (class_companion_name module_name name)))


let module_attribute module_name attr_name =
let str = F.asprintf "PyModuleAttr::%s::%s" module_name attr_name in
Textual.(Typ.Ptr (Typ.Struct (TypeName.of_string str)))


let value = Textual.(Typ.Ptr (Typ.Struct (TypeName.of_string "PyObject")))
end

Expand Down Expand Up @@ -551,6 +556,7 @@ module DefaultType : sig
type decl =
| Class of {name: string; target: string}
| Import of {name: string; target: string}
| ImportFrom of {module_name: string; attr_name: string; target: string}
| Fundef of {typ: Textual.Typ.t; target: string}

type acc
Expand All @@ -567,6 +573,8 @@ module DefaultType : sig

val add_import : Textual.Ident.t -> string -> acc -> acc

val add_import_from : Textual.Ident.t -> module_name:string -> attr_name:string -> acc -> acc

val add_string_constant : Textual.Ident.t -> string -> acc -> acc

val get_allocate : Textual.Ident.t -> acc -> Textual.Typ.t option
Expand All @@ -577,6 +585,8 @@ module DefaultType : sig

val get_import : Textual.Ident.t -> acc -> string option

val get_import_from : Textual.Ident.t -> acc -> (string * string) option

val get_string_constant : Textual.Ident.t -> acc -> string option

val is_allocate : Textual.Ident.t -> acc -> bool
Expand All @@ -587,20 +597,24 @@ module DefaultType : sig

val is_import : Textual.Ident.t -> acc -> bool

val is_import_from : Textual.Ident.t -> acc -> bool

val is_string_constant : Textual.Ident.t -> acc -> bool

val export : acc -> decl list
end = struct
type decl =
| Class of {name: string; target: string}
| Import of {name: string; target: string}
| ImportFrom of {module_name: string; attr_name: string; target: string}
| Fundef of {typ: Textual.Typ.t; target: string}

type exp =
| Allocate of Textual.Typ.t
| ClassBody of string
| FuncPtr of Textual.Typ.t
| Import of string
| ImportFrom of {module_name: string; attr_name: string}
| StringConstant of string

type acc = {default_type: decl list; exps: exp Textual.Ident.Map.t}
Expand All @@ -621,6 +635,10 @@ end = struct
{acc with exps= Textual.Ident.Map.add ident (Import str) exps}


let add_import_from ident ~module_name ~attr_name ({exps} as acc) =
{acc with exps= Textual.Ident.Map.add ident (ImportFrom {module_name; attr_name}) exps}


let add_class_body ident str ({exps} as acc) =
{acc with exps= Textual.Ident.Map.add ident (ClassBody str) exps}

Expand All @@ -645,6 +663,10 @@ end = struct
match Textual.Ident.Map.find_opt ident exps with Some (Import _) -> true | _ -> false


let is_import_from ident {exps} =
match Textual.Ident.Map.find_opt ident exps with Some (ImportFrom _) -> true | _ -> false


let is_string_constant ident {exps} =
match Textual.Ident.Map.find_opt ident exps with Some (StringConstant _) -> true | _ -> false

Expand All @@ -665,6 +687,14 @@ end = struct
match Textual.Ident.Map.find_opt ident exps with Some (Import str) -> Some str | _ -> None


let get_import_from ident {exps} =
match Textual.Ident.Map.find_opt ident exps with
| Some (ImportFrom {module_name; attr_name}) ->
Some (module_name, attr_name)
| _ ->
None


let get_string_constant ident {exps} =
match Textual.Ident.Map.find_opt ident exps with
| Some (StringConstant str) ->
Expand All @@ -678,6 +708,8 @@ end

let py_import_name = builtin_qual_proc_name str_py_import_name

let py_import_from = builtin_qual_proc_name str_py_import_from

let py_build_class = builtin_qual_proc_name str_py_build_class

let py_store_name = builtin_qual_proc_name str_py_store_name
Expand All @@ -692,12 +724,19 @@ let sil_allocate : Textual.QualifiedProcName.t =

let gen_type module_name ~allow_classes name node =
let mk_class_companion str = Typ.class_companion module_name str in
let mk_module_attribute_name module_name attr_name = Typ.module_attribute module_name attr_name in
let open Textual in
let rec find_next_declaration acc = function
| Instr.Let {id= Some ident; exp= Call {proc; args= Const (Str name) :: _}} :: instrs
when QualifiedProcName.equal py_import_name proc ->
let acc = DefaultType.add_import ident name acc in
find_next_declaration acc instrs
| Instr.Let {id= Some ident; exp= Call {proc; args= [Const (Str attr_name); Var id_module]}}
:: instrs
when QualifiedProcName.equal py_import_from proc ->
let module_name = DefaultType.get_import id_module acc |> Option.value_exn in
let acc = DefaultType.add_import_from ident ~module_name ~attr_name acc in
find_next_declaration acc instrs
| Instr.Let {id= Some ident; exp= Call {proc; args= [Typ typ]}} :: instrs
when QualifiedProcName.equal sil_allocate proc ->
let acc = DefaultType.add_allocate ident typ acc in
Expand Down Expand Up @@ -728,6 +767,11 @@ let gen_type module_name ~allow_classes name node =
let name = DefaultType.get_import ident acc |> Option.value_exn in
let acc = DefaultType.add_decl (Import {name; target}) acc in
find_next_declaration acc instrs
| Instr.Let {exp= Call {proc; args= [Const (Str target); _; _; Var ident]}} :: instrs
when QualifiedProcName.equal py_store_name proc && DefaultType.is_import_from ident acc ->
let module_name, attr_name = DefaultType.get_import_from ident acc |> Option.value_exn in
let acc = DefaultType.add_decl (ImportFrom {module_name; attr_name; target}) acc in
find_next_declaration acc instrs
| Instr.Let {exp= Call {proc; args= [Const (Str target); _; _; Var ident]}} :: instrs
when QualifiedProcName.equal py_store_name proc && DefaultType.is_class_body ident acc ->
let name = DefaultType.get_class_body ident acc |> Option.value_exn in
Expand Down Expand Up @@ -757,6 +801,11 @@ let gen_type module_name ~allow_classes name node =
{ FieldDecl.qualified_name= mk_fieldname target
; typ= global_type_of_str name
; attributes= [] }
| ImportFrom {module_name; attr_name; target} ->
Some
{ FieldDecl.qualified_name= mk_fieldname target
; typ= mk_module_attribute_name module_name attr_name
; attributes= [] }
| Fundef {typ; target} ->
Some {FieldDecl.qualified_name= mk_fieldname target; typ= Typ.Ptr typ; attributes= []} )
in
Expand Down
112 changes: 63 additions & 49 deletions infer/src/python/unit/PyIR2TextualTest.ml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ let%expect_test _ =
{|
import random
import asyncio as a
from dir1.dir2.mod import x as y

x = 0

Expand Down Expand Up @@ -79,17 +80,20 @@ class C:
_ = $builtins.py_store_name("random", n1, n2, n3)
n4 = $builtins.py_import_name("asyncio", n0, $builtins.py_make_int(0))
_ = $builtins.py_store_name("a", n1, n2, n4)
n5 = $builtins.py_import_name("dir1::dir2::mod", $builtins.py_build_tuple($builtins.py_make_string("x")), $builtins.py_make_int(0))
n6 = $builtins.py_import_from("x", n5)
_ = $builtins.py_store_name("y", n1, n2, n6)
_ = $builtins.py_store_name("x", n1, n2, $builtins.py_make_int(0))
n5 = $builtins.py_make_function(fun (locals) -> dummy.f(n2, locals), n0, n0, n0, n0)
_ = $builtins.py_store_name("f", n1, n2, n5)
n6 = $builtins.py_make_function(fun (locals) -> dummy.g(n2, locals), n0, n0, n0, n0)
_ = $builtins.py_store_name("g", n1, n2, n6)
n7 = $builtins.py_make_function(fun (locals) -> dummy.D(n2, locals), n0, n0, n0, n0)
n8 = $builtins.py_build_class(n7, $builtins.py_make_string("D"))
_ = $builtins.py_store_name("D", n1, n2, n8)
n9 = $builtins.py_make_function(fun (locals) -> dummy.C(n2, locals), n0, n0, n0, n0)
n10 = $builtins.py_build_class(n9, $builtins.py_make_string("C"))
_ = $builtins.py_store_name("C", n1, n2, n10)
n7 = $builtins.py_make_function(fun (locals) -> dummy.f(n2, locals), n0, n0, n0, n0)
_ = $builtins.py_store_name("f", n1, n2, n7)
n8 = $builtins.py_make_function(fun (locals) -> dummy.g(n2, locals), n0, n0, n0, n0)
_ = $builtins.py_store_name("g", n1, n2, n8)
n9 = $builtins.py_make_function(fun (locals) -> dummy.D(n2, locals), n0, n0, n0, n0)
n10 = $builtins.py_build_class(n9, $builtins.py_make_string("D"))
_ = $builtins.py_store_name("D", n1, n2, n10)
n11 = $builtins.py_make_function(fun (locals) -> dummy.C(n2, locals), n0, n0, n0, n0)
n12 = $builtins.py_build_class(n11, $builtins.py_make_string("C"))
_ = $builtins.py_store_name("C", n1, n2, n12)
ret n0

}
Expand Down Expand Up @@ -208,17 +212,20 @@ class C:
_ = $builtins.py_store_name("random", n1, n2, n3)
n4 = $builtins.py_import_name("asyncio", n0, $builtins.py_make_int(0))
_ = $builtins.py_store_name("a", n1, n2, n4)
n5 = $builtins.py_import_name("dir1::dir2::mod", $builtins.py_build_tuple($builtins.py_make_string("x")), $builtins.py_make_int(0))
n6 = $builtins.py_import_from("x", n5)
_ = $builtins.py_store_name("y", n1, n2, n6)
_ = $builtins.py_store_name("x", n1, n2, $builtins.py_make_int(0))
n5 = $builtins.py_make_function(fun (locals) -> dummy.f(n2, locals), n0, n0, n0, n0)
_ = $builtins.py_store_name("f", n1, n2, n5)
n6 = $builtins.py_make_function(fun (locals) -> dummy.g(n2, locals), n0, n0, n0, n0)
_ = $builtins.py_store_name("g", n1, n2, n6)
n7 = $builtins.py_make_function(fun (locals) -> dummy.D(n2, locals), n0, n0, n0, n0)
n8 = $builtins.py_build_class(n7, $builtins.py_make_string("D"))
_ = $builtins.py_store_name("D", n1, n2, n8)
n9 = $builtins.py_make_function(fun (locals) -> dummy.C(n2, locals), n0, n0, n0, n0)
n10 = $builtins.py_build_class(n9, $builtins.py_make_string("C"))
_ = $builtins.py_store_name("C", n1, n2, n10)
n7 = $builtins.py_make_function(fun (locals) -> dummy.f(n2, locals), n0, n0, n0, n0)
_ = $builtins.py_store_name("f", n1, n2, n7)
n8 = $builtins.py_make_function(fun (locals) -> dummy.g(n2, locals), n0, n0, n0, n0)
_ = $builtins.py_store_name("g", n1, n2, n8)
n9 = $builtins.py_make_function(fun (locals) -> dummy.D(n2, locals), n0, n0, n0, n0)
n10 = $builtins.py_build_class(n9, $builtins.py_make_string("D"))
_ = $builtins.py_store_name("D", n1, n2, n10)
n11 = $builtins.py_make_function(fun (locals) -> dummy.C(n2, locals), n0, n0, n0, n0)
n12 = $builtins.py_build_class(n11, $builtins.py_make_string("C"))
_ = $builtins.py_store_name("C", n1, n2, n12)
ret n0

}
Expand Down Expand Up @@ -326,7 +333,8 @@ class C:
FINAL TRANSFORMATIONS
.source_language = "python"

type PyGlobals::dummy = {random: *PyGlobals::random; a: *PyGlobals::asyncio; f: *closure:dummy:0;
type PyGlobals::dummy = {random: *PyGlobals::random; a: *PyGlobals::asyncio;
y: *PyModuleAttr::dir1::dir2::mod::x; f: *closure:dummy:0;
g: *closure:dummy:1; D: *PyClassCompanion::dummy::D;
C: *PyClassCompanion::dummy::C}

Expand Down Expand Up @@ -409,38 +417,44 @@ class C:
define dummy.__module_body__(globals: *PyGlobals::dummy) : *PyObject {
local locals: *PyLocals
#b0:
n11:*PyGlobals::dummy = load &globals
store &locals <- n11:*PyGlobals::dummy
n12:*PyLocals = load &locals
n13:*PyGlobals::dummy = load &globals
store &locals <- n13:*PyGlobals::dummy
n14:*PyLocals = load &locals
n0 = $builtins.py_make_none()
n13 = $builtins.py_make_int(0)
n3 = $builtins.py_import_name("random", n0, n13)
n14 = $builtins.py_store_name("random", n12, n11, n3)
n15 = $builtins.py_make_int(0)
n4 = $builtins.py_import_name("asyncio", n0, n15)
n16 = $builtins.py_store_name("a", n12, n11, n4)
n3 = $builtins.py_import_name("random", n0, n15)
n16 = $builtins.py_store_name("random", n14, n13, n3)
n17 = $builtins.py_make_int(0)
n18 = $builtins.py_store_name("x", n12, n11, n17)
n19 = __sil_allocate(<closure:dummy:0>)
store n19.?.globals <- n11:*PyGlobals::dummy
n5 = $builtins.py_make_function(n19, n0, n0, n0, n0)
n21 = $builtins.py_store_name("f", n12, n11, n5)
n22 = __sil_allocate(<closure:dummy:1>)
store n22.?.globals <- n11:*PyGlobals::dummy
n6 = $builtins.py_make_function(n22, n0, n0, n0, n0)
n24 = $builtins.py_store_name("g", n12, n11, n6)
n25 = __sil_allocate(<closure:dummy:2>)
store n25.?.globals <- n11:*PyGlobals::dummy
n4 = $builtins.py_import_name("asyncio", n0, n17)
n18 = $builtins.py_store_name("a", n14, n13, n4)
n19 = $builtins.py_make_string("x")
n20 = $builtins.py_build_tuple(n19)
n21 = $builtins.py_make_int(0)
n5 = $builtins.py_import_name("dir1::dir2::mod", n20, n21)
n6 = $builtins.py_import_from("x", n5)
n22 = $builtins.py_store_name("y", n14, n13, n6)
n23 = $builtins.py_make_int(0)
n24 = $builtins.py_store_name("x", n14, n13, n23)
n25 = __sil_allocate(<closure:dummy:0>)
store n25.?.globals <- n13:*PyGlobals::dummy
n7 = $builtins.py_make_function(n25, n0, n0, n0, n0)
n27 = $builtins.py_make_string("D")
n8 = $builtins.py_build_class(n7, n27)
n28 = $builtins.py_store_name("D", n12, n11, n8)
n29 = __sil_allocate(<closure:dummy:3>)
store n29.?.globals <- n11:*PyGlobals::dummy
n9 = $builtins.py_make_function(n29, n0, n0, n0, n0)
n31 = $builtins.py_make_string("C")
n10 = $builtins.py_build_class(n9, n31)
n32 = $builtins.py_store_name("C", n12, n11, n10)
n27 = $builtins.py_store_name("f", n14, n13, n7)
n28 = __sil_allocate(<closure:dummy:1>)
store n28.?.globals <- n13:*PyGlobals::dummy
n8 = $builtins.py_make_function(n28, n0, n0, n0, n0)
n30 = $builtins.py_store_name("g", n14, n13, n8)
n31 = __sil_allocate(<closure:dummy:2>)
store n31.?.globals <- n13:*PyGlobals::dummy
n9 = $builtins.py_make_function(n31, n0, n0, n0, n0)
n33 = $builtins.py_make_string("D")
n10 = $builtins.py_build_class(n9, n33)
n34 = $builtins.py_store_name("D", n14, n13, n10)
n35 = __sil_allocate(<closure:dummy:3>)
store n35.?.globals <- n13:*PyGlobals::dummy
n11 = $builtins.py_make_function(n35, n0, n0, n0, n0)
n37 = $builtins.py_make_string("C")
n12 = $builtins.py_build_class(n11, n37)
n38 = $builtins.py_store_name("C", n14, n13, n12)
ret n0

}
Expand Down

0 comments on commit 1bcba18

Please sign in to comment.