Skip to content

Check role declarations make sense#3881

Merged
hdgarrood merged 5 commits intopurescript:masterfrom
kl0tl:fix-issue-3870
Sep 14, 2020
Merged

Check role declarations make sense#3881
hdgarrood merged 5 commits intopurescript:masterfrom
kl0tl:fix-issue-3870

Conversation

@kl0tl
Copy link
Copy Markdown
Member

@kl0tl kl0tl commented May 18, 2020

This PR enforces the following checks:

  • Role declarations cannot reference non declared types.
  • Role declarations must match the number of arguments of their corresponding type.
  • Role declarations are not allowed for type classes nor type synonyms.
  • Duplicate role declarations are not be allowed.

I’m unsure about the locations of those checks. Should they be in Language.PureScript.Sugar.RoleDeclarations rather than Language.PureScript.Sugar.TypeDeclarations? Role declarations are not desugared to anything though, so maybe they should be in Language.PureScript.TypeChecker instead? I’ve moved those checks to the type checker.

Close #3870 and close #3876.

@hdgarrood
Copy link
Copy Markdown
Contributor

Thanks! I will review after the other roles PRs are merged.

@hdgarrood
Copy link
Copy Markdown
Contributor

The grouping code you have here feels like a bit of a code smell to me, since we're already doing grouping based on which declarations refer to each other in the BindingGroups desugaring step. It feels to me like these checks ought to be relatively easy to implement alongside the existing BindingGroups desugaring step:

  • Role declarations must refer to declarations which exist in the same module
  • Role declarations must refer to data declarations
  • Role declarations may not be duplicated

so perhaps we should have all of the above in the BindingGroups step and only have the arity check in the type checker? I think if we were to go ahead and implement the first refactoring I discussed in #3910 (comment) that might simplify this code as well. Would you mind if I took a stab at that refactoring first?

@kl0tl
Copy link
Copy Markdown
Member Author

kl0tl commented Sep 9, 2020

Sure, go ahead!

@hdgarrood
Copy link
Copy Markdown
Contributor

I had a go at doing that refactoring and had some thoughts:

The graph in the BindingGroups step is already very subtle in terms of what the vertices are and which vertices should depend on which, especially in the presence of kind declarations and type synonyms, and I think introducing roles into the mix as well is likely to make that code quite brittle and difficult to change. On reflection, the current approach of putting all the role declarations before any data declarations at the very end of the desugaring phase and then storing roles temporarily in the Environment when we encounter those role declarations during type checking isn't really all that bad.

I think if I was going to try to refactor this to remove the need to put role declarations in the environment, then I'd modify the AST so that we can store role annotation data directly in the DataDeclaration constructor with a new constructor argument. However, there may be drawbacks to that I'm not aware of, and there's also the issue that these are positional arguments which we pattern match on everywhere, so there would be a ton of places to update, which probably wouldn't be fun.

I also realised that the three checks for role declarations which I recommended adding to the BindingGroups step (must refer to a declaration in the same module, must refer to a data or foreign import data declaration, must not be duplicated) are really more analogous to the checks in the Sugar.TypeDeclarations step, which perform those same checks for type and kind signatures, so I think they would be best off going there. I think the simplest way of doing that would be to require that a role annotation directly follows the data type it describes, like how we require that type and kind signatures directly precede the declarations they describe. That does make things more restrictive than they are now, but I suspect most of the code which has been written already follows this rule, and I think it's a sensible rule to impose.

@kl0tl
Copy link
Copy Markdown
Member Author

kl0tl commented Sep 12, 2020

I moved the checks back to Language.PureScript.Sugar.TypeDeclarations and strengthened the orphan check to throw unless role declarations directly follow their type. This is pleasantly simpler than it used to, thank you for the suggestion!

Copy link
Copy Markdown
Contributor

@hdgarrood hdgarrood left a comment

Choose a reason for hiding this comment

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

Looks great, just a couple of grammar comments regarding one of the errors

, markCode (runProperName name)
, "expects"
, T.pack (show expected)
, if expected > 1 then "arguments" else "argument"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When there are no arguments, the word "argument" should be pluralized, so I think the condition we want here is if expected == 1 then "argument" else "arguments"

, "expects"
, T.pack (show expected)
, if expected > 1 then "arguments" else "argument"
, "but its role declaration list"
Copy link
Copy Markdown
Contributor

@hdgarrood hdgarrood Sep 13, 2020

Choose a reason for hiding this comment

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

To make the verb "list" agree, it should be written "lists", so for example: "but its role declaration lists only 3 roles".

, T.pack (show expected)
, if expected > 1 then "arguments" else "argument"
, "but its role declaration list"
<> if actual > expected then "" else " only"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Adding "only" when there are not enough roles is a nice touch 👍

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is something I picked up from the ClassInstanceArityMismatch error ^^

@kl0tl
Copy link
Copy Markdown
Member Author

kl0tl commented Sep 13, 2020

I applied your suggestions.

Copy link
Copy Markdown
Contributor

@hdgarrood hdgarrood left a comment

Choose a reason for hiding this comment

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

Thanks! Just for future reference, would you mind pushing new commits and leaving the existing commits alone when responding to code review feedback? It makes it easier for reviewers to see what has changed since the last review. The commit history will be tidied up automatically by the "Squash and merge" button.

@kl0tl
Copy link
Copy Markdown
Member Author

kl0tl commented Sep 14, 2020

Sorry about that, I’m trying to get into the habit of pushing fixup commits instead. In the case of whole commits, do you prefer them to be removed from the history or reverted by new commits?

@hdgarrood
Copy link
Copy Markdown
Contributor

No worries - it's not a big deal, so don't worry if you forget. I think pushing new commits which revert the old unwanted ones is preferable, as that makes it easier to see what changed since the last time you looked.

@hdgarrood hdgarrood merged commit d159242 into purescript:master Sep 14, 2020
@kl0tl kl0tl deleted the fix-issue-3870 branch September 18, 2020 17:25
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.

Can declare roles multiple times Can declare roles for a non-existent type

2 participants