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

Implement lazy sequence concatenation in remaining backends #2390

Open
robin-aws opened this issue Jul 11, 2022 · 6 comments
Open

Implement lazy sequence concatenation in remaining backends #2390

robin-aws opened this issue Jul 11, 2022 · 6 comments
Assignees
Labels
kind: enhancement Enhancements, feature requests, etc. These are NOT bugs, but ways we can improve Dafny lang: c++ Dafny's C++ transpiler and its runtime lang: golang Dafny's transpiler to Go and its runtime lang: js Dafny's JavaScript transpiler and its runtime lang: python Dafny's Python transpiler and its runtime part: runtime Happens in Dafny's runtime (Add a `lang` tag if relevant)

Comments

@robin-aws
Copy link
Member

This is currently only implemented for C# and Java, but is a pretty critical optimization.

I believe it is feasible to implement this logic once in Dafny code instead of N times in each target language.

@robin-aws robin-aws added kind: enhancement Enhancements, feature requests, etc. These are NOT bugs, but ways we can improve Dafny lang: golang Dafny's transpiler to Go and its runtime lang: js Dafny's JavaScript transpiler and its runtime part: runtime Happens in Dafny's runtime (Add a `lang` tag if relevant) lang: c++ Dafny's C++ transpiler and its runtime lang: python Dafny's Python transpiler and its runtime labels Jul 11, 2022
@robin-aws robin-aws self-assigned this Jul 11, 2022
@robin-aws
Copy link
Member Author

Implementation strategy is as described in #1775

@robin-aws
Copy link
Member Author

Here are more details on the implementation strategy:

Sequences are currently implemented in each runtime via one or more target language classes or similar datatypes, such as ISequence, Sequence, ArraySequence, ConcatSequence etc. in C#. I have a similar common implementation in Dafny code, which will be compiled to each target language as externs with similar names. Then it is only a matter of tweaking code generation for each language to target the Dafny-generated types instead.

The remaining challenges that must be addressed are:

  1. Circular dependency: the Dafny Sequence<T> code requires a fully-built dafny tool, but the core DafnyPipeline project currently depends on the DafnyRuntime project. The solution will likely be to build any Dafny runtime code with a previously-released version of Dafny, and to check in the generated code (with a CI mechanism to regenerate it and verify it matches).
  2. Monomorphization for numeric types: the existing hand-rolled sequence implementations all at least provide versions of methods like Select and Count that accept and/or produce unbounded integer types, such as BigIntegers in C#. Several also provide multiple overloads or alternate versions that use more primitive types such as int or long. When compiling Dafny source code that uses newtype declarations that align with native types, these overloads help avoid the overhead of casting to and from such BigInteger types. The solution here will be for each compiler to track the set of native numeric types to use for indexing, and to generate these overloads for each such type.
  3. Covariance of trait type parameters: the seq<T> value type is implicitly covariant in its single type parameter: S <: T implies seq<S> <: seq<T>. Therefore the equivalent trait must be declared as Sequence<+T>, but variance symbols on traits are currently rejected by the resolver. This support will be at least partially added in order to support the Sequence<+T> type.

The features used to solve 2. and 3. will likely be enabled through CLI options marked as experimental, to avoid too much scope creep.

@keyboardDrummer
Copy link
Member

Circular dependency

What's the long term setup you envision for this?

Would it be something like:

DafnyPipeline:, has the ability for runtimes to be injected, but does not reference DafnyRuntime
DafnyRuntime: uses DafnyPipeline to compile .dfy files, without injecting any runtimes into DafnyPipeline
DafnyDriver: references DafnyRuntime and uses it to inject runtimes into DafnyPipeline.

@robin-aws
Copy link
Member Author

Yup, something like that, once we break the hard .csproj-level dependency of DafnyPipeline on DafnyRuntime.

We probably want to make the driver fetch the required runtime from nuget/maven/npm/etc rather than packaging them all inside Dafny itself, but have DafnyRuntime's build pass /useRuntimeLib to disable that behavior.

@cpitclaudel
Copy link
Member

the core DafnyPipeline project currently depends on the DafnyRuntime project.

Isn't that dependency spurious, at the moment? Things build fine for me if I just remove it.

@robin-aws
Copy link
Member Author

Semi-spurious, yes :) It's a packaging dependency rather than a true build dependency, because ultimately DafnyDriver needs to embed the source files so that it can emit them unless /useRuntimeLib is specified.

As I'm implementing I'm realizing it makes sense to clean up this dependency as a separate initial change first. We need to also break the assumption in some compilers that the runtime is a single source file like DafnyRuntime.cs or DafnyRuntime.js anyway, if I'm going to start generating some of that source code from Dafny target code spilling.

robin-aws added a commit that referenced this issue Sep 9, 2022
Factors out two new projects that don't depend on the `DafnyRuntime`
project or `DafnyRuntime.*` runtime artifacts, and removes the DLL-level
dependencies on `DafnyRuntime.dll`. It also has the happy side-effect of
making directory, csproj, and assembly names consistent.

The four main changes are:

* Rename `Dafny` folder/`DafnyPipeline` project to `DafnyCore`, remove
reference to `DafnyRuntime` and `DafnyRuntime.*` embedded resources
* Create new `DafnyPipeline` folder/project, referencing `DafnyCore` and
adding `DafnyRuntime.*` embedded resources
* Note `DafnyPipeline.dll` does NOT depend on `DafnyRuntime.dll`,
however! It has a project-level dependency to ensure `DafnyRuntime` is
built first, but only embeds the runtime artifacts.
* Relabel `DafnyDriver` as `DafnyDriver.dll` instead of `Dafny.dll`,
remove reference to `DafnyRuntime`
* I'll be able to use this entry point to build Dafny source code into
the runtimes when implementing #2390
* Create new `Dafny` folder/project, referencing `DafnyDriver` and
`DafnyPipeline` (and hence pulling in embedded runtimes)

Note that we are still only publishing `Dafny.dll` and
`DafnyPipeline.dll` as Nuget packages, and they still transitively
contain the same content. We could publish `DafnyDriver` and/or
`DafnyCore` in the future as well.

Removing `DafnyRuntime` as a DLL dependency from all of these projects
initially caused the `Test/dafny0/DafnyLibClient.dfy` test to break.
This is because the driver was attempting to `Assembly.Load` each DLL
passed to it, in order to read any `DafnySourceAttribute` value out of
it if present. This has never been a good idea, since an arbitrary DLL
will have dependencies not satisfied by the invoking context, and not
finding `DafnyRuntime.dll` was just one potential error this could
cause. I had previously used
[Cecil](https://www.mono-project.com/docs/tools+libraries/libraries/Mono.Cecil/)
to read DLL files without loading them, but this was lost when we moved
to .NET Core v2 since that library was not supported. This time I've
used the built-in `System.Reflection.Metadata.MetadataReader` and
related types to decode enough of the DLL to read this attribute.
robin-aws added a commit that referenced this issue Feb 11, 2023
…untime (#3167)

Addresses #2390 for Go, via the strategy described in #1775.

~The only outstanding TODO I'm concerned with is the special-casing in
the Go compiler needed to allow `Sequence<T>` to specify a custom
equality predicate. I'm open to suggestions on how to improve that: at a
minimum it should be checking the full type names, so that we can safely
use the fact that the top-level `dafny` module is reserved, but I'm
hoping there's a cleaner solution.~
Adds an internal `--bootstrapping` option only for use when compiling
Dafny source used in the Dafny implementation itself.

Also adds a `--platform` option to `package.py`: while debugging earlier
attempts to change the build process, I realized that packaging for Mac
OS was often picking the wrong platform.

<small>By submitting this pull request, I confirm that my contribution
is made under the terms of the [MIT
license](https://github.com/dafny-lang/dafny/blob/master/LICENSE.txt).</small>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: enhancement Enhancements, feature requests, etc. These are NOT bugs, but ways we can improve Dafny lang: c++ Dafny's C++ transpiler and its runtime lang: golang Dafny's transpiler to Go and its runtime lang: js Dafny's JavaScript transpiler and its runtime lang: python Dafny's Python transpiler and its runtime part: runtime Happens in Dafny's runtime (Add a `lang` tag if relevant)
Projects
None yet
Development

No branches or pull requests

3 participants