From af98e2819242c8e5dabe15d07cdeff121b95d98f Mon Sep 17 00:00:00 2001 From: Fallible <118682743+fallible-algebra@users.noreply.github.com> Date: Mon, 6 Jul 2026 10:05:07 +0100 Subject: [PATCH 01/13] empty file --- src/analysis/hir-tyck.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/analysis/hir-tyck.md diff --git a/src/analysis/hir-tyck.md b/src/analysis/hir-tyck.md new file mode 100644 index 0000000000..e69de29bb2 From b426605509874685fd9490a88b152eeb686b4b0f Mon Sep 17 00:00:00 2001 From: Fallible <118682743+fallible-algebra@users.noreply.github.com> Date: Mon, 6 Jul 2026 16:13:03 +0100 Subject: [PATCH 02/13] Some expectations writeup --- src/analysis/hir-tyck.md | 0 src/hir-typeck/expectations.md | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+) delete mode 100644 src/analysis/hir-tyck.md create mode 100644 src/hir-typeck/expectations.md diff --git a/src/analysis/hir-tyck.md b/src/analysis/hir-tyck.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/hir-typeck/expectations.md b/src/hir-typeck/expectations.md new file mode 100644 index 0000000000..d4be2d83a7 --- /dev/null +++ b/src/hir-typeck/expectations.md @@ -0,0 +1,23 @@ +# Expectations + +In an ideal world, we would only perform type inference at one point in the compiler: once all constraints have been collected. + +In reality, we perform it (at least) twice: First, at eager evaluation points, and secondly "at the end." + +`Expectations` are a piece of type-checking state we maintain for the cases where we need to eagerly infer the types of expressions rather than leave them to the end. + +The main subjects of "eager type inference" are: + +- Method calls +- Closures (their signatures could be higher-ranked) +- Coercions +- fields +- indexing (because of weird dereferencing stuff this isn't just a Method Call) + + +## Closures and Higher-Ranked Variables + +Closures need to have type inference eagerly applied to them because [reasons]. + +Papers: +- [This one](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/putting.pdf) \ No newline at end of file From f502a6864deb762366f1ae22f9f0359ff50d4ad1 Mon Sep 17 00:00:00 2001 From: Fallible <118682743+fallible-algebra@users.noreply.github.com> Date: Fri, 10 Jul 2026 16:18:28 +0100 Subject: [PATCH 03/13] Some assertions, mostly blank space --- src/hir-typeck/expectations.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/hir-typeck/expectations.md b/src/hir-typeck/expectations.md index d4be2d83a7..e0515aba5a 100644 --- a/src/hir-typeck/expectations.md +++ b/src/hir-typeck/expectations.md @@ -2,22 +2,27 @@ In an ideal world, we would only perform type inference at one point in the compiler: once all constraints have been collected. -In reality, we perform it (at least) twice: First, at eager evaluation points, and secondly "at the end." +In reality, we perform it (at least) twice: First, at eager evaluation points, and secondly "at the end" when those constraints have been collected. `Expectations` are a piece of type-checking state we maintain for the cases where we need to eagerly infer the types of expressions rather than leave them to the end. -The main subjects of "eager type inference" are: +## Eager Type Checking / Inference. -- Method calls -- Closures (their signatures could be higher-ranked) -- Coercions -- fields -- indexing (because of weird dereferencing stuff this isn't just a Method Call) + +### Closures and Higher-Ranked Variables -## Closures and Higher-Ranked Variables +Closures need to have type inference eagerly applied to them because they are functions that are rarely fully annotated. Top-level functions i.e. `fn (what_that_is: T, what_it_isnt: Y) -> bool {/* */}` have their input / output types fully defined (opaque types still being an explicit annotation) but closures tend to have most/all of their type annotations missing, like `|a, b| if a < b {vec![1, 2, 3]} else {vec![6, 7, 8]} `. -Closures need to have type inference eagerly applied to them because [reasons]. +Eager, Higher-Ranked type inference happens in closures because closures can introduce Higher-Ranked Lifetimes. `for<'a> T<'a>` is the only style of higher-ranked bound in Rust, and these can appear in the types of closures. + +### Method calls + +### Coercions + +### Fields + +### Indexing Papers: - [This one](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/putting.pdf) \ No newline at end of file From 9736afb2370c7c1ae40075b2edb032c6f4026fa8 Mon Sep 17 00:00:00 2001 From: Fallible <118682743+fallible-algebra@users.noreply.github.com> Date: Fri, 10 Jul 2026 16:39:50 +0100 Subject: [PATCH 04/13] More sketching --- src/hir-typeck/eager-inference.md | 1 + src/hir-typeck/expectations.md | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 src/hir-typeck/eager-inference.md diff --git a/src/hir-typeck/eager-inference.md b/src/hir-typeck/eager-inference.md new file mode 100644 index 0000000000..894af9e527 --- /dev/null +++ b/src/hir-typeck/eager-inference.md @@ -0,0 +1 @@ +## Eager Type Inference \ No newline at end of file diff --git a/src/hir-typeck/expectations.md b/src/hir-typeck/expectations.md index e0515aba5a..ada16e28eb 100644 --- a/src/hir-typeck/expectations.md +++ b/src/hir-typeck/expectations.md @@ -4,7 +4,7 @@ In an ideal world, we would only perform type inference at one point in the comp In reality, we perform it (at least) twice: First, at eager evaluation points, and secondly "at the end" when those constraints have been collected. -`Expectations` are a piece of type-checking state we maintain for the cases where we need to eagerly infer the types of expressions rather than leave them to the end. +`Expectations` are a piece of type inference state we maintain for the cases where we need to eagerly infer the types of expressions rather than leave them to the end. ## Eager Type Checking / Inference. @@ -16,13 +16,22 @@ Closures need to have type inference eagerly applied to them because they are fu Eager, Higher-Ranked type inference happens in closures because closures can introduce Higher-Ranked Lifetimes. `for<'a> T<'a>` is the only style of higher-ranked bound in Rust, and these can appear in the types of closures. -### Method calls ### Coercions -### Fields +Actually yes does use expectations. + +### Method calls? + +Maybe Not. Maybe just point to [method lookup](./method-lookup.md). + +### Fields? + +Maybe Not ### Indexing +lcnr said so. + Papers: - [This one](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/putting.pdf) \ No newline at end of file From 808e1d30edfdd54adba4d53ad7ecec591430aeb9 Mon Sep 17 00:00:00 2001 From: Fallible <118682743+fallible-algebra@users.noreply.github.com> Date: Mon, 20 Jul 2026 10:59:55 +0100 Subject: [PATCH 05/13] Add a bunch of wrong stuff to check later --- src/hir-typeck/expectations.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/hir-typeck/expectations.md b/src/hir-typeck/expectations.md index ada16e28eb..81a082cdcd 100644 --- a/src/hir-typeck/expectations.md +++ b/src/hir-typeck/expectations.md @@ -1,4 +1,4 @@ -# Expectations +# Expectations and Eager Type Inference In an ideal world, we would only perform type inference at one point in the compiler: once all constraints have been collected. @@ -6,32 +6,42 @@ In reality, we perform it (at least) twice: First, at eager evaluation points, a `Expectations` are a piece of type inference state we maintain for the cases where we need to eagerly infer the types of expressions rather than leave them to the end. -## Eager Type Checking / Inference. +## Eager Type Inference +Eager evaluation of the type of a term (type inference) is required at some specific points to either make type inference more consistent or make it even possible, in the case of higher-ranked bounds / Higher Ranked Lifetime bounds. + +Eager evaluation is when we do type inference earlier than we otherwise would. To do this, we bring in Expectations. + ### Closures and Higher-Ranked Variables Closures need to have type inference eagerly applied to them because they are functions that are rarely fully annotated. Top-level functions i.e. `fn (what_that_is: T, what_it_isnt: Y) -> bool {/* */}` have their input / output types fully defined (opaque types still being an explicit annotation) but closures tend to have most/all of their type annotations missing, like `|a, b| if a < b {vec![1, 2, 3]} else {vec![6, 7, 8]} `. Eager, Higher-Ranked type inference happens in closures because closures can introduce Higher-Ranked Lifetimes. `for<'a> T<'a>` is the only style of higher-ranked bound in Rust, and these can appear in the types of closures. +The above is only slightly true. Let's be wrong about it in more interesting ways. ### Coercions -Actually yes does use expectations. +? [Coercions](https://doc.rust-lang.org/reference/type-coercions.html) engage in eager type inference as we need to know the type of a coerced type as early as possible for some reason idk. Maybe this is fail-early stuff? Need to engage in investigation. ### Method calls? Maybe Not. Maybe just point to [method lookup](./method-lookup.md). +? Method calls engage in Coercion and therefore need to engage in Eager Type Inference. + ### Fields? -Maybe Not +? Fields engage in corcion and therefore need to engage in eager type inference. ### Indexing +? Indexing engages in coercion and therefore needs to engage in eager type inference. + lcnr said so. Papers: -- [This one](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/putting.pdf) \ No newline at end of file +- [Practical Type Inference for Arbitrary-Rank Types, Jones ](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/putting.pdf) +- [Local type inference (referenced in PTIfART)] \ No newline at end of file From 2f8e2f04661269182859cd0a8fd35032cdedfed3 Mon Sep 17 00:00:00 2001 From: Fallible <118682743+fallible-algebra@users.noreply.github.com> Date: Fri, 24 Jul 2026 10:40:35 +0100 Subject: [PATCH 06/13] Can't keep fussing over this --- src/hir-typeck/expectations.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/hir-typeck/expectations.md b/src/hir-typeck/expectations.md index 81a082cdcd..560a3ded9f 100644 --- a/src/hir-typeck/expectations.md +++ b/src/hir-typeck/expectations.md @@ -4,7 +4,7 @@ In an ideal world, we would only perform type inference at one point in the comp In reality, we perform it (at least) twice: First, at eager evaluation points, and secondly "at the end" when those constraints have been collected. -`Expectations` are a piece of type inference state we maintain for the cases where we need to eagerly infer the types of expressions rather than leave them to the end. +`Expectations` are a piece of type inference state we maintain for the cases where we need to eagerly infer the types of expressions rather than leave them to the end. They allow us to "ask questions" of the form "hey, we're expecting this term to have this type, is this true?" ## Eager Type Inference @@ -16,15 +16,15 @@ Eager evaluation is when we do type inference earlier than we otherwise would. T ### Closures and Higher-Ranked Variables -Closures need to have type inference eagerly applied to them because they are functions that are rarely fully annotated. Top-level functions i.e. `fn (what_that_is: T, what_it_isnt: Y) -> bool {/* */}` have their input / output types fully defined (opaque types still being an explicit annotation) but closures tend to have most/all of their type annotations missing, like `|a, b| if a < b {vec![1, 2, 3]} else {vec![6, 7, 8]} `. +Closures need to have type inference eagerly applied to them because they are functions that are rarely fully annotated. Top-level functions i.e. `fn is_even(number: i32) -> bool {number % 2 == 0}` have their input / output types fully defined (opaque types still being an explicit annotation) but closures tend to have most/all of their type annotations missing, like `|a, b| if a < b {vec![1, 2, 3]} else {vec![6, 7, 8]}`. -Eager, Higher-Ranked type inference happens in closures because closures can introduce Higher-Ranked Lifetimes. `for<'a> T<'a>` is the only style of higher-ranked bound in Rust, and these can appear in the types of closures. +If we didn't do eager type inference we would instead have closures whose types were filled with inference variables. This would be able to solve in some situations, but because we do not have Higher-Ranked Inference Variables[^higher-ranked-inference] this would make the higher-ranked bounds for lifetimes that rust can have unusable without more annotation. The above is only slightly true. Let's be wrong about it in more interesting ways. -### Coercions +### Coercions -? [Coercions](https://doc.rust-lang.org/reference/type-coercions.html) engage in eager type inference as we need to know the type of a coerced type as early as possible for some reason idk. Maybe this is fail-early stuff? Need to engage in investigation. +[Coercions](./coercions.md) can happen in many places, and so we check for them and perform them when able. When we successfully find a coercion, we need to eagerly perform type inference/checking on it as future inference will require or benefit from this information to be known ahead of time. ### Method calls? @@ -34,7 +34,9 @@ Maybe Not. Maybe just point to [method lookup](./method-lookup.md). ### Fields? -? Fields engage in corcion and therefore need to engage in eager type inference. +Field access is inherently typed, so when we are doing field access we want to be able to know what a type is as early as possible. + +? There might be something about deref here idk. ### Indexing @@ -44,4 +46,6 @@ lcnr said so. Papers: - [Practical Type Inference for Arbitrary-Rank Types, Jones ](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/putting.pdf) -- [Local type inference (referenced in PTIfART)] \ No newline at end of file +- [Local type inference (referenced in PTIfART)] + +[^higher-ranked-inference]: \ No newline at end of file From f7156a20599d4695141283db14aba3e884d39019 Mon Sep 17 00:00:00 2001 From: Fallible <118682743+fallible-algebra@users.noreply.github.com> Date: Fri, 24 Jul 2026 10:51:02 +0100 Subject: [PATCH 07/13] Link to HRIV --- src/hir-typeck/expectations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hir-typeck/expectations.md b/src/hir-typeck/expectations.md index 560a3ded9f..db513f4b59 100644 --- a/src/hir-typeck/expectations.md +++ b/src/hir-typeck/expectations.md @@ -48,4 +48,4 @@ Papers: - [Practical Type Inference for Arbitrary-Rank Types, Jones ](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/putting.pdf) - [Local type inference (referenced in PTIfART)] -[^higher-ranked-inference]: \ No newline at end of file +[^higher-ranked-inference]: https://github.com/rust-lang/types-team/issues/131 \ No newline at end of file From 780b6f6eed3ed3de3c3cfab84b1497504177fe13 Mon Sep 17 00:00:00 2001 From: Fallible <118682743+fallible-algebra@users.noreply.github.com> Date: Mon, 27 Jul 2026 13:17:53 +0100 Subject: [PATCH 08/13] Re-frame around eager type inference rather than expectations specifically --- src/SUMMARY.md | 1 + src/hir-typeck/eager-inference.md | 50 +++++++++++++++++++++++++++++- src/hir-typeck/expectations.md | 51 ------------------------------- 3 files changed, 50 insertions(+), 52 deletions(-) delete mode 100644 src/hir-typeck/expectations.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 7c4ffe7b05..823a8bb15b 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -207,6 +207,7 @@ - [Coherence checking](./coherence.md) - [HIR Type checking](./hir-typeck/summary.md) - [Coercions](./hir-typeck/coercions.md) + - [Eager Inference](./hir-typeck/eager-inference.md) - [Method lookup](./hir-typeck/method-lookup.md) - [Const generics](./const-generics.md) - [Opaque types](./opaque-types-type-alias-impl-trait.md) diff --git a/src/hir-typeck/eager-inference.md b/src/hir-typeck/eager-inference.md index 894af9e527..43bbf407cc 100644 --- a/src/hir-typeck/eager-inference.md +++ b/src/hir-typeck/eager-inference.md @@ -1 +1,49 @@ -## Eager Type Inference \ No newline at end of file +# Eager Type Inference + +There are places during compilation where we need to depend on the current state of inference. This means we need to establish the type of a {term, item, etc, figure out exact wording in review} earlier than we otherwise would. + +Eager evaluation of the type of a term (type inference) is required at some specific points to either make later type inference more consistent or (in the case of higher-ranked bounds / Higher Ranked Lifetime bounds) make it even possible. + +Eager type inference is when we do type inference earlier than we otherwise would. To do this, we bring in Expectations as an additional piece of context. + +### Closures and Higher-Ranked Variables + +Closures need to have type inference eagerly applied to them because they are functions that are rarely fully annotated. Top-level functions i.e. `fn is_even(number: i32) -> bool {number % 2 == 0}` have their input / output types fully defined (opaque types still being an explicit annotation) but closures tend to have most/all of their type annotations missing, like `|a, b| if a < b {vec![1, 2, 3]} else {vec![6, 7, 8]}`. + +If we didn't do eager type inference we would instead have closures whose types were filled with inference variables. This would be able to solve in some situations, but because we do not have Higher-Ranked Inference Variables[^higher-ranked-inference] this would make the higher-ranked bounds for lifetimes that rust can have unusable without more annotation. + +The above is only slightly true. Let's be wrong about it in more interesting ways. + +### Coercions + +[Coercions](./coercions.md) can happen in many places, and so we check for them and perform them when able. When we successfully find a coercion, we need to eagerly perform type inference/checking on it as future inference will require or benefit from this information to be known ahead of time. + +## Expectations + +`Expectations` are a piece of type inference state we maintain for the cases where we need to eagerly infer the types of expressions rather than leave them to the end. They allow us to "ask questions" of the form "hey, we're expecting this term to have this type, is this true?" + +### Method calls, Fields, and Indexes. + +These are areas which technically take expectations, but in practice use them for + +Maybe Not. Maybe just point to [method lookup](./method-lookup.md). + +? Method calls engage in Coercion and therefore need to engage in Eager Type Inference. + +### Fields? + +Field access is inherently typed, so when we are doing field access we want to be able to know what a type is as early as possible. + +? There might be something about deref here idk. + +### Indexing + +? Indexing engages in coercion and therefore needs to engage in eager type inference. + +lcnr said so. + +Papers: +- [Practical Type Inference for Arbitrary-Rank Types, Jones ](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/putting.pdf) +- [Local type inference (referenced in PTIfART)] + +[^higher-ranked-inference]: https://github.com/rust-lang/types-team/issues/131 \ No newline at end of file diff --git a/src/hir-typeck/expectations.md b/src/hir-typeck/expectations.md deleted file mode 100644 index db513f4b59..0000000000 --- a/src/hir-typeck/expectations.md +++ /dev/null @@ -1,51 +0,0 @@ -# Expectations and Eager Type Inference - -In an ideal world, we would only perform type inference at one point in the compiler: once all constraints have been collected. - -In reality, we perform it (at least) twice: First, at eager evaluation points, and secondly "at the end" when those constraints have been collected. - -`Expectations` are a piece of type inference state we maintain for the cases where we need to eagerly infer the types of expressions rather than leave them to the end. They allow us to "ask questions" of the form "hey, we're expecting this term to have this type, is this true?" - -## Eager Type Inference - - - -Eager evaluation of the type of a term (type inference) is required at some specific points to either make type inference more consistent or make it even possible, in the case of higher-ranked bounds / Higher Ranked Lifetime bounds. - -Eager evaluation is when we do type inference earlier than we otherwise would. To do this, we bring in Expectations. - -### Closures and Higher-Ranked Variables - -Closures need to have type inference eagerly applied to them because they are functions that are rarely fully annotated. Top-level functions i.e. `fn is_even(number: i32) -> bool {number % 2 == 0}` have their input / output types fully defined (opaque types still being an explicit annotation) but closures tend to have most/all of their type annotations missing, like `|a, b| if a < b {vec![1, 2, 3]} else {vec![6, 7, 8]}`. - -If we didn't do eager type inference we would instead have closures whose types were filled with inference variables. This would be able to solve in some situations, but because we do not have Higher-Ranked Inference Variables[^higher-ranked-inference] this would make the higher-ranked bounds for lifetimes that rust can have unusable without more annotation. - -The above is only slightly true. Let's be wrong about it in more interesting ways. - -### Coercions - -[Coercions](./coercions.md) can happen in many places, and so we check for them and perform them when able. When we successfully find a coercion, we need to eagerly perform type inference/checking on it as future inference will require or benefit from this information to be known ahead of time. - -### Method calls? - -Maybe Not. Maybe just point to [method lookup](./method-lookup.md). - -? Method calls engage in Coercion and therefore need to engage in Eager Type Inference. - -### Fields? - -Field access is inherently typed, so when we are doing field access we want to be able to know what a type is as early as possible. - -? There might be something about deref here idk. - -### Indexing - -? Indexing engages in coercion and therefore needs to engage in eager type inference. - -lcnr said so. - -Papers: -- [Practical Type Inference for Arbitrary-Rank Types, Jones ](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/putting.pdf) -- [Local type inference (referenced in PTIfART)] - -[^higher-ranked-inference]: https://github.com/rust-lang/types-team/issues/131 \ No newline at end of file From 273aae6c1f24425991915c4e4f61074eba1d8ecc Mon Sep 17 00:00:00 2001 From: Fallible <118682743+fallible-algebra@users.noreply.github.com> Date: Wed, 29 Jul 2026 09:14:06 +0100 Subject: [PATCH 09/13] Some rewording --- src/hir-typeck/eager-inference.md | 39 +++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/hir-typeck/eager-inference.md b/src/hir-typeck/eager-inference.md index 43bbf407cc..b4efa30b0e 100644 --- a/src/hir-typeck/eager-inference.md +++ b/src/hir-typeck/eager-inference.md @@ -8,40 +8,59 @@ Eager type inference is when we do type inference earlier than we otherwise woul ### Closures and Higher-Ranked Variables -Closures need to have type inference eagerly applied to them because they are functions that are rarely fully annotated. Top-level functions i.e. `fn is_even(number: i32) -> bool {number % 2 == 0}` have their input / output types fully defined (opaque types still being an explicit annotation) but closures tend to have most/all of their type annotations missing, like `|a, b| if a < b {vec![1, 2, 3]} else {vec![6, 7, 8]}`. +Top-level functions, such as the following, have their types fully annotated at their definition site: -If we didn't do eager type inference we would instead have closures whose types were filled with inference variables. This would be able to solve in some situations, but because we do not have Higher-Ranked Inference Variables[^higher-ranked-inference] this would make the higher-ranked bounds for lifetimes that rust can have unusable without more annotation. +```rust +fn is_even(number: i32) -> bool { + number % 2 == 0 +} +``` -The above is only slightly true. Let's be wrong about it in more interesting ways. +This makes inference at points where they're used relatively easy. We know it's a `fn(i32) -> bool`, so when we give it an `i32` we know the expression is a `bool`. -### Coercions +_Closures_ need to have type inference eagerly applied to them because they are functions that are rarely fully annotated: -[Coercions](./coercions.md) can happen in many places, and so we check for them and perform them when able. When we successfully find a coercion, we need to eagerly perform type inference/checking on it as future inference will require or benefit from this information to be known ahead of time. +```rust +let closure = |a, b| if a < b {vec![1, 2, 3]} else {vec![5, 6, 7]}; +``` -## Expectations +If we didn't do eager type inference we would instead have closures whose types were filled with [inference variables](../appendix/glossary.md#inf-var). -`Expectations` are a piece of type inference state we maintain for the cases where we need to eagerly infer the types of expressions rather than leave them to the end. They allow us to "ask questions" of the form "hey, we're expecting this term to have this type, is this true?" +This would be able to be solved in some situations, but because we do not have Higher-Ranked Inference Variables[^higher-ranked-inference] this would make the higher-ranked bounds for lifetimes that rust can have unusable without more annotation. + +### Coercions + +[Coercions](./coercions.md) can happen in many places. We check to see if a coercion can happen, and if it can we perform the coercion. + +When we successfully find a coercion, we need to eagerly perform type inference/checking on it as future inference will require or benefit from this information to be known ahead of time. ### Method calls, Fields, and Indexes. -These are areas which technically take expectations, but in practice use them for +These are areas which technically take expectations, but in practice use them for diagnostics only. + +#### Methods Maybe Not. Maybe just point to [method lookup](./method-lookup.md). ? Method calls engage in Coercion and therefore need to engage in Eager Type Inference. -### Fields? +#### Fields? Field access is inherently typed, so when we are doing field access we want to be able to know what a type is as early as possible. ? There might be something about deref here idk. -### Indexing +#### Indexing ? Indexing engages in coercion and therefore needs to engage in eager type inference. lcnr said so. + +## Expectations + +`Expectations` are a piece of type inference state we maintain for the cases where we need to eagerly infer the types of expressions rather than leave them to the end. They allow us to "ask questions" of the form "hey, we're expecting this term to have this type, is this true?" + Papers: - [Practical Type Inference for Arbitrary-Rank Types, Jones ](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/putting.pdf) - [Local type inference (referenced in PTIfART)] From 4a158b81ab15bb09a3435671a88bdb1796c10e1e Mon Sep 17 00:00:00 2001 From: Fallible <118682743+fallible-algebra@users.noreply.github.com> Date: Wed, 12 Aug 2026 15:42:12 +0100 Subject: [PATCH 10/13] Pushing for review purposes --- src/hir-typeck/eager-inference.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/hir-typeck/eager-inference.md b/src/hir-typeck/eager-inference.md index b4efa30b0e..a550b428dc 100644 --- a/src/hir-typeck/eager-inference.md +++ b/src/hir-typeck/eager-inference.md @@ -2,7 +2,9 @@ There are places during compilation where we need to depend on the current state of inference. This means we need to establish the type of a {term, item, etc, figure out exact wording in review} earlier than we otherwise would. -Eager evaluation of the type of a term (type inference) is required at some specific points to either make later type inference more consistent or (in the case of higher-ranked bounds / Higher Ranked Lifetime bounds) make it even possible. +Depending on the current state of inference means _we pay attention to the set constraints we currently have_ even if we've not finished finding all constraints yet. + +Eager evaluation of the type of a term (type inference) is required at specific points either to make later type inference more consistent or (in the case of higher-ranked bounds / Higher Ranked Lifetime bounds) make it possible at all. Eager type inference is when we do type inference earlier than we otherwise would. To do this, we bring in Expectations as an additional piece of context. @@ -24,18 +26,30 @@ _Closures_ need to have type inference eagerly applied to them because they are let closure = |a, b| if a < b {vec![1, 2, 3]} else {vec![5, 6, 7]}; ``` -If we didn't do eager type inference we would instead have closures whose types were filled with [inference variables](../appendix/glossary.md#inf-var). +If we didn't do eager type inference we would instead have closures whose types were filled with [inference variables](../appendix/glossary.md#inf-var). This would be able to be solved in some situations, but because we do not have Higher-Ranked Inference Variables[^higher-ranked-inference] this would make the higher-ranked bounds for lifetimes that rust can have unusable without more annotation. +Another reason we infer eagerly for closures is that it's a good heuristic: We will need to know the types of the closure if it's used anywhere, so it is best to figure out what it is sooner. + +? TODO: higher-ranked inference, but more. Go over notes. + ### Coercions [Coercions](./coercions.md) can happen in many places. We check to see if a coercion can happen, and if it can we perform the coercion. When we successfully find a coercion, we need to eagerly perform type inference/checking on it as future inference will require or benefit from this information to be known ahead of time. +? TODO: Coercions are found by eager inference, this is the other way round to what is currently written. + +### Trait Solving + +Trait solving happens in a + ### Method calls, Fields, and Indexes. +? This bucket of stuff should be changed. + These are areas which technically take expectations, but in practice use them for diagnostics only. #### Methods From 1e92fe60e512e55f5ceb1bcc263fe09a68dd60cb Mon Sep 17 00:00:00 2001 From: Fallible <118682743+fallible-algebra@users.noreply.github.com> Date: Fri, 28 Aug 2026 17:50:05 +0100 Subject: [PATCH 11/13] Current state of inference structure etc. --- src/hir-typeck/eager-inference.md | 83 ++++++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 7 deletions(-) diff --git a/src/hir-typeck/eager-inference.md b/src/hir-typeck/eager-inference.md index a550b428dc..2b6b56924c 100644 --- a/src/hir-typeck/eager-inference.md +++ b/src/hir-typeck/eager-inference.md @@ -2,12 +2,70 @@ There are places during compilation where we need to depend on the current state of inference. This means we need to establish the type of a {term, item, etc, figure out exact wording in review} earlier than we otherwise would. -Depending on the current state of inference means _we pay attention to the set constraints we currently have_ even if we've not finished finding all constraints yet. +Depending on the Current State of Inference means _we pay attention to the set of constraints we currently have_ even if we've not finished finding all constraints yet. See: ["Current State of Inference"](#current-state-of-inference). Eager evaluation of the type of a term (type inference) is required at specific points either to make later type inference more consistent or (in the case of higher-ranked bounds / Higher Ranked Lifetime bounds) make it possible at all. Eager type inference is when we do type inference earlier than we otherwise would. To do this, we bring in Expectations as an additional piece of context. +## Current State of Inference + +The "Current State of Inference" is a set of ongoing context state that may not yet be "complete." This is a set of information we care about while performing type inference and checking. + +### Inference Variables + +Knowledge about what inference variables are currently resolved to. When we first introduce a variable, we give it an inference variable. + +```rust +let x = ...; +--- +// At initialization. +x: ?a; +?a = not known; +// Maybe later? +?a = Vec; +// And later still +?a = Vec; +``` + +Each time we look up `?a` we are accessing this part of the current state of inference. + +### Expectations + +We _expect_ that a subject will have a given type, but we have not yet assigned this to its inference variables. + +We may have knowledge about what an expression's type _should_ be before we further constrain the inference variable associated with it. Consider the following: + +```rust +// fn(u32) -> bool +fn north(a: u32) -> bool { + a > 2000 +} + +// Has the type ?a, an inference variable. +let x = ...; +// Introduces "x: u32" and "result: bool" as expectations. +let result = north(x); +--- +x: ?a; +result: ?b; +// These have not yet been assigned to ?a ?b respectively. But we do know we +// will need to reconcile these with ?a and ?b in the short term. +expect x: u32; +expect result: bool; +// Then we type check and infer +``` + +These pieces of information have not yet been canonized as "things an inference variable _is_" the same way we might be able to look up what `?x` currently maps to in the current state of inference. + +### Constraints + +This is the set of constraints that need to be solved. We collect these constraints as we move through the codebase being type checked. + +## Places Where Eager Inference Happens + +Eager inference is very common. + ### Closures and Higher-Ranked Variables Top-level functions, such as the following, have their types fully annotated at their definition site: @@ -16,6 +74,9 @@ Top-level functions, such as the following, have their types fully annotated at fn is_even(number: i32) -> bool { number % 2 == 0 } +--- +// We don't have to infer this, it's annotated. +is_even: fn(i32) -> bool ``` This makes inference at points where they're used relatively easy. We know it's a `fn(i32) -> bool`, so when we give it an `i32` we know the expression is a `bool`. @@ -24,15 +85,19 @@ _Closures_ need to have type inference eagerly applied to them because they are ```rust let closure = |a, b| if a < b {vec![1, 2, 3]} else {vec![5, 6, 7]}; +--- +// Doesn't capture any information so it's a bare function pointer +// but we still don't know much about it. +closure: fn(?x, ?y) -> ?z ``` -If we didn't do eager type inference we would instead have closures whose types were filled with [inference variables](../appendix/glossary.md#inf-var). +If we didn't do eager type inference we would instead have closures whose types were filled with [inference variables](../appendix/glossary.md#inf-var) (like the `?x, ?y, ?z` variables above). -This would be able to be solved in some situations, but because we do not have Higher-Ranked Inference Variables[^higher-ranked-inference] this would make the higher-ranked bounds for lifetimes that rust can have unusable without more annotation. +Eagerly inferring the type of `closure` here serves a couple of purposes. Firstly, it's a decent heuristic that if we define a closure we'll use it later and having its type be known will mean a less intense inference solve. Having eagerly inferred the type of a closure lets us establish [Expectations](#expectations) that don't have inference variables in them. -Another reason we infer eagerly for closures is that it's a good heuristic: We will need to know the types of the closure if it's used anywhere, so it is best to figure out what it is sooner. +Secondly, functions can introduce Higher-Ranked Bounds for lifetimes. Inferring the types for a closure that uses higher-ranked bounds **requires** us to do work earlier, as part of the bidirectional () type checking algorithm we use. -? TODO: higher-ranked inference, but more. Go over notes. +This would be able to be solved in some situations, but because we do not have Higher-Ranked Inference Variables[^higher-ranked-inference] this would make the higher-ranked bounds for lifetimes that rust can have unusable without more annotation. ### Coercions @@ -44,7 +109,7 @@ When we successfully find a coercion, we need to eagerly perform type inference/ ### Trait Solving -Trait solving happens in a +Trait solving can be run on a [`TyKind`][tykind] during at any point. Failure is recoverable, so we can repeat this operation, but if at the end of an Item we can't establish if a type implements a trait then we error. ### Method calls, Fields, and Indexes. @@ -79,4 +144,8 @@ Papers: - [Practical Type Inference for Arbitrary-Rank Types, Jones ](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/putting.pdf) - [Local type inference (referenced in PTIfART)] -[^higher-ranked-inference]: https://github.com/rust-lang/types-team/issues/131 \ No newline at end of file +--- + +[tykind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/type.TyKind.html +[^higher-ranked-inference]: https://github.com/rust-lang/types-team/issues/131 +[^practical]: [Practical Type Inference for Arbitrary-Rank Types, Jones et al 2007](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/putting.pdf) \ No newline at end of file From 4b397b0c40d0ce540262141d79aa4c635fcbb77d Mon Sep 17 00:00:00 2001 From: Fallible <118682743+fallible-algebra@users.noreply.github.com> Date: Mon, 31 Aug 2026 13:15:55 +0100 Subject: [PATCH 12/13] Major editing pass --- src/hir-typeck/eager-inference.md | 67 +++++++++++-------------------- 1 file changed, 23 insertions(+), 44 deletions(-) diff --git a/src/hir-typeck/eager-inference.md b/src/hir-typeck/eager-inference.md index 2b6b56924c..665c05baf4 100644 --- a/src/hir-typeck/eager-inference.md +++ b/src/hir-typeck/eager-inference.md @@ -1,6 +1,6 @@ # Eager Type Inference -There are places during compilation where we need to depend on the current state of inference. This means we need to establish the type of a {term, item, etc, figure out exact wording in review} earlier than we otherwise would. +There are places during type checking where we need to depend on the "current state of inference." This means we need to establish the type of a term earlier than we otherwise would, making it _eager_ inference. Depending on the Current State of Inference means _we pay attention to the set of constraints we currently have_ even if we've not finished finding all constraints yet. See: ["Current State of Inference"](#current-state-of-inference). @@ -12,9 +12,11 @@ Eager type inference is when we do type inference earlier than we otherwise woul The "Current State of Inference" is a set of ongoing context state that may not yet be "complete." This is a set of information we care about while performing type inference and checking. +In practical terms, accessing the current state of inference usually means accessing [`InferCtxtInner`][inferctxtinner] + ### Inference Variables -Knowledge about what inference variables are currently resolved to. When we first introduce a variable, we give it an inference variable. +Knowledge about what inference variables are currently resolved to. When we first introduce a variable, we give it an inference variable. This is a number of fields of [`InferCtxtInner`][inferctxtinner], but in the general case it's [`InferCtxtInner::type_variable_storage`][inferctxtinner-tyvars]. ```rust let x = ...; @@ -28,11 +30,11 @@ x: ?a; ?a = Vec; ``` -Each time we look up `?a` we are accessing this part of the current state of inference. +Each time we look up what `?a` currently is we are accessing this part of the Current State of Inference. ### Expectations -We _expect_ that a subject will have a given type, but we have not yet assigned this to its inference variables. +[Expectations][expectation] are an additional piece of typing context that shows we _expect_ that a subject will have a given type, but we have not yet assigned applied this to the inference variables for that subject. We may have knowledge about what an expression's type _should_ be before we further constrain the inference variable associated with it. Consider the following: @@ -47,6 +49,7 @@ let x = ...; // Introduces "x: u32" and "result: bool" as expectations. let result = north(x); --- +// Regular inference variables x: ?a; result: ?b; // These have not yet been assigned to ?a ?b respectively. But we do know we @@ -95,57 +98,33 @@ If we didn't do eager type inference we would instead have closures whose types Eagerly inferring the type of `closure` here serves a couple of purposes. Firstly, it's a decent heuristic that if we define a closure we'll use it later and having its type be known will mean a less intense inference solve. Having eagerly inferred the type of a closure lets us establish [Expectations](#expectations) that don't have inference variables in them. -Secondly, functions can introduce Higher-Ranked Bounds for lifetimes. Inferring the types for a closure that uses higher-ranked bounds **requires** us to do work earlier, as part of the bidirectional () type checking algorithm we use. +Secondly, functions can introduce Higher-Ranked Bounds for lifetimes. Inferring the types for a closure that uses higher-ranked bounds **requires** us to do work earlier, as part of the bidirectional[^jonesetal2007] type checking algorithm we use. This would be able to be solved in some situations, but because we do not have Higher-Ranked Inference Variables[^higher-ranked-inference] this would make the higher-ranked bounds for lifetimes that rust can have unusable without more annotation. -### Coercions - -[Coercions](./coercions.md) can happen in many places. We check to see if a coercion can happen, and if it can we perform the coercion. - -When we successfully find a coercion, we need to eagerly perform type inference/checking on it as future inference will require or benefit from this information to be known ahead of time. - -? TODO: Coercions are found by eager inference, this is the other way round to what is currently written. - ### Trait Solving -Trait solving can be run on a [`TyKind`][tykind] during at any point. Failure is recoverable, so we can repeat this operation, but if at the end of an Item we can't establish if a type implements a trait then we error. - -### Method calls, Fields, and Indexes. - -? This bucket of stuff should be changed. - -These are areas which technically take expectations, but in practice use them for diagnostics only. - -#### Methods - -Maybe Not. Maybe just point to [method lookup](./method-lookup.md). - -? Method calls engage in Coercion and therefore need to engage in Eager Type Inference. - -#### Fields? - -Field access is inherently typed, so when we are doing field access we want to be able to know what a type is as early as possible. - -? There might be something about deref here idk. - -#### Indexing - -? Indexing engages in coercion and therefore needs to engage in eager type inference. - -lcnr said so. +Trait solving can be run on a [`TyKind`][tykind] at any point. Failure is recoverable, so we can repeat this operation, but if at the end of an Item we can't establish if a type implements a trait then we error. +### Coercions -## Expectations +[Coercions](./coercions.md) can happen in many places. We check to see if a coercion can happen, and if it can we perform the coercion. -`Expectations` are a piece of type inference state we maintain for the cases where we need to eagerly infer the types of expressions rather than leave them to the end. They allow us to "ask questions" of the form "hey, we're expecting this term to have this type, is this true?" +TODO: Stub, relationship to eager inference is not well established. -Papers: -- [Practical Type Inference for Arbitrary-Rank Types, Jones ](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/putting.pdf) -- [Local type inference (referenced in PTIfART)] +TODO: Following are stubs, need to determine if these are relevant to bring up. +- **Methods**: + - See: [method lookup](./method-lookup.md) +- **Fields**: + - Field access is inherently typed, so when we are doing field access we want to be able to know what a type is as early as possible. +- **Indexes**: + Indexing engages in coercion and therefore needs to engage in eager type inference. --- +[expectation]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_hir_typeck/expectation/enum.Expectation.html +[inferctxtinner]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_infer/infer/struct.InferCtxtInner.html +[inferctxtinner-tyvars]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_infer/infer/struct.InferCtxtInner.html#structfield.type_variable_storage [tykind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/type.TyKind.html [^higher-ranked-inference]: https://github.com/rust-lang/types-team/issues/131 -[^practical]: [Practical Type Inference for Arbitrary-Rank Types, Jones et al 2007](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/putting.pdf) \ No newline at end of file +[^jonesetal2007]: [Practical Type Inference for Arbitrary-Rank Types, Jones et al 2007](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/putting.pdf) \ No newline at end of file From 01c6ba3b7d23331b8551c3febbe1126a12c0c880 Mon Sep 17 00:00:00 2001 From: Fallible <118682743+fallible-algebra@users.noreply.github.com> Date: Mon, 12 Oct 2026 12:13:16 +0100 Subject: [PATCH 13/13] Small editing pass --- src/SUMMARY.md | 2 +- src/hir-typeck/eager-inference.md | 40 +++++++++++++++---------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 823a8bb15b..15efb60464 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -207,7 +207,7 @@ - [Coherence checking](./coherence.md) - [HIR Type checking](./hir-typeck/summary.md) - [Coercions](./hir-typeck/coercions.md) - - [Eager Inference](./hir-typeck/eager-inference.md) + - [Eager Type Inference](./hir-typeck/eager-inference.md) - [Method lookup](./hir-typeck/method-lookup.md) - [Const generics](./const-generics.md) - [Opaque types](./opaque-types-type-alias-impl-trait.md) diff --git a/src/hir-typeck/eager-inference.md b/src/hir-typeck/eager-inference.md index 665c05baf4..6c291c928a 100644 --- a/src/hir-typeck/eager-inference.md +++ b/src/hir-typeck/eager-inference.md @@ -1,18 +1,16 @@ # Eager Type Inference -There are places during type checking where we need to depend on the "current state of inference." This means we need to establish the type of a term earlier than we otherwise would, making it _eager_ inference. +There are places during type checking where we need to depend on a "Current State of Inference" to establish the type of a term earlier than we otherwise would, this where we _Eagerly_ infer a type of a subject. -Depending on the Current State of Inference means _we pay attention to the set of constraints we currently have_ even if we've not finished finding all constraints yet. See: ["Current State of Inference"](#current-state-of-inference). +Depending on the Current State of Inference means we pay attention to the set of constraints we currently have, even if we've not finished finding all constraints yet. See: ["Current State of Inference"](#current-state-of-inference) and [`InferCtxtInner`][inferctxtinner]. -Eager evaluation of the type of a term (type inference) is required at specific points either to make later type inference more consistent or (in the case of higher-ranked bounds / Higher Ranked Lifetime bounds) make it possible at all. - -Eager type inference is when we do type inference earlier than we otherwise would. To do this, we bring in Expectations as an additional piece of context. +Eager type inference is required at specific points either to make later type inference more consistent or — in the case of higher-ranked bounds / Higher Ranked Lifetime bounds — make it possible at all. ## Current State of Inference -The "Current State of Inference" is a set of ongoing context state that may not yet be "complete." This is a set of information we care about while performing type inference and checking. +The Current State of Inference is a set of ongoing context state that may not yet be "complete." This is a set of information we care about while performing type inference and checking. -In practical terms, accessing the current state of inference usually means accessing [`InferCtxtInner`][inferctxtinner] +In practical terms, accessing the current state of inference usually means accessing [`InferCtxtInner`][inferctxtinner]. ### Inference Variables @@ -34,9 +32,9 @@ Each time we look up what `?a` currently is we are accessing this part of the Cu ### Expectations -[Expectations][expectation] are an additional piece of typing context that shows we _expect_ that a subject will have a given type, but we have not yet assigned applied this to the inference variables for that subject. +[Expectations][expectation] are an additional piece of typing context that represents an _expectation_ that a subject will have a given type, but without having committed this to the inference variables for that subject. -We may have knowledge about what an expression's type _should_ be before we further constrain the inference variable associated with it. Consider the following: +We may have knowledge about what a subject's type _should_ be before we further constrain the inference variable associated with it. Consider the following: ```rust // fn(u32) -> bool @@ -56,22 +54,21 @@ result: ?b; // will need to reconcile these with ?a and ?b in the short term. expect x: u32; expect result: bool; -// Then we type check and infer ``` These pieces of information have not yet been canonized as "things an inference variable _is_" the same way we might be able to look up what `?x` currently maps to in the current state of inference. -### Constraints +### Obligations -This is the set of constraints that need to be solved. We collect these constraints as we move through the codebase being type checked. +Sometimes we look at the set of obligations/goals/predicates the trait solver is required to prove. The relevant data structure here is [ObligationCtxt][obligation]. See: [Trait Solving](#trait-solving). ## Places Where Eager Inference Happens Eager inference is very common. -### Closures and Higher-Ranked Variables +### Closures -Top-level functions, such as the following, have their types fully annotated at their definition site: +Top-level functions have their types fully annotated at their definition site: ```rust fn is_even(number: i32) -> bool { @@ -94,12 +91,14 @@ let closure = |a, b| if a < b {vec![1, 2, 3]} else {vec![5, 6, 7]}; closure: fn(?x, ?y) -> ?z ``` -If we didn't do eager type inference we would instead have closures whose types were filled with [inference variables](../appendix/glossary.md#inf-var) (like the `?x, ?y, ?z` variables above). - -Eagerly inferring the type of `closure` here serves a couple of purposes. Firstly, it's a decent heuristic that if we define a closure we'll use it later and having its type be known will mean a less intense inference solve. Having eagerly inferred the type of a closure lets us establish [Expectations](#expectations) that don't have inference variables in them. +Eagerly inferring the type of a closure serves a couple of purposes. Firstly, it's a decent heuristic that if we define a closure we'll use it later and having its type be known will mean a less intense inference solve. Having eagerly inferred the type of a closure lets us establish [Expectations](#expectations) that don't have inference variables in them. Secondly, functions can introduce Higher-Ranked Bounds for lifetimes. Inferring the types for a closure that uses higher-ranked bounds **requires** us to do work earlier, as part of the bidirectional[^jonesetal2007] type checking algorithm we use. +If we didn't do eager type inference we would instead have closures whose types were filled with [inference variables](../appendix/glossary.md#inf-var) (like the `?x, ?y, ?z` variables above). + +### Higher-Ranked Variables + This would be able to be solved in some situations, but because we do not have Higher-Ranked Inference Variables[^higher-ranked-inference] this would make the higher-ranked bounds for lifetimes that rust can have unusable without more annotation. ### Trait Solving @@ -122,9 +121,10 @@ TODO: Following are stubs, need to determine if these are relevant to bring up. Indexing engages in coercion and therefore needs to engage in eager type inference. --- -[expectation]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_hir_typeck/expectation/enum.Expectation.html -[inferctxtinner]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_infer/infer/struct.InferCtxtInner.html -[inferctxtinner-tyvars]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_infer/infer/struct.InferCtxtInner.html#structfield.type_variable_storage +[obligation]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/struct.ObligationCtxt.html +[expectation]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/expectation/enum.Expectation.html +[inferctxtinner]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/struct.InferCtxtInner.html +[inferctxtinner-tyvars]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/struct.InferCtxtInner.html#structfield.type_variable_storage [tykind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/type.TyKind.html [^higher-ranked-inference]: https://github.com/rust-lang/types-team/issues/131 [^jonesetal2007]: [Practical Type Inference for Arbitrary-Rank Types, Jones et al 2007](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/putting.pdf) \ No newline at end of file