Modified infer

This commit is contained in:
2026-10-23 12:07:23 +00:00
parent a80bf6f19a
commit cf455b88a8
8 changed files with 186 additions and 180 deletions
+5 -4
View File
@@ -2,8 +2,9 @@
//!
//! There are four kinds:
//!
//! - **A JS function**, as infer walked it. Infer walks a JS function once for each set of
//! arg types that it is called with, and each walk is one C function (see `js`).
//! - **A JS function**, as infer walked it. Infer walks a JS function once for each context,
//! which is the function in one call site, and each set of arg types. Each walk is one C
//! function (see `js`).
//! - **A lookup**, which gets a field that some objects of a class do not have. It gives the
//! object's own field if the object has it, else it looks on the object's prototype, and so
//! on along the prototype chain. At the end of the chain, the field is undefined.
@@ -19,7 +20,7 @@ use crate::{infer::Type, libqjs::parse::AtomRef, regify::Operand, transform::PRO
#[derive(Clone, Copy)]
pub enum Function {
Js(usize), // a JS function, as one walk of infer: the walk's index
Js(usize), // a JS function, as one walk: the walk's index
Lookup(usize, AtomRef), // a lookup of a field on the objects of a class: the class, and the field
Start,
Complete,
@@ -171,7 +172,7 @@ fn complete_call(program: &Program) -> Option<String> {
Some(host_call(program, walk, &args))
}
/// A call of a JS function's walk, with each arg converted to its param's C type
/// A call of a walk's C function, with each arg converted to its param's C type
fn host_call(program: &Program, walk: usize, args: &[(String, Member)]) -> String {
let args: Vec<String> = (args.iter().enumerate())
.map(|(i, (value, member))| {
+14 -12
View File
@@ -11,7 +11,7 @@ use super::{
values::{base, int, strict_eq, to_int32, to_number, truthy, undefined, unit_literal},
};
use crate::{
infer::{InstrRef, ObjAddr, Type},
infer::{CallSite, InstrRef, ObjAddr, Type},
libqjs::parse::{ImmOperand, JsValue, opcodes::OpCode},
native::NATIVES,
regify::{Operand, RegInstr},
@@ -82,19 +82,21 @@ impl JsFunction<'_> {
}
}
/// Instr `idx` of this JS function
fn here(&self, idx: usize) -> InstrRef {
InstrRef {
fn_idx: self.program.cache.walks[self.walk].req.fn_idx,
/// The call site of the call at instr `idx`
fn call_site(&self, idx: usize) -> CallSite {
Some(InstrRef {
fn_idx: self.context.fn_idx,
instr_idx: idx,
}
})
}
/// The class of the objects that instr `idx` makes, in this walk
/// The class of the objects that instr `idx` makes, in this walk's context
pub(super) fn class_here(&self, idx: usize) -> usize {
let in_call = self.program.cache.walks[self.walk].req.in_call;
let made_at = self.here(idx);
self.program.types.class(ObjAddr { made_at, in_call })
let obj = ObjAddr {
context: self.context,
instr: idx,
};
self.program.types.class(obj)
}
/// `close_loc x`: a local that closures share gets a new cell with the same value.
@@ -466,7 +468,7 @@ impl JsFunction<'_> {
let mut types: Vec<Type> = instr.args.iter().map(|&s| self.ty(s)).collect();
types[0] = Type::Ref(program.types.classes[class].objs[0]);
let walk = (program.cache).find(fn_idx, Some(self.here(idx)), &types, program.fns);
let walk = (program.cache).find(fn_idx, self.call_site(idx), &types, program.fns);
let mut args = vec![];
for i in 0..program.fns[fn_idx].arg_count {
@@ -481,7 +483,7 @@ impl JsFunction<'_> {
}
let call = format!("js{walk}({})", args.join(", "));
let ret = &program.cache.walks[walk].ret;
let ret = program.cache.ret(walk);
// A function that never returns gives no value
if *ret == Type::Unknown {
+13 -12
View File
@@ -1,8 +1,9 @@
//! A JS function as a C function
//!
//! Infer walks each JS function once for each set of arg types that it is called with. Each
//! walk is one C function, `js<walk>`, in which every value has the type that infer found in
//! that walk.
//! Infer walks a JS function once for each context, which is the function in one call site,
//! and each set of arg types. Each walk is one C function, `js<walk>`, in which every value
//! has the type that infer found in that walk. What the walk returns is in memory too (see
//! `TypeCache::ret`).
//!
//! Each local, arg and reg is a C local, of the C type of its type. A local or arg that a
//! closure shares is a pointer to a cell (see transform/closure.rs). The cell is made when the
@@ -20,16 +21,17 @@ use super::{
values::truthy,
};
use crate::{
infer::Type,
infer::{Context, Type},
libqjs::parse::opcodes::OpCode,
regify::{BasicBlock, Operand, RegFn},
};
/// A JS function's walk, as its C function is written
/// A walk of a JS function, as its C function is written
pub struct JsFunction<'a> {
pub program: &'a Program<'a>,
pub body: Body<'a>,
pub walk: usize,
pub context: Context, // the walk's function, and the call site that it is called from
pub f: &'a RegFn,
shared: Vec<Operand>, // the locals and args that closures share: each is a pointer to a cell
}
@@ -37,8 +39,7 @@ pub struct JsFunction<'a> {
/// `static <ret> js<walk>(<params>)`. A shared arg's param is named `p<k>`, because its value
/// goes into a cell named `a<k>`
pub fn signature(program: &Program, walk: usize) -> String {
let w = &program.cache.walks[walk];
let f = &program.fns[w.req.fn_idx];
let f = program.js_fn(walk);
let shared = f.captured_slots();
let params: Vec<String> = (0..f.arg_count)
@@ -58,19 +59,20 @@ pub fn signature(program: &Program, walk: usize) -> String {
} else {
params.join(", ")
};
let ret = program.types.of(&w.ret);
let ret = program.types.of(program.cache.ret(walk));
format!("static {} js{walk}({params})", ret.c_name())
}
/// The body of `js<walk>`: its locals, args and regs, then each block that can run
pub fn body(program: &Program, walk: usize) -> String {
let w = &program.cache.walks[walk];
let f = &program.fns[w.req.fn_idx];
let f = program.js_fn(walk);
let mut js = JsFunction {
program,
body: Body::new(program),
walk,
context: w.context,
f,
shared: f.captured_slots(),
};
@@ -134,10 +136,9 @@ impl JsFunction<'_> {
self.set(dst, value, CType::One(member));
}
/// Return `value`, of C type `from`
/// Return `value`, of C type `from`, as the C type of what the walk returns
pub fn ret(&mut self, value: &str, from: CType) {
let walk = &self.program.cache.walks[self.walk];
let to = self.program.types.of(&walk.ret);
let to = self.program.types.of(self.program.cache.ret(self.walk));
self.body.put("return ", value, from, to);
}
+13 -7
View File
@@ -81,7 +81,7 @@ impl<'a> Program<'a> {
}
}
/// One C function for each walk of a JS function in infer's last round, one for each
/// One C function for each walk in infer's last round, one for each
/// lookup, and the host's two entry points
fn add_functions(&mut self) {
let walks = (0..self.cache.walks.len()).filter(|&walk| self.cache.is_live(walk));
@@ -142,7 +142,7 @@ impl<'a> Program<'a> {
continue;
};
let cpool = &self.fns[self.cache.walks[walk].req.fn_idx].cpool;
let cpool = &self.js_fn(walk).cpool;
for instr in self.instrs(walk) {
let text = match instr.op {
@@ -183,9 +183,10 @@ impl<'a> Program<'a> {
fn value_types(&self, function: &Function) -> Vec<Type> {
match *function {
Function::Js(walk) => {
let f = &self.fns[self.cache.walks[walk].req.fn_idx];
let slots = slots(f).into_iter().map(|slot| self.slot_type(walk, slot));
slots.chain([self.cache.walks[walk].ret.clone()]).collect()
let slots = slots(self.js_fn(walk))
.into_iter()
.map(|slot| self.slot_type(walk, slot));
slots.chain([self.cache.ret(walk).clone()]).collect()
}
Function::Lookup(class, field) => vec![self.lookup_type(class, field)],
Function::Start | Function::Complete => vec![],
@@ -196,7 +197,7 @@ impl<'a> Program<'a> {
fn instrs(&self, walk: usize) -> impl Iterator<Item = &'a RegInstr> + use<'a> {
let (cache, fns) = (self.cache, self.fns);
let walk = &cache.walks[walk];
let f = &fns[walk.req.fn_idx];
let f = &fns[walk.context.fn_idx];
let blocks = f.blocks.iter().zip(&walk.reachable);
let blocks = blocks.filter(|(_, reachable)| **reachable);
blocks.flat_map(|(block, _)| &f.code[block.start..block.end])
@@ -214,7 +215,12 @@ pub struct Case {
pub first: bool, // the first case of its operands: an `if`. The others are `else if`
}
impl Program<'_> {
impl<'a> Program<'a> {
/// The JS function of a walk
pub fn js_fn(&self, walk: usize) -> &'a RegFn {
&self.fns[self.cache.walks[walk].context.fn_idx]
}
/// The type of a local, arg or reg of a JS function, in one walk
pub fn slot_type(&self, walk: usize, slot: Operand) -> Type {
let addr = self.cache.addr(walk, slot, self.fns);
+27 -24
View File
@@ -17,36 +17,39 @@ pub struct InstrRef {
pub instr_idx: usize,
}
/// The call instr that a request comes from. `None` for the entry fn, which the host calls
/// The call instr that a function is called from. `None` for a call from the host
pub type CallSite = Option<InstrRef>;
/// The objects that one instr makes, in the requests from one call site
/// A function, in one call site. Without polymorphism, infer would walk each context once.
/// Objects, and the bindings that closures share, are named by their context, so that every
/// walk of a context sees the same ones (see `TypeCache`)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ObjAddr {
pub made_at: InstrRef,
pub in_call: CallSite,
pub struct Context {
pub fn_idx: usize,
pub call_site: CallSite,
}
/// A location in the program, which memory holds a type for. A location that an object
/// can hold is named like an object, by where it is made and the call site of the request.
/// One that only its walk can see is named by the walk
/// The objects that one instr makes, in one context
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ObjAddr {
pub context: Context,
pub instr: usize,
}
/// A location in the program, which memory holds a type for
///
/// A request key can hold an address, in a `Type::Ref`. Every part of an address comes from
/// the program text (or the walks, which come from the keys), so there are finitely many
/// addresses and keys, and infer ends
/// Every location is named by its context and a place in its function, except the ones that
/// only one walk can see, which are named by the walk. A type can hold an object's address,
/// and a walk's arg types can hold that type, so objects are never named by a walk: that
/// could make new walks without end. Every part of an address comes from the program text or
/// from a walk, so there are finitely many, and infer ends
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Addr {
Object(ObjAddr), // an object
Object(ObjAddr), // an object
Field(ObjAddr, AtomRef), // a field of an object, unless the field shares another location
Captured {
fn_idx: usize,
slot: Operand,
in_call: CallSite,
}, // a binding that a closure shares (see transform/closure.rs)
Binding {
walk: usize,
slot: Operand,
}, // a binding that only its walk sees
Shared { context: Context, slot: Operand }, // a binding that closures share (see transform/closure.rs). The closures are objects, so it is named like them
Private { walk: usize, slot: Operand }, // a binding that only its walk sees
Return(usize), // what a walk returns
}
/// What each location holds, by its address. A location that nothing has reached holds
@@ -226,9 +229,9 @@ impl Memory {
/// Make its field `name` the location `addr` (see `define_field_ref`). Such a field is
/// always made when the object is made, so it is defined
///
/// An address stands for all the objects that one instr makes in one call site. Two
/// walks from that call site can share two locations into the field: walks of one fn
/// with different closures as their env, for example. A field is one location, so those
/// An address stands for all the objects that one instr makes in one context. Two walks
/// of that context can share two locations into the field: walks with different
/// closures as their env, for example. A field is one location, so those
/// two are one location too: they are merged, as the types of all its objects' fields are
/// joined. Their C types are then one type as well
pub fn share_field(&mut self, obj: ObjAddr, name: AtomRef, addr: Addr) {
+85 -89
View File
@@ -1,11 +1,23 @@
//! Infers a type for every location
//! Infers a type for every location in the program
//!
//! A request is a fn, the call site that calls it, and the types of its args. Every location
//! has an address (see `Addr`), and memory holds one type for each address. An object is
//! named by the instr that makes it and the call site of the request that runs the instr. A
//! binding is named by its walk, or like an object if a closure shares it. A walk reads
//! memory and the returns of other requests, which only grow, so infer walks the program in
//! rounds until one changes nothing
//! **The base model.** A context is a function in one call site. Infer walks each context:
//! it goes over the function's instrs, and joins the type of each result into memory, which
//! holds one type for each location (see `Addr`). An object is named by the instr that makes
//! it and its context. A call walks the callee in its own context, and what the callee
//! returns is a location too. A walk reads memory, and memory only grows, so infer walks the
//! program in rounds, until a round changes nothing.
//!
//! **Polymorphism.** A function can be called with args of different types. Each set of arg
//! types gets a walk of its own, so a walk is a context and the types of its args. This adds
//! three things to the base model:
//!
//! 1. A walk's arg types are part of its key. An arg passes only its kind, not a known value,
//! so that there are finitely many keys (see `key`).
//! 2. A binding that no closure shares is named by its walk, so that each walk has its own
//! types (see `binding_addr`). Objects and shared bindings stay named by their context.
//! 3. A walk can go stale. When an arg's type grows, its call has a new key, so the call
//! makes a new walk. Only the walks of the last round are part of the program (see
//! `TypeCache::is_live`).
mod memory;
mod types;
@@ -18,53 +30,40 @@ use crate::{
regify::{Operand, RegFn},
};
pub use memory::{Addr, Builtins, CallSite, InstrRef, Memory, ObjAddr};
pub use memory::{Addr, Builtins, CallSite, Context, InstrRef, Memory, ObjAddr};
pub use types::{LENGTH_FIELD, Shape, Type, join};
/// A fn, the call it comes from, and the values of its args. Values compare by their
/// types, and an object by its address, so there are finitely many requests. The call site
/// is part of the key because it names the objects that the walk makes
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct InferRequest {
pub fn_idx: usize,
pub in_call: CallSite,
pub arg_types: Vec<Type>, // one for each param
}
impl InferRequest {
/// The request of a call of `fn_idx` from `in_call` with args of these types. An arg it
/// does not pass is undefined, and an arg it passes beyond its params is never read.
/// Only an arg's kind crosses a call, not a known value, else every literal passed would
/// make a new walk
fn new(fn_idx: usize, in_call: CallSite, arg_types: &[Type], fns: &[RegFn]) -> Self {
let params = (0..fns[fn_idx].arg_count)
.map(|i| arg_types.get(i).map_or(Type::Undefined, Type::widen));
Self {
fn_idx,
in_call,
arg_types: params.collect(),
}
}
}
/// A request and what its walks found. Its index in `TypeCache::walks` names its bindings,
/// whose types are in memory
/// A context, and the types of its args: what a walk walks. What the walk found is in memory
#[derive(Debug)]
pub struct Walk {
pub req: InferRequest,
pub ret: Type, // what it returns
pub context: Context,
pub arg_types: Vec<Type>, // one for each param
pub reachable: Vec<bool>, // the blocks that can run
round: usize, // the last round that walked it. A walk that the last round did not reach is stale
walking: bool, // it is on the call stack: a recursive call gives `ret` as it is
walking: bool, // it is on the call stack: a recursive call gives what it returned so far
}
#[derive(Debug)]
pub struct TypeCache {
pub walks: Vec<Walk>,
ids: HashMap<InferRequest, usize>, // the walk of each request
ids: HashMap<(Context, Vec<Type>), usize>, // the walk of each context and arg types
pub memory: Memory,
round: usize,
changes: usize, // new walks and growths of a return. With the changes of memory, these tell whether a round found anything new
}
/// The key of a call of `fn_idx` from `call_site` with args of these types. An arg that the
/// call does not pass is undefined, and an arg that it passes beyond the params is never
/// read. Only an arg's kind passes, not a known value, else every literal that a call passes
/// would make a new walk
fn key(
fn_idx: usize,
call_site: CallSite,
arg_types: &[Type],
fns: &[RegFn],
) -> (Context, Vec<Type>) {
let params =
(0..fns[fn_idx].arg_count).map(|i| arg_types.get(i).map_or(Type::Undefined, Type::widen));
(Context { fn_idx, call_site }, params.collect())
}
impl TypeCache {
@@ -72,11 +71,11 @@ impl TypeCache {
pub fn find(
&self,
fn_idx: usize,
in_call: CallSite,
call_site: CallSite,
arg_types: &[Type],
fns: &[RegFn],
) -> usize {
self.ids[&InferRequest::new(fn_idx, in_call, arg_types, fns)]
self.ids[&key(fn_idx, call_site, arg_types, fns)]
}
/// Whether the last round walked `w`. Only such walks are part of the program
@@ -84,15 +83,21 @@ impl TypeCache {
self.walks[w].round == self.round
}
/// The address of a local, arg or reg of walk `w`
pub fn addr(&self, w: usize, slot: Operand, fns: &[RegFn]) -> Addr {
let req = &self.walks[w].req;
binding_addr(w, req, &fns[req.fn_idx].captured_slots(), slot)
/// What walk `w` returns
pub fn ret(&self, w: usize) -> &Type {
self.memory.get(Addr::Return(w))
}
/// Everything that can change, counted. Infer is done when a round leaves it the same
/// The address of a local, arg or reg of walk `w`
pub fn addr(&self, w: usize, slot: Operand, fns: &[RegFn]) -> Addr {
let context = self.walks[w].context;
binding_addr(w, context, &fns[context.fn_idx].captured_slots(), slot)
}
/// Everything that can change, counted: memory, and the walks. Infer is done when a round
/// leaves it the same
fn progress(&self) -> usize {
self.changes + self.memory.changes()
self.memory.changes() + self.walks.len()
}
/// The host's call of `__complete` with an op's handle: each closure that the binding can
@@ -112,66 +117,54 @@ impl TypeCache {
}
}
/// Infer a call of `fn_idx` from `in_call`, walking it unless this round walked it
/// Infer a call of `fn_idx` from `call_site`, walking it unless this round walked it
/// already. Gives what it returns
fn call(
&mut self,
fn_idx: usize,
in_call: CallSite,
call_site: CallSite,
arg_types: &[Type],
fns: &[RegFn],
) -> Type {
let req = InferRequest::new(fn_idx, in_call, arg_types, fns);
let (context, arg_types) = key(fn_idx, call_site, arg_types, fns);
let w = match self.ids.get(&req) {
let w = match self.ids.get(&(context, arg_types.clone())) {
Some(&w) => w,
None => {
self.ids.insert(req.clone(), self.walks.len());
self.ids
.insert((context, arg_types.clone()), self.walks.len());
self.walks.push(Walk {
req,
ret: Type::Unknown,
context,
arg_types,
reachable: vec![],
round: 0,
walking: false,
});
self.changes += 1;
self.walks.len() - 1
}
};
if self.walks[w].walking || self.is_live(w) {
return self.walks[w].ret.clone();
if !self.walks[w].walking && !self.is_live(w) {
self.walks[w].walking = true;
let reachable = walk::walk_fn(w, fns, self);
let walk = &mut self.walks[w];
walk.walking = false;
walk.round = self.round;
walk.reachable = reachable;
}
self.walks[w].walking = true;
let (ret, reachable) = walk::walk_fn(w, fns, self);
let walk = &mut self.walks[w];
let joined = types::join(&walk.ret, &ret);
if joined != walk.ret {
walk.ret = joined;
self.changes += 1;
}
walk.walking = false;
walk.round = self.round;
walk.reachable = reachable;
walk.ret.clone()
self.ret(w).clone()
}
}
/// The address of a local, arg or reg of walk `w` of `req`, given the slots of its fn that a
/// closure shares. A binding that a closure shares is named like an object, any other by
/// the walk
fn binding_addr(w: usize, req: &InferRequest, captured: &[Operand], slot: Operand) -> Addr {
match captured.contains(&slot) {
true => Addr::Captured {
fn_idx: req.fn_idx,
slot,
in_call: req.in_call,
},
false => Addr::Binding { walk: w, slot },
/// The address of a local, arg or reg of walk `w` of `context`, given the slots of its
/// function that closures share. A shared binding is named by its context, like the closures
/// that share it. Any other binding is named by its walk
fn binding_addr(w: usize, context: Context, shared: &[Operand], slot: Operand) -> Addr {
match shared.contains(&slot) {
true => Addr::Shared { context, slot },
false => Addr::Private { walk: w, slot },
}
}
@@ -189,7 +182,6 @@ pub fn infer_types(fns: &[RegFn], bindings: &ModuleBindings) -> TypeCache {
ids: HashMap::new(),
memory: Memory::default(),
round: 0,
changes: 0,
};
cache.memory.builtins = Builtins {
@@ -215,11 +207,15 @@ pub fn infer_types(fns: &[RegFn], bindings: &ModuleBindings) -> TypeCache {
}
/// The address of module binding `local` (see `flatten::ModuleBindings`): a local of the
/// entry, which the module fn captures
/// entry, which the module's function shares with its closures
pub fn module_binding(entry: usize, local: usize) -> Addr {
Addr::Captured {
let context = Context {
fn_idx: entry,
call_site: None,
};
Addr::Shared {
context,
slot: Operand::Local(local),
in_call: None,
}
}
+28 -31
View File
@@ -1,4 +1,5 @@
//! One walk: the types that one request gives the locals, args and regs of its fn
//! One walk: the types that one context and its arg types give the locals, args and regs of
//! a function
//!
//! The walk goes over the reachable blocks until nothing changes. Each instr has a rule,
//! which gives the type of its result from the types of its operands. The result joins into
@@ -6,8 +7,8 @@
//! reached the instr yet
use super::{
InferRequest, TypeCache, binding_addr,
memory::{Addr, InstrRef, ObjAddr},
TypeCache, binding_addr,
memory::{Addr, Context, InstrRef, ObjAddr},
types::{
BaseType, EMPTY_STRING, LENGTH_FIELD, Shape, Type, decide_pair, is_base, join, join_all,
loose_eq, pairs, strict_eq, truthiness,
@@ -20,19 +21,24 @@ use crate::{
transform::{FIRST_ARG, THIS_ARG},
};
/// Walk `w`. Gives what it returns, and the blocks that can run
pub fn walk_fn(w: usize, fns: &[RegFn], cache: &mut TypeCache) -> (Type, Vec<bool>) {
let req = cache.walks[w].req.clone();
let f = &fns[req.fn_idx];
/// Walk `w`. Gives the blocks that can run
pub fn walk_fn(w: usize, fns: &[RegFn], cache: &mut TypeCache) -> Vec<bool> {
let context = cache.walks[w].context;
let f = &fns[context.fn_idx];
// The args get their types from the walk's key
for (i, ty) in cache.walks[w].arg_types.clone().iter().enumerate() {
let addr = binding_addr(w, context, &f.captured_slots(), Operand::Arg(i));
cache.memory.join(addr, ty);
}
let walker = Walker {
w,
context,
f,
fns,
captured: f.captured_slots(),
req,
shared: f.captured_slots(),
cache,
ret: Type::Unknown,
reachable: vec![false; f.blocks.len()],
};
@@ -41,37 +47,28 @@ pub fn walk_fn(w: usize, fns: &[RegFn], cache: &mut TypeCache) -> (Type, Vec<boo
struct Walker<'a> {
w: usize,
req: InferRequest,
context: Context,
f: &'a RegFn,
fns: &'a [RegFn],
captured: Vec<Operand>, // the slots of `f` that a closure shares
shared: Vec<Operand>, // the slots of `f` that closures share
cache: &'a mut TypeCache,
ret: Type,
reachable: Vec<bool>,
}
impl Walker<'_> {
fn addr(&self, slot: Operand) -> Addr {
binding_addr(self.w, &self.req, &self.captured, slot)
binding_addr(self.w, self.context, &self.shared, slot)
}
/// The address of the objects that the instr at `idx` makes in this walk
/// The address of the objects that the instr at `idx` makes, in this context
fn here(&self, idx: usize) -> ObjAddr {
ObjAddr {
made_at: InstrRef {
fn_idx: self.req.fn_idx,
instr_idx: idx,
},
in_call: self.req.in_call,
context: self.context,
instr: idx,
}
}
fn run(mut self) -> (Type, Vec<bool>) {
// The args get their values from the request
for (i, ty) in self.req.arg_types.iter().enumerate() {
self.cache.memory.join(self.addr(Operand::Arg(i)), ty);
}
fn run(mut self) -> Vec<bool> {
self.reachable[0] = true;
// A return only changes once its block is reachable, and a read only gives more
@@ -87,7 +84,7 @@ impl Walker<'_> {
}
if (self.cache.progress(), &self.reachable) == (before.0, &before.1) {
return (self.ret, self.reachable);
return self.reachable;
}
}
}
@@ -146,7 +143,7 @@ impl Walker<'_> {
let ty = self.rule(instr, idx, &args);
if matches!(instr.op, Return | ReturnAsync | ReturnUndef) {
self.ret = join(&self.ret, &ty);
self.cache.memory.join(Addr::Return(self.w), &ty);
}
for &dst in &instr.dsts {
@@ -381,8 +378,8 @@ impl Walker<'_> {
/// `new` gives what the constructor returns if that is an object, else the new object
/// itself, `this`
fn call(&mut self, instr: &RegInstr, idx: usize, args: &[Type]) -> Type {
let in_call = Some(InstrRef {
fn_idx: self.req.fn_idx,
let call_site = Some(InstrRef {
fn_idx: self.context.fn_idx,
instr_idx: idx,
});
@@ -402,7 +399,7 @@ impl Walker<'_> {
let mut call_args = args.to_vec();
call_args[0] = callee.clone();
self.cache.call(fn_idx, in_call, &call_args, self.fns)
self.cache.call(fn_idx, call_site, &call_args, self.fns)
})
.collect();
+1 -1
View File
@@ -67,7 +67,7 @@
//! The owner's code does not change for this: `get_loc k` and `put_loc k` always use the
//! current location of `k`. This module keeps `close_loc` as it is. Then:
//! - infer can ignore it. The old and new locations of a binding have the same address
//! (`Addr::Captured`), so their types are joined
//! (`Addr::Shared`), so their types are joined
//! - `escape.rs` must give each iteration a new location if a closure can see the old one
//! after the `close_loc`
//!