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

Factor out AST node for function return types. #912

Merged
merged 5 commits into from
Nov 12, 2021
Merged

Factor out AST node for function return types. #912

merged 5 commits into from
Nov 12, 2021

Conversation

geoffromer
Copy link
Contributor

This enables us to stop treating the return type as a Pattern (which is really isn't), treat return types more consistently with other static types in the typechecker, and drop ReturnTypeContext.

Additional changes:

  • Merge TypeCheckFunDef with TypeOfFunDef.
  • Handle implicit conversions in return statements.
  • Require function type literals to have an explicit ->.
  • Move consistency check for omitted returns from TypeChecker to ResolveControlFlow.

This enables us to stop treating the return type as a Pattern (which is really isn't), treat return types more consistently with other static types in the typechecker, and drop ReturnTypeContext.

Additional changes:
- Merge TypeCheckFunDef with TypeOfFunDef.
- Handle implicit conversions in `return` statements.
- Require function type literals to have an explicit `->`.
- Move consistency check for omitted returns from TypeChecker to ResolveControlFlow.
@geoffromer geoffromer added the explorer Action items related to Carbon explorer code label Oct 22, 2021
@geoffromer geoffromer requested a review from a team as a code owner October 22, 2021 21:42
@google-cla google-cla bot added the cla: yes PR meets CLA requirements according to bot. label Oct 22, 2021
Copy link
Contributor

@jonmeow jonmeow left a comment

Choose a reason for hiding this comment

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

Note, it may be worth splitting off the -> requirement for fnty into its own discussion. I think that's a significant syntax change, and probably indicates we should get a leads decision for a path forward. I've bumped #191 to try to address this.

executable_semantics/ast/declaration.h Outdated Show resolved Hide resolved
executable_semantics/ast/declaration.h Show resolved Hide resolved
Comment on lines 112 to 116
// Returns true if this represents an omitted return term.
auto is_omitted() const -> bool { return form_ == Form::Omitted; }

// Returns true if this represents an auto return term.
auto is_auto() const -> bool { return form_ == Form::Auto; }
Copy link
Contributor

Choose a reason for hiding this comment

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

Why expose the kind with boolean accessors? Wouldn't it be less code if the enum were exposed more directly? e.g., in TypeCheckFunctionDeclaration, you could write that as a switch instead of checking is_omitted and type_expression.has_value

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It might be less code in the places that need to handle all three cases, but it would be more code in the places that need to give special treatment to one of the cases. The latter seem to be slightly more common, and they seem to be places where there's more need for brevity. For example, see resolve_control_flow.cpp lines 51 and 53, where calls to function_return.is_omitted() are part of larger expressions, and replacing them with
something like function_return.return_kind() == ReturnTerm::ReturnKind::Omitted would be fairly unwieldy.

Copy link
Contributor

Choose a reason for hiding this comment

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

FWIW, I'd still argue for just having a single accessor in order to make it clear that there are other cases being ignored, and also to make it clearer at call sites that there is a mutually exclusive state (i.e., that it can't have is_omitted == true and is_auto == true, and that both of those imply no expression). But I'll leave this up to you.

executable_semantics/interpreter/resolve_control_flow.cpp Outdated Show resolved Hide resolved
Comment on lines +16 to +17
// Aggregate information about a function being analyzed.
struct FunctionData {
Copy link
Contributor

Choose a reason for hiding this comment

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

It might be helpful to make it clearer this is only for return statements. This took me a few passes to understand while reading.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not quite sure what you mean by "only for return statements". It's true that currently the only nontrivial usages of this type are in the Return case, but that's not a requirement of the type, it's a more-or-less accidental fact about how the type is currently used.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the accidental fact would be useful to note, at least until (or unless) it changes. I view this as a readability issue where reducing the amount of context that needs to be gained is helpful. But you call this accidental -- do you have other situations where you expect usage to expand when resolving control flow?

While it is a function, if Return is the only control flow that cares about functions, then I think it's easier to understand in that context. e.g., even just annotating "// Aggregate information about a function being analyzed. Currently only used for Return statement flow." would be an aid. Renaming it to ReturnContext would be clearer at usage sites that it's not used in other ways, and encourage a rename should it get used in more situations.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think the accidental fact would be useful to note, at least until (or unless) it changes. I view this as a readability issue where reducing the amount of context that needs to be gained is helpful.

I don't understand how that would be helpful, so it seems like there's a gap in my mental model of how people read code. Can you help me understand? For example, can you describe a situation where omitting that information would make the code harder for a reader to understand, and what their thought process might be like?

But you call this accidental -- do you have other situations where you expect usage to expand when resolving control flow?

I don't have specific situations in mind, but I don't have a specific reason to rule it out either.

Partly, I see this as a matter of consistency -- this way, each parameter ofResolveControlFlow (other than statement itself) corresponds to a particular kind of enclosing scope that statement might transfer control to. I think it would be more confusing if one parameter was named and documented in those terms, but the other was named and documented in terms of what kind of statement uses it.

executable_semantics/syntax/parser.ypp Outdated Show resolved Hide resolved
Copy link
Contributor

@jonmeow jonmeow left a comment

Choose a reason for hiding this comment

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

LGTM, thank you for discussing this last night.

case ReturnKind::Omitted:
return;
case ReturnKind::Auto:
out << "-> auto";
Copy link
Contributor

Choose a reason for hiding this comment

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

Stray thought: as static types are added, should they be part of the output for debugability?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently, Print on AST nodes is basically an "unparse" operation whose output is syntactically equivalent to the original input, in the sense that parsing the output of Print should give you an identical AST. I think that's a useful property, and I'm reluctant to abandon it.

I do think we'll eventually want to be able to print something more like a direct dump of the AST, and it would make sense to include type information in that.

executable_semantics/ast/declaration.h Show resolved Hide resolved
executable_semantics/ast/declaration.h Outdated Show resolved Hide resolved
executable_semantics/syntax/parser.ypp Outdated Show resolved Hide resolved
Comment on lines +16 to +17
// Aggregate information about a function being analyzed.
struct FunctionData {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the accidental fact would be useful to note, at least until (or unless) it changes. I view this as a readability issue where reducing the amount of context that needs to be gained is helpful. But you call this accidental -- do you have other situations where you expect usage to expand when resolving control flow?

While it is a function, if Return is the only control flow that cares about functions, then I think it's easier to understand in that context. e.g., even just annotating "// Aggregate information about a function being analyzed. Currently only used for Return statement flow." would be an aid. Renaming it to ReturnContext would be clearer at usage sites that it's not used in other ways, and encourage a rename should it get used in more situations.

executable_semantics/interpreter/resolve_control_flow.cpp Outdated Show resolved Hide resolved
Comment on lines 112 to 116
// Returns true if this represents an omitted return term.
auto is_omitted() const -> bool { return form_ == Form::Omitted; }

// Returns true if this represents an auto return term.
auto is_auto() const -> bool { return form_ == Form::Auto; }
Copy link
Contributor

Choose a reason for hiding this comment

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

FWIW, I'd still argue for just having a single accessor in order to make it clear that there are other cases being ignored, and also to make it clearer at call sites that there is a mutually exclusive state (i.e., that it can't have is_omitted == true and is_auto == true, and that both of those imply no expression). But I'll leave this up to you.

@geoffromer geoffromer merged commit 3bdf7ce into carbon-language:trunk Nov 12, 2021
@geoffromer geoffromer deleted the function branch November 12, 2021 19:30
chandlerc pushed a commit that referenced this pull request Jun 28, 2022
This enables us to stop treating the return type as a Pattern (which is really isn't), treat return types more consistently with other static types in the typechecker, and drop ReturnTypeContext.

Additional changes:
- Merge TypeCheckFunDef with TypeOfFunDef.
- Handle implicit conversions in `return` statements.
- Require function type literals to have an explicit `->`.
- Move consistency check for omitted returns from TypeChecker to ResolveControlFlow.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes PR meets CLA requirements according to bot. explorer Action items related to Carbon explorer code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants