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

Fixes: Rust supports overriding traits #5948

Open
wants to merge 55 commits into
base: master
Choose a base branch
from

Conversation

MikaelMayer
Copy link
Member

@MikaelMayer MikaelMayer commented Nov 30, 2024

Description

The PR #5940 defined a preliminary support for value traits.
This PR fixes everything that encompasses value traits and newtypes. Here is the list of fixes.

  • Fix to support functions declared in a trait and implemented in another trait
  • Fix to support for equality in type parameters and conditional equality support for datatypes.
  • Fix to support constants in datatypes
  • Fix to support constants in traits
  • Fix to support back/forth conversions between &self and the underlying type
  • Fix to ensure we don't support coercions where it's not possible (that caused compilation errors for Rust and it's behind the discovery of soundness issue Soundness issue: Map's range not inferred to require equality for datatype to support equality #5972)
  • Fix support of equality for newtypes (that caused compilation errors for Rust and it's behind the discovery of soundness issue General newtypes not supporting equality in a sound way #5978)
  • Fix to support type bounds in type parameters
  • Fix to support newtypes on booleans
  • Fix to support correct borrowing of type arguments in methods when the method instantiate a trait method which always borrows the arguments with the type parameter
  • Fix to support static constants in datatypes, including type parameters
  • Fix to support type parameters in static functions
  • Fix to ensure type parameter equality is correctly generated
  • Fix to support downcast from one value trait to another value trait of the same hierarchy (needed to change the resolver to record all such traits)
  • Fix to support the enclosing module name in externs

Also some enhancements

  • Unification of extern semantics: Remove the error message about {:extern} on types requiring a hint when it can simply take the Dafny name, like for modules and classes.
  • DafnyCodeGenerator.GenType now caches its result to avoid exponential computations

I also updated the documentation regarding the use of externs

How has this been tested?

  • All the tests of comp/rust/traits-datatypes.dfy and comp/rust/small/* were not passing before this PR, and now they are.
  • The way to downcast traits from one to the other is tested in the test/mod.rs of the runtime.

By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.

MikaelMayer and others added 30 commits November 21, 2024 13:42
Fixed support for hashing and equality for traits overrides
Support for static functions and methods
# Conflicts:
#	Source/DafnyCore/Backends/Dafny/AST.dfy
#	Source/DafnyCore/Backends/Dafny/DafnyCodeGenerator.cs
#	Source/DafnyCore/Backends/Rust/Dafny-compiler-rust-definitions.dfy
#	Source/DafnyCore/Backends/Rust/Dafny-compiler-rust-rast.dfy
#	Source/DafnyCore/Backends/Rust/Dafny-compiler-rust.dfy
#	Source/DafnyCore/Backends/SinglePassCodeGenerator/SinglePassCodeGenerator.cs
#	Source/DafnyCore/GeneratedFromDafny/DAST.cs
#	Source/DafnyCore/GeneratedFromDafny/DCOMP.cs
#	Source/DafnyCore/GeneratedFromDafny/Defs.cs
#	Source/DafnyCore/GeneratedFromDafny/RAST.cs
#	Source/DafnyRuntime/DafnyRuntimeRust/src/lib.rs
#	Source/DafnyRuntime/DafnyRuntimeRust/src/tests/mod.rs
#	Source/IntegrationTests/TestFiles/LitTests/LitTest/comp/rust/traits-datatypes.dfy
#	Source/IntegrationTests/TestFiles/LitTests/LitTest/comp/rust/traits-datatypes.dfy.expect
# Conflicts:
#	Source/DafnyCore.Test/GeneratedFromDafny/DefsCoverage.cs
#	Source/DafnyCore/Backends/Rust/Dafny-compiler-rust-rast.dfy
#	Source/DafnyCore/Backends/Rust/Dafny-compiler-rust.dfy
#	Source/DafnyCore/GeneratedFromDafny/DCOMP.cs
#	Source/DafnyCore/GeneratedFromDafny/Defs.cs
#	Source/DafnyCore/GeneratedFromDafny/RAST.cs
# Conflicts:
#	Source/DafnyCore/Backends/Dafny/DafnyCodeGenerator.cs
#	Source/DafnyCore/Backends/Rust/Dafny-compiler-rust.dfy
#	Source/DafnyCore/Backends/SinglePassCodeGenerator/SinglePassCodeGenerator.cs
#	Source/DafnyCore/GeneratedFromDafny/DCOMP.cs
#	Source/DafnyCore/Resolver/ModuleResolver.cs
#	Source/DafnyCore/Resolver/TypeCharacteristicChecker.cs
Caching of compilation name.
Don't copy target file if same path
# Conflicts:
#	Source/DafnyCore/Backends/Dafny/DafnyCodeGenerator.cs
#	Source/DafnyCore/Backends/SinglePassCodeGenerator/SinglePassCodeGenerator.cs
@MikaelMayer MikaelMayer changed the title Feat: Rust supports overriding traits Fixes: Rust supports overriding traits Jan 3, 2025
@ssomayyajula
Copy link
Contributor

Some high level comments:

I'm mainly just concerned about the soundness and implementation of trait downcasting. It looks like you do this in phases:

  • At resolution time, determine the parents of a trait declaration
  • In Dafny code generator, compute for which parents the subtype relation works both ways

I don't understand why the latter can't be done earlier - is there not a subtype check method in the type system refresh?

@MikaelMayer
Copy link
Member Author

Thank you very much for reading the PR and trying to understand the underlying logic. That's already a lot of work.

  • In Dafny code generator, compute for which parents the subtype relation works both ways

I don't understand why the latter can't be done earlier - is there not a subtype check method in the type system refresh?

You're right that there is an issue here. The problem is that in other backends, it's fine to do something like this:

trait X {}
trait Y<T> extends X {}
var x: X;
if x is Y<int> {
}

This is because other back-ends keep run-time information of the type and types are behind single references.
However, in Rust, unless we do a deeper embedding, traits are only represented as the fat part of a pointer that points the vtable for that trait.
Therefore, it is not possible to check at run-time if x is Y<int> like that. More precisely, the only thing we know from a x: X is a pointer to the data and all the methods of X, plus possibly other internal methods.
The only way to know what the type at run-time is is through the trait Any, that Dafny traits extend. However, there could be two traits implementations for concrete types in two separate modules, so traits are not inherently attached to concrete types like they are in Dafny.

The only sensible way to do trait downcast is to add special methods to the trait X {} so that we can test if it's a Y {} for some T. However, again, because of monomorphization, Rust prevents type parameters on functions that are in a trait with a function. Hence, the only way to do a downcast test and instantiation is if the type parameters of the subtrait can be obtained from the type parameters of X. This is what I am computing in CanDowncast().

This means indeed that users who try to downcast X to Y<int> will get a compiler error, but I do not consider this as a soundness issue, more a kind of unsupported issue. Still, let me document that if that would alleviate your concern.

One problem with doing it earlier is that the subtype check depends on whether the instantiated type parameters support

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.

2 participants