Skip to content

Commit d91278e

Browse files
Merge branch 'main' into lean-printer
2 parents bcc0d4c + e8cb72f commit d91278e

File tree

8 files changed

+58
-32
lines changed

8 files changed

+58
-32
lines changed

.github/workflows/charon.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
on:
22
pull_request:
3+
workflow_dispatch:
34

45
jobs:
56
charon:

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ Quicklinks:
122122
## Hacking on Hax
123123
The documentation of the internal crate of hax and its engine can be
124124
found [here for the engine](https://hax.cryspen.com/engine/index.html)
125-
and [here for the frontent](https://hax.cryspen.com/frontend/index.html).
125+
and [here for the frontend](https://hax.cryspen.com/frontend/index.html).
126126
127127
### Edit the sources (Nix)
128128

engine/bin/lib.ml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ let parse_id_table_node (json : Yojson.Safe.t) :
186186
in
187187
(table, value)
188188

189-
let load_table (check_version : bool) : Yojson.Safe.t =
189+
let load_table ?(check_version = true) : Yojson.Safe.t =
190190
let table, json =
191191
Hax_io.read_json () |> Option.value_exn |> parse_id_table_node
192192
in
@@ -212,7 +212,7 @@ Please reinstall hax.
212212
json
213213
214214
let parse_options () =
215-
let json = load_table true in
215+
let json = load_table ~check_version:true in
216216
let options = [%of_yojson: Types.engine_options] json in
217217
Profiling.enabled := options.backend.profile;
218218
options
@@ -258,7 +258,7 @@ module ExportFStarAst = Export_ast.Make (Fstar_backend.InputLanguage)
258258
let driver_for_rust_engine () : unit =
259259
let query : Rust_engine_types.query =
260260
(* TODO: support for table *)
261-
(* let json = load_table false in *)
261+
(* let json = load_table ~check_version:false in *)
262262
let json = Hax_io.read_json () |> Option.value_exn in
263263
[%of_yojson: Rust_engine_types.query] json
264264
in

frontend/exporter/src/traits/resolution.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_trait_selection::traits::ImplSource;
1313

1414
use crate::{self_predicate, traits::utils::erase_and_norm};
1515

16-
use super::utils::{implied_predicates, required_predicates, ToPolyTraitRef};
16+
use super::utils::{implied_predicates, normalize_bound_val, required_predicates, ToPolyTraitRef};
1717

1818
#[derive(Debug, Clone)]
1919
pub enum PathChunk<'tcx> {
@@ -281,7 +281,7 @@ impl<'tcx> PredicateSearcher<'tcx> {
281281
let mut new_candidates = Vec::new();
282282
for mut candidate in candidates {
283283
// Normalize and erase all lifetimes.
284-
candidate.pred = erase_and_norm(tcx, self.typing_env, candidate.pred);
284+
candidate.pred = normalize_bound_val(tcx, self.typing_env, candidate.pred);
285285
if let Entry::Vacant(entry) = self.candidates.entry(candidate.pred) {
286286
entry.insert(candidate.clone());
287287
new_candidates.push(candidate);
@@ -421,7 +421,7 @@ impl<'tcx> PredicateSearcher<'tcx> {
421421
let tcx = self.tcx;
422422
let drop_trait = tcx.lang_items().drop_trait().unwrap();
423423

424-
let erased_tref = erase_and_norm(self.tcx, self.typing_env, *tref);
424+
let erased_tref = normalize_bound_val(self.tcx, self.typing_env, *tref);
425425
let trait_def_id = erased_tref.skip_binder().def_id;
426426

427427
let impl_source = shallow_resolve_trait_ref(tcx, self.typing_env.param_env, erased_tref);

frontend/exporter/src/traits/utils.rs

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,17 @@ pub fn implied_predicates<'tcx>(
167167
}
168168
}
169169

170-
/// Erase all regions. Largely copied from `tcx.erase_regions`.
171-
pub fn erase_all_regions<'tcx, T>(tcx: TyCtxt<'tcx>, value: T) -> T
170+
/// Erase free regions from the given value. Largely copied from `tcx.erase_regions`, but also
171+
/// erases bound regions that are bound outside `value`, so we can call this function inside a
172+
/// `Binder`.
173+
fn erase_free_regions<'tcx, T>(tcx: TyCtxt<'tcx>, value: T) -> T
172174
where
173175
T: TypeFoldable<TyCtxt<'tcx>>,
174176
{
175177
use rustc_middle::ty;
176178
struct RegionEraserVisitor<'tcx> {
177179
tcx: TyCtxt<'tcx>,
180+
depth: u32,
178181
}
179182

180183
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RegionEraserVisitor<'tcx> {
@@ -190,33 +193,53 @@ where
190193
where
191194
T: TypeFoldable<TyCtxt<'tcx>>,
192195
{
193-
// Empty the binder
194-
Binder::dummy(t.skip_binder().fold_with(self))
196+
let t = self.tcx.anonymize_bound_vars(t);
197+
self.depth += 1;
198+
let t = t.super_fold_with(self);
199+
self.depth -= 1;
200+
t
195201
}
196202

197-
fn fold_region(&mut self, _r: ty::Region<'tcx>) -> ty::Region<'tcx> {
198-
// We erase bound regions despite it being possibly incorrect. `for<'a> fn(&'a
199-
// ())` and `fn(&'free ())` are different types: they may implement different
200-
// traits and have a different `TypeId`. It's unclear whether this can cause us
201-
// to select the wrong trait reference.
202-
self.tcx.lifetimes.re_erased
203+
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
204+
// We don't erase bound regions that are bound inside the expression we started with,
205+
// but we do erase those that point "outside of it".
206+
match r.kind() {
207+
ty::ReBound(dbid, _) if dbid.as_u32() < self.depth => r,
208+
_ => self.tcx.lifetimes.re_erased,
209+
}
203210
}
204211
}
205-
value.fold_with(&mut RegionEraserVisitor { tcx })
212+
value.fold_with(&mut RegionEraserVisitor { tcx, depth: 0 })
206213
}
207214

208-
// Lifetimes are irrelevant when resolving instances.
215+
// Normalize and erase lifetimes, erasing more lifetimes than normal because we might be already
216+
// inside a binder and rustc doesn't like that.
209217
pub fn erase_and_norm<'tcx, T>(tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>, x: T) -> T
210218
where
211219
T: TypeFoldable<TyCtxt<'tcx>> + Copy,
212220
{
213-
erase_all_regions(
221+
erase_free_regions(
214222
tcx,
215223
tcx.try_normalize_erasing_regions(typing_env, x)
216224
.unwrap_or(x),
217225
)
218226
}
219227

228+
/// Given our currently hacky handling of binders, in order for trait resolution to work we must
229+
/// empty out the binders of trait refs. Specifically it's so that we can reconnect associated type
230+
/// constraints with the trait ref they come from, given that the projection in question doesn't
231+
/// track the right binder currently.
232+
pub fn normalize_bound_val<'tcx, T>(
233+
tcx: TyCtxt<'tcx>,
234+
typing_env: TypingEnv<'tcx>,
235+
x: Binder<'tcx, T>,
236+
) -> Binder<'tcx, T>
237+
where
238+
T: TypeFoldable<TyCtxt<'tcx>> + Copy,
239+
{
240+
Binder::dummy(erase_and_norm(tcx, typing_env, x.skip_binder()))
241+
}
242+
220243
pub trait ToPolyTraitRef<'tcx> {
221244
fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>;
222245
}

rust-engine/README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
This crate should implement an AST for which:
2-
1. Valid (cargo check) pretty-printed Rust can be produced out of it.
3-
2. The Rust THIR AST from the frontend can be imported into this AST.
4-
3. The AST defined in the OCaml engine can be imported into this AST.
5-
4. This AST can be exported to the OCaml engine.
6-
5. This AST should be suitable for AST transformations.
1+
# Hax Rust Engine
2+
3+
This crate implements an alternative engine for Rust: the main one is implemented in OCaml and is located in `/engine`.
4+
This Rust engine is designed so that it can re-use some bits of the OCaml engine.
5+
6+
The plan is to slowly deprecate the OCaml engine, rewrite most of its components and drop it.
77

88
## Usage
9-
The following command will run hax with the rust engine instead of the ocaml one.
10-
For now, this will create a dummy lean file, regardless the backend provided.
9+
The Rust engine supports only one backend for now: `Lean`.
10+
The Lean backend is currently empty and produces only a dummy file.
1111

12+
To run it, use the follwing command:
1213
```bash
13-
HAX_ENGINE_BINARY=hax-rust-engine cargo hax into fstar
14+
cargo hax into lean
1415
```

rust-engine/src/ast.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1418,7 +1418,7 @@ pub enum ItemKind {
14181418
/// A resugared item.
14191419
/// This variant is introduced before printing only.
14201420
/// Phases must not produce this variant.
1421-
Resugared(ResugaredTyKind),
1421+
Resugared(ResugaredItemKind),
14221422

14231423
/// Item that is not implemented yet
14241424
NotImplementedYet,

rust-engine/src/ocaml_engine.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub enum Response {
4646
},
4747
}
4848

49-
/// Extends the common `ToEngine` messages with one extra case: `Input`.
49+
/// Extends the common `ToEngine` messages with one extra case: `Query`.
5050
#[derive(::serde::Deserialize, ::serde::Serialize)]
5151
#[serde(untagged)]
5252
pub enum ExtendedToEngine {
@@ -94,12 +94,13 @@ impl Query {
9494
.expect("Could not write on stdin"),
9595
);
9696

97-
// TODO: send a table here
97+
// TODO: send a table here (see https://github.com/cryspen/hax/issues/1536)
9898
send!(stdin, self);
9999

100100
let mut response = None;
101101
let stdout = std::io::BufReader::new(engine_subprocess.stdout.take().unwrap());
102102
// TODO: this should be streaming (i.e. use a `LineAsEOF` reader wrapper that consumes a reader until `\n` occurs)
103+
// See https://github.com/cryspen/hax/issues/1537.
103104
for slice in stdout.split(b'\n') {
104105
let msg = (|| {
105106
let slice = slice.ok()?;

0 commit comments

Comments
 (0)