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

feat: add type descriptors for data constructors in Java, when necessary #301

Merged
merged 53 commits into from
Nov 27, 2023

Conversation

robin-aws
Copy link
Contributor

@robin-aws robin-aws commented Oct 20, 2023

Description of changes:
As part of the progress towards supporting traits on non-reference types, the Java backend in Dafny 4.2 changed datatype constructors to require type descriptors for any generic type parameters: dafny-lang/dafny#4240.

This change fixes a big chunk of the failing nightly build failures by augmenting the Java code generation to conditionally provide these type descriptors when creating Result and Option values. In most cases this is handled by the new nameresolver.Dafny.createSuccess/Failure/Some/None helper methods, but in some cases some new Dafny helper methods are added to StandardLibrary or emitted as well, so that TestModels can provide the same Java code for any Dafny version.

It also adds a parameter for the target Dafny version so it knows when to emit them, exposed both as a --dafny-version option for the CLI and a dafnyVersion configuration value on smithy-build.json file. This defaults to 4.1.0, but the latter knob is actually required when using the latest "edition" of the Smithy plugin (now 2023.10 instead of 2023), as this is exactly the kind of otherwise-breaking change editions are designed to support!

Finally, it refactors the CI to test all Dafny-relevant things on multiple versions of Dafny. It follows the same workflow template as https://github.com/aws/aws-cryptographic-material-providers-library-java/tree/main/.github/workflows (and kudos to @texastony!). This exposed one verification regression on 4.3 that I'm looking into, and may factor out of this PR if it's not quick to fix. Note the nightly build depends on a pending patch to setup-dafny-action to provide the actual version when using nightly-latest: dafny-lang/setup-dafny-action#19

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@robin-aws robin-aws changed the title feat: add type descriptors for java (WIP) feat: add type descriptors for data constructors in Java (WIP) Oct 23, 2023
@robin-aws robin-aws force-pushed the robin-aws/add-type-descriptors-for-java branch from a116040 to 0a24bc8 Compare October 29, 2023 16:02
@robin-aws robin-aws marked this pull request as ready for review October 31, 2023 19:30
@robin-aws robin-aws marked this pull request as ready for review November 3, 2023 00:03

class WrappersInterop {

static function method CreateStringSome(s: string): Option<string> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Generics? CreateSome<T>(t: T): Option<T>?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately no, but I beefed up the module comment to explain why better.

"ensures res.Success? ==> ",
"&& fresh(res.value)",
"&& fresh(res.value.%s)".formatted(nameResolver.mutableStateFunctionName()),
"&& fresh(res.value.%s)".formatted(nameResolver.callHistoryFieldName()),
"&& res.value.%s()".formatted(nameResolver.validStateInvariantName())
).lineSeparated();

final TokenTree createSuccessOfClient = TokenTree
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a little confused about where this is getting emitted.
On the one hand, I see additions in the "StandardLibrary" but I also see this getting written here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Similarly added more comments to explain better.

@texastony texastony dismissed their stale review November 27, 2023 20:46

Potentially radically different from when I blocked.

Copy link
Contributor

@texastony texastony left a comment

Choose a reason for hiding this comment

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

Java docs on new public methods is the only blocking comment.

Comment on lines 12 to 13
4.1.0,
4.3.0
Copy link
Contributor

Choose a reason for hiding this comment

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

Note: I am still learning how to best utilize GHA/Ws,
and it seems that GitHub is evolving them as well.

And our test models need to be refactored to an integration test project...

Comment on lines 23 to 26
dafny-version: [
4.1.0,
4.3.0
]
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Is there no way to declare this once and reference it elsewhere in this file?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Definitely possible but not nearly as simple as you'd think :) I made the change as I already had to do something similar for Dafny (where we needed a lot more dynamic calculation)

Comment on lines 110 to 167
public CodeBlock createNone(CodeBlock typeDescriptor) {
if (datatypeConstructorsNeedTypeDescriptors()) {
return CodeBlock.of(
"$T.create_None($L)",
ClassName.get("Wrappers_Compile", "Option"),
typeDescriptor);
} else {
return CodeBlock.of(
"$T.create_None()",
ClassName.get("Wrappers_Compile", "Option"));
}
}

public CodeBlock createSome(CodeBlock typeDescriptor, CodeBlock value) {
if (datatypeConstructorsNeedTypeDescriptors()) {
return CodeBlock.of(
"$T.create_Some($L, $L)",
Constants.DAFNY_OPTION_CLASS_NAME,
typeDescriptor,
value);
} else {
return CodeBlock.of(
"$T.create_Some($L)",
Constants.DAFNY_OPTION_CLASS_NAME,
value);
}
}

public CodeBlock createSuccess(CodeBlock valueTypeDescriptor, CodeBlock value) {
if (datatypeConstructorsNeedTypeDescriptors()) {
return CodeBlock.of(
"$T.create_Success($L, Error._typeDescriptor(), $L)",
Constants.DAFNY_RESULT_CLASS_NAME,
valueTypeDescriptor,
value);
} else {
return CodeBlock.of(
"$T.create_Success($L)",
Constants.DAFNY_RESULT_CLASS_NAME,
value);
}
}

public CodeBlock createFailure(CodeBlock typeDescriptor, CodeBlock error) {
if (datatypeConstructorsNeedTypeDescriptors()) {
return CodeBlock.of(
"$T.create_Failure($L, Error._typeDescriptor(), $L)",
Constants.DAFNY_RESULT_CLASS_NAME,
typeDescriptor,
error);
} else {
return CodeBlock.of(
"$T.create_Failure($L)",
Constants.DAFNY_RESULT_CLASS_NAME,
error);
}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Blocking: Add Java docs.
Smithy-Dafny is a wildly confusing project with many hands contributing functionality.
We MUST describe what our methods do.

I checked, its true, all of these method names are unique.
But they are barely unique.

We need to java docs so future us know when to invoke this these methods.

@robin-aws robin-aws merged commit 8106b08 into main-1.x Nov 27, 2023
122 checks passed
@robin-aws robin-aws deleted the robin-aws/add-type-descriptors-for-java branch November 27, 2023 23:17
robin-aws added a commit that referenced this pull request Feb 27, 2024
This is necessary for Dafny 4.3 and higher, as Dafny no longer adds this method on reference types. The explicit construction of the right kind of TypeDescriptor (which works both before and after 4.3) was already implemented for other shapes in typeDescriptor(ShapeId), so I just tweaked the case identification, and scoped the default case more explicitly to shapes we know it works for, and now raise an exception for unrecognized shape types.

Unfortunately not covered by any TestModels (which is why #301 didn't fix this as well), but tested on MPL: aws/aws-cryptographic-material-providers-library#195
robin-aws added a commit to aws/aws-cryptographic-material-providers-library that referenced this pull request Mar 1, 2024
#195)

Applies several fixes/improvements in order to work with newer Dafny versions.

* Adds `smithy-dafny` as a submodule so that we can lock down the exact commit used to generate code, and use the tool in CI.
* Updates the shared makefile with similar improvements to smithy-dafny's (hook up `--library-root` and `--patch-files-dir`, generate dependencies first)
* Regenerates the checked-in code using a newer `smithy-dafny` to abstract away from the changes in Java TypeDescriptors when constructing datatypes in Dafny 4.3. This includes adding some helper methods to Dafny code for the benefit of Java external code, in the same style as smithy-lang/smithy-dafny#301 did.
  * Also updated various `__default` classes to use the above to avoid constructing Dafny datatypes directly. 
  * Regenerating means that the effect of smithy-lang/smithy-dafny#311 on C# code shows up too.
  * Introduced `InternalResult<T, R>` to replace some internal-only uses of Dafny's compiled `Result` type, to avoid even more Dafny helper methods. 
* Leverage the `smithy-dafny` `<library-root>/codegen-patches[/<service>]` feature to extract out manual patch files.
  * This allows building with a newer version of Dafny to work despite having to regenerate code differently, in some cases by providing different patch files for different version ranges.
  * Cheated slightly by using this to conditionally remove an instance of `{:vcs_split_on_every_assert}` on `AwsKmsKeyring.OnDecrypt'` that is necessary on Dafny 4.2 but makes things work on Dafny 4.4 (which was not the intention of the patching feature, but also solves this problem much more cheaply than having to refactor the code to work in both versions :)
* Add regenerating code to CI, either to verify that it matches what's checked in, or to pick up the necessary changes to work with newer Dafny versions.

Manually verified the CI passes on the source branch with the latest Dafny nightly prerelease: https://github.com/aws/aws-cryptographic-material-providers-library/actions/runs/8039889665

Note that CI will now reject making further manual edits to generated files without capturing those edits in patch files. The error message will suggest how to update the patch files accordingly. See also https://github.com/smithy-lang/smithy-dafny/tree/main-1.x/TestModels/CodegenPatches
robin-aws added a commit that referenced this pull request Apr 9, 2024
…crete class) (#331)

*Description of changes:*

Reverts the part of #301 that changed the result type of constructing services from the concrete class type to the trait type, which turned out to be breaking in .NET as well as Dafny and was manually reverted in aws/aws-cryptographic-material-providers-library#257. See aws/aws-cryptographic-material-providers-library#267 for the upcoming proper fix to the MPL using this change.

As it turns out this change wasn't necessary in the first place, and the `CreateSuccessOfClient/CreateFailureOfError` helpers are unchanged and still use the trait type, because these helpers are only needed for services that are created by target language code, i.e. SDK-style services and `--local-service-test` wrapped services. Regular local services are constructed in Dafny code so they never need the helper methods.
ShubhamChaturvedi7 pushed a commit to ShubhamChaturvedi7/smithy-dafny that referenced this pull request Apr 24, 2024
…ary (smithy-lang#301)

*Description of changes:*
As part of the progress towards supporting traits on non-reference types, the Java backend in Dafny 4.2 changed datatype constructors to require type descriptors for any generic type parameters: dafny-lang/dafny#4240.

This change fixes a big chunk of the failing nightly build failures by augmenting the Java code generation to conditionally provide these type descriptors when creating `Result` and `Option` values. In most cases this is handled by the new `nameresolver.Dafny.createSuccess/Failure/Some/None` helper methods, but in some cases some new Dafny helper methods are added to `StandardLibrary` or emitted as well, so that TestModels can provide the same Java code for any Dafny version.

It also adds a parameter for the target Dafny version so it knows when to emit them, exposed both as a `--dafny-version` option for the CLI and a `dafnyVersion` configuration value on `smithy-build.json` file. This defaults to `4.1.0`, but the latter knob is actually required when using the latest "edition" of the Smithy plugin (now `2023.10` instead of `2023`), as this is exactly the kind of otherwise-breaking change editions are designed to support!

Finally, it refactors the CI to test all Dafny-relevant things on multiple versions of Dafny. It follows the same workflow template as https://github.com/aws/aws-cryptographic-material-providers-library-java/tree/main/.github/workflows (and kudos to @texastony!). This exposed one verification regression on 4.3 that I'm looking into, and may factor out of this PR if it's not quick to fix.

---------

Co-authored-by: Tony Knapp <[email protected]>
lucasmcdonald3 pushed a commit to aws/aws-cryptographic-material-providers-library that referenced this pull request Jun 4, 2024
#195)

Applies several fixes/improvements in order to work with newer Dafny versions.

* Adds `smithy-dafny` as a submodule so that we can lock down the exact commit used to generate code, and use the tool in CI.
* Updates the shared makefile with similar improvements to smithy-dafny's (hook up `--library-root` and `--patch-files-dir`, generate dependencies first)
* Regenerates the checked-in code using a newer `smithy-dafny` to abstract away from the changes in Java TypeDescriptors when constructing datatypes in Dafny 4.3. This includes adding some helper methods to Dafny code for the benefit of Java external code, in the same style as smithy-lang/smithy-dafny#301 did.
  * Also updated various `__default` classes to use the above to avoid constructing Dafny datatypes directly. 
  * Regenerating means that the effect of smithy-lang/smithy-dafny#311 on C# code shows up too.
  * Introduced `InternalResult<T, R>` to replace some internal-only uses of Dafny's compiled `Result` type, to avoid even more Dafny helper methods. 
* Leverage the `smithy-dafny` `<library-root>/codegen-patches[/<service>]` feature to extract out manual patch files.
  * This allows building with a newer version of Dafny to work despite having to regenerate code differently, in some cases by providing different patch files for different version ranges.
  * Cheated slightly by using this to conditionally remove an instance of `{:vcs_split_on_every_assert}` on `AwsKmsKeyring.OnDecrypt'` that is necessary on Dafny 4.2 but makes things work on Dafny 4.4 (which was not the intention of the patching feature, but also solves this problem much more cheaply than having to refactor the code to work in both versions :)
* Add regenerating code to CI, either to verify that it matches what's checked in, or to pick up the necessary changes to work with newer Dafny versions.

Manually verified the CI passes on the source branch with the latest Dafny nightly prerelease: https://github.com/aws/aws-cryptographic-material-providers-library/actions/runs/8039889665

Note that CI will now reject making further manual edits to generated files without capturing those edits in patch files. The error message will suggest how to update the patch files accordingly. See also https://github.com/smithy-lang/smithy-dafny/tree/main-1.x/TestModels/CodegenPatches
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