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: Modules in the python backend #1871

Merged
merged 12 commits into from
Mar 3, 2022

Conversation

fabiomadge
Copy link
Collaborator

@fabiomadge fabiomadge commented Feb 28, 2022

Adds support for modules, and increases the stinginess w.r.t. whitespace in the generated code.

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

@@ -1194,6 +1196,8 @@ public enum ResolvedUnaryOp { BoolNot, BitwiseNot, Cardinality }
var w = DeclareDatatype(dt, wr);
if (w != null) {
CompileClassMembers(program, dt, w);
} else if (this is not CppCompiler) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The alternative is living with a \n when there's a tuple. I personally don't like either.

Copy link
Member

Choose a reason for hiding this comment

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

Could this be an attribute of each of the compilers that the main code checks to decide whether to add the \n?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This code is a little strange, we always add a \n in L1161 for every TopLevelDecl that's compiled. Sometimes we only find out later that we don't have to do anything. This leaves unsightly whitespace in the generated sources. Looking it is no fun.

The concrete problem here is a hack in the cpp compiler. They include the ClassMembers on the first pass, so they can't have the second one. To avoid that, they act like they didn't do anything.

Copy link
Member

Choose a reason for hiding this comment

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

Sounds like we can still define an attribute, even if it's a weird name like CompilesClassMembersOnTheFirstPass or whatever. :)

Referring to concrete subclasses here is just a recipe for weird hard-to-debug edge cases as we add more compilers.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Now, it's this one field we happen to overwrite in this one compiler.

@@ -1238,8 +1242,10 @@ public enum ResolvedUnaryOp { BoolNot, BitwiseNot, Cardinality }
}
} else if (d is ValuetypeDecl) {
// nop
wr.DeleteLast();
Copy link
Member

Choose a reason for hiding this comment

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

That's a bit scary: are we sure that we know which character this will be deleting?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Reasonably so, the test go trough after all. At least in this case it should be.

Copy link
Member

Choose a reason for hiding this comment

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

I agree this is very risky and makes the code an order of magnitude harder to reason about confidently. I'd define whatever additional Compiler attributes you need to specialize the behavior without doing this.

I hesitate to even mention it, but IF this was absolutely necessary, I would at least provide an argument to DeleteLast to specify WHAT you expect to be deleted, so at least you have a chance of catching bugs if the state is ever not what you thought it would be.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think it's reasonably nice now.

Copy link
Member

@robin-aws robin-aws left a comment

Choose a reason for hiding this comment

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

Thanks for starting to tackle the confusing labelling on the BraceStyle enum!

compiledName = IdProtect((string)idParam);
break;
default:
Contract.Assert(false); // unexpected ID
Copy link
Member

Choose a reason for hiding this comment

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

I'd prefer throwing an exception instead. Contract.Assert is turned off for release builds, and we don't the chance of a user hitting this case and failing in a weird way elsewhere because of the default "" value.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is a very common pattern in our code base and all the other implementations of GetSpecialFieldInfo do it this way, but I can change it if you feel strongly about it. I'd argue that a user will notice it at the very latest when their generated code doesn't compile.

Copy link
Member

Choose a reason for hiding this comment

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

Okay, I'll let it go for now. I'll consider this something we should clean up when we address #1390 more generally.

@@ -7,10 +7,11 @@

namespace Microsoft.Dafny {
public enum BraceStyle {
Copy link
Member

Choose a reason for hiding this comment

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

Since you're already touching most of the references to this type, I think the time is right to fix the name and call it BlockStyle instead. That will resolve the weird redundancy in BraceStyle.Brace and just plain contradiction in BraceStyle.NewlineNoBrace (which would just become BlockStyle.Newline).

I think then Space would also be renamed to SpaceBrace?

@@ -1194,6 +1196,8 @@ public enum ResolvedUnaryOp { BoolNot, BitwiseNot, Cardinality }
var w = DeclareDatatype(dt, wr);
if (w != null) {
CompileClassMembers(program, dt, w);
} else if (this is not CppCompiler) {
Copy link
Member

Choose a reason for hiding this comment

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

Sounds like we can still define an attribute, even if it's a weird name like CompilesClassMembersOnTheFirstPass or whatever. :)

Referring to concrete subclasses here is just a recipe for weird hard-to-debug edge cases as we add more compilers.

@@ -1238,8 +1242,10 @@ public enum ResolvedUnaryOp { BoolNot, BitwiseNot, Cardinality }
}
} else if (d is ValuetypeDecl) {
// nop
wr.DeleteLast();
Copy link
Member

Choose a reason for hiding this comment

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

I agree this is very risky and makes the code an order of magnitude harder to reason about confidently. I'd define whatever additional Compiler attributes you need to specialize the behavior without doing this.

I hesitate to even mention it, but IF this was absolutely necessary, I would at least provide an argument to DeleteLast to specify WHAT you expect to be deleted, so at least you have a chance of catching bugs if the state is ever not what you thought it would be.


var customReceiver = !forBodyInheritance && NeedsCustomReceiver(m);
Copy link
Member

Choose a reason for hiding this comment

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

Did all of this disappear because customReceiver is never true in our test cases so far?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Exactly.

Copy link
Member

Choose a reason for hiding this comment

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

Great, I'm definitely in favour of removing code with no testing coverage (assuming it's not easy to add a test instead).

Copy link
Member

@robin-aws robin-aws left a comment

Choose a reason for hiding this comment

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

Much better, thanks for the improvements! Just the one small tweak and then LGTM.

compiledName = IdProtect((string)idParam);
break;
default:
Contract.Assert(false); // unexpected ID
Copy link
Member

Choose a reason for hiding this comment

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

Okay, I'll let it go for now. I'll consider this something we should clean up when we address #1390 more generally.

protected abstract ConcreteSyntaxTree CreateIterator(IteratorDecl iter, ConcreteSyntaxTree wr);
/// <summary>
/// Returns an IClassWriter that can be used to write additional members. If "dt" is already written
/// in the DafnyRuntime.targetlanguage file, then returns "null".
/// </summary>
protected abstract IClassWriter/*?*/ DeclareDatatype(DatatypeDecl dt, ConcreteSyntaxTree wr);
protected virtual bool DatatypeDeclarationAndMemberCompilationAreSeparate => true;
Copy link
Member

Choose a reason for hiding this comment

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

Much better name than my suggestion :)

@@ -114,6 +115,12 @@ public T Append<T>(T node)
return this;
}

public void DeleteLast() {
Copy link
Member

Choose a reason for hiding this comment

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

Very glad we don't need this any more. Now let's remove the method so no one is tempted by the dark side in the future. :)

Comment on lines -9 to 15
public enum BraceStyle {
public enum BlockStyle {
Nothing,
Space,
Newline,
NewlineNoBrace
Brace,
SpaceBrace,
NewlineBrace
}
Copy link
Member

Choose a reason for hiding this comment

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

SO much better, thank you!

@fabiomadge fabiomadge enabled auto-merge (squash) March 2, 2022 19:01
@fabiomadge fabiomadge merged commit af985e8 into dafny-lang:master Mar 3, 2022
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