Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add record constructor to subtyping #2007

Merged
merged 24 commits into from
Sep 6, 2024

Conversation

Eckaos
Copy link
Contributor

@Eckaos Eckaos commented Jul 25, 2024

No description provided.

@Eckaos
Copy link
Contributor Author

Eckaos commented Jul 25, 2024

@yannham What I did here is pretty much unify for record rows. It miss TailVar, Constant, etc...
Do you know how to reconstruct a GenericUnifType from GenericUnifRecordRows. For the time being, I reconstruct it from the data I have. I may have reconstruct the type with the wrong levels.

@@ -1599,7 +1599,7 @@ impl Unify for UnifRecordRows {
}

#[derive(Clone, Copy, Debug)]
enum RemoveRowError {
pub enum RemoveRowError {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to make this symbol public? Public means that it's accessible to other crates. I think most of those symbols are specific to nickel-lang-core, or even to typecheck.

Copy link
Contributor Author

@Eckaos Eckaos Jul 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the mod.rs file, rust does not know what is RemoveRowError. It says it is not accessible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In which file? typecheck/mod.rs?

Copy link
Member

@yannham yannham Jul 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, because it's a parent of unif. Then you can use pub(super) instead of pub, which is more restricted and doesn't make this a public symbol of nickel-lang-core. The latter has more consequences, because it means it impacts e.g. semantic versioning (if we change the name of this trait, it's a breaking change). While it stays internal, we can do whatever we want 🙂

@yannham
Copy link
Member

yannham commented Jul 25, 2024

Do you know how to reconstruct a GenericUnifType from GenericUnifRecordRows.

The best, if those record rows are coming from destructuring a unif type to begin with, is to keep the original var_levels around so that you can reconstruct the original type.

If this type is modified, or generated, or coming from some type inference transformation, you can just use UnifType::concrete (or GenericUnifType::concrete), the method with a lowercase c (and not the constructor Concrete), which takes care of updating and filling the levels accordingly.

@Eckaos
Copy link
Contributor Author

Eckaos commented Jul 26, 2024

The commit 58af17d break stuff about record.
Left to do :

  • Comments
  • Fix the problem about records
  • Fix warning

@Eckaos
Copy link
Contributor Author

Eckaos commented Jul 31, 2024

The problem about records wasn't real, it was just a simple problem about reporting error correctly.
Another problem arose. It is about lsp/nls/tests/inputs/hover_field_typed_block_regression_1574.ncl, it give a new snapshot and I don't know why. @yannham

If my fix does work, the work left to do is to :

  • Add documentation
  • Update comments
  • Add tests
  • Remove subsumption function in typecheck/mod.rs

@Eckaos Eckaos requested a review from yannham July 31, 2024 16:17
Copy link
Member

@yannham yannham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a first review, the approach looks good. We can think about how to de-depulicate the code between subsume and unify later on. Maybe the code comments still need to be put at the right place and updated a bit, they've been mostly copy pasted or so it seems.

core/src/typecheck/subtyping.rs Outdated Show resolved Hide resolved
core/src/typecheck/subtyping.rs Outdated Show resolved Hide resolved
@Eckaos
Copy link
Contributor Author

Eckaos commented Aug 5, 2024

To de-duplicate the code between is_subsumed_by and unify, we can just call unify where we need in is_subsumed_by. The cost, I see, will be the cost of pattern matching + function call (depends on what optimization the rust compiler will do). Or we could abstract cases that are the same in is_subsumed_by and unify into another function. The cost, I see here, will be pattern matching + function call in the two functions (depends on what optimization the rust compiler will do). @yannham

@yannham
Copy link
Member

yannham commented Aug 5, 2024

To de-duplicate the code between is_subsumed_by and unify, we can just call unify where we need in is_subsumed_by.

I'm not sure to understand what you mean here. For example, when subsuming record types, we do the same general work but we still recursively calls is_subsumed_by on each component, so we can't just replace the whole case by a call to unify. Indeed, I think we need to have a core function that is generic how it calls itself recursively. It's certainly doable, but we can do that in a second step.

P.S: it could be good to have tests for more complicated record types: types with tails, record types with subsumption acting on several components, etc.

@Eckaos
Copy link
Contributor Author

Eckaos commented Aug 6, 2024

I was talking about cases where we do exactly the same work (with Constant and UnifVar etc...). In those cases, we can call unify from is_subsumed_by.
The other alternative was to factor out a function which does only cases where it is the same result for unify and is_subsumed_by and then call it from unify and is_subsumed_by when needed.

@Eckaos Eckaos marked this pull request as ready for review August 6, 2024 13:19
@yannham yannham force-pushed the subtyping/record_constructor branch from e55fce5 to b09a6d6 Compare August 6, 2024 13:26
Copy link
Contributor

github-actions bot commented Aug 6, 2024

🐰Bencher

ReportFri, September 6, 2024 at 15:27:59 UTC
Projectnickel
Branch2007/merge
Testbedubuntu-latest

⚠️ WARNING: The following Measure does not have a Threshold. Without a Threshold, no Alerts will ever be generated!

  • Latency (latency)

Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the --ci-only-thresholds CLI flag.

Click to view all benchmark results
BenchmarkLatencyLatency Results
nanoseconds (ns)
fibonacci 10➖ (view plot)504,530.00
pidigits 100➖ (view plot)3,192,200.00
product 30➖ (view plot)796,650.00
scalar 10➖ (view plot)1,496,400.00
sum 30➖ (view plot)793,290.00

Bencher - Continuous Benchmarking
View Public Perf Page
Docs | Repo | Chat | Help

@Eckaos
Copy link
Contributor Author

Eckaos commented Aug 7, 2024

I don't understand why cargo and the continuous integration pipeline give me an error about the lsp. @yannham

@Eckaos Eckaos requested a review from yannham August 7, 2024 14:04
@jneem
Copy link
Member

jneem commented Aug 7, 2024

It's because the lsp snapshot tests have changed -- you've changed docs in the stdlib and so the hover text in the lsp is different. Running cargo test -p nickel-lang-lsp and cargo insta review locally should let you update the snapshots.

@yannham
Copy link
Member

yannham commented Aug 7, 2024

It's because the lsp snapshot tests have changed -- you've changed docs in the stdlib and so the hover text in the lsp is different. Running cargo test -p nickel-lang-lsp and cargo insta review locally should let you update the snapshots.

I don't see any change to the stdlib. Also, the diff of the snapshot is dubious - we now get an additional monomorphic type with _a coming first in the LSP output. So it seems this PR has changed something related to either how TypecheckVisitor is called, or to the apparent type of values, etc.

Copy link
Member

@yannham yannham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good, but the LSP regression is suspicious.

core/src/typecheck/subtyping.rs Outdated Show resolved Hide resolved
Comment on lines 1 to 2
//! Type subsumption
/// Type subsumption is generally used when we change from inference mode to checking mode.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//! Type subsumption
/// Type subsumption is generally used when we change from inference mode to checking mode.
//! Type subsumption
//!
/// Type subsumption is generally used when we change from inference mode to checking mode.

@@ -0,0 +1,244 @@
//! Type subsumption
/// Type subsumption is generally used when we change from inference mode to checking mode.
/// Currently, there is one subtyping relations :
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, and below, you should continue to use //! instead of ///, because it's part of the top-level module documentation.

doc/manual/typing.md Outdated Show resolved Hide resolved
Eckaos and others added 5 commits August 9, 2024 14:55
Co-authored-by: Yann Hamdaoui <yann.hamdaoui@gmail.com>
Co-authored-by: Yann Hamdaoui <yann.hamdaoui@gmail.com>
@yannham yannham self-assigned this Sep 5, 2024
@yannham
Copy link
Member

yannham commented Sep 5, 2024

I'm taking over this PR for the foreseeable future, unless @Eckaos thinks he has the time to bring it down the finish line.

This commit performs minor cosmetic improvements in the code handling
subtyping, mostly around comments and function interfaces.
The support of subtyping for record types makes some types to be
instantiated earlier than before. Given the way terms are visited, when
writing something like `let f = std.array.map in ..`, `std.array.map`
used to be inferred to be of a polymorphic type `forall a b. ...` and
then only was instantiated (because record access is inferred, while the
bound expression is checked). With the new implementation, it is
instantiated as part of the subsumption rule, meaning that it'll appear
differently on the LSP.

All in all, both version of the data shown by the LSP (before this
change and after) are meaningful, in some sense. The polymorphic type is
still shown when it's part of the metadata anyway, in addition to the
inferred monomorphic type. This also doesn't change which expressions
are accepted or not. It's more of an artifact of when we visit terms,
before or after instantiation and how those visits are intertwined with
`check` and `infer`. It's not easy to revert to the previous state of
affairs, and it's in fact not necessarily a good thing either: in the
example above, the LSP would also show a polymorphic type for `f` - the
one of `map` - while in fact `f` has a monomorphic type, so you couldn't
use it in different contexts (say on an `Array Number` and later on a
`Array String`). The current version is showing the real monomorphic
nature of `f`.
@yannham
Copy link
Member

yannham commented Sep 6, 2024

After some thorough investigation, I think I understand what's happening and I convinced myself that it makes sense (although in the test case it adds a bit of noise, it's also more correct in some cases - see the commit message). It an artifact of when we visit the term compared to when the types are instantiated, more or less.

@yannham yannham added this pull request to the merge queue Sep 6, 2024
Merged via the queue into tweag:master with commit 273ae0f Sep 6, 2024
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants