-
Notifications
You must be signed in to change notification settings - Fork 175
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
Adding local functions #59
Conversation
Function declarations now need to derive from StatementSyntax so that we can use them anywhere we can use statements. MemberSyntax is no longer needed - it was necessary to make functions usable as statements in a special scenario, but now they're always statements. I don't think we need to have separate MethodDeclarationSyntax (which would be a MemberSyntax) and LocalFunctionStatementSyntax like C# does, at least for now, because we don't even have classes and don't need to differentiate these.
… variables aren't evaluated). In order to allow calling functions before their declaration, we have to declare all functions in each scope before binding the scope itself. Previously, binding functions was done in a separate step from binding the root statements, and they were lowered at the same time. Now, binding all functions happens during the binding phase when all other statements are bound and lowering happens later during LowerProgram. This allows a better separation of concerns (binder can be independent from the lowerer and we can get the binding result without lowering). BoundProgram renamed to LoweredProgram to better describe its usage.
Previously, GlobalVariableSymbol needed to be separate from LocalVariableSymbol because there was no way to use a (non-global) variable that was declared outside of the current function. Now that local function variable capturing was added, this *just works* and there is no need for special handling of global variables.
This isn't really necessary. We can use the normal lookup rules for a scope to get the previous submission's variables and functions if we just make it our parent scope. No need to copy and redeclare all symbols.
I'm not sure why this was ever needed.
…unctions for SourceCodeKind.Script
_functionBodies.Add((function, body)); | ||
|
||
// Function declaration is a no-op. | ||
return new BoundBlockStatement(ImmutableArray<BoundStatement>.Empty); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, we could create some sort of a no-op bound node.
public ImmutableArray<Diagnostic> Diagnostics { get; } | ||
public ImmutableArray<FunctionSymbol> Functions { get; } | ||
public ImmutableArray<VariableSymbol> Variables { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is useful anymore since it's accessible from the scope (and noone actually used these properties anyway after the refactoring I did).
{ | ||
public BoundProgram(ImmutableArray<Diagnostic> diagnostics, ImmutableDictionary<FunctionSymbol, BoundBlockStatement> functions, BoundBlockStatement statement) | ||
public LoweredProgram(ImmutableArray<Diagnostic> diagnostics, ImmutableDictionary<FunctionSymbol, BoundBlockStatement> functions, BoundBlockStatement statement) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let the bikeshedding begin. 😄
// } | ||
// foo() | ||
// It would require flow analysis to prevent this from compiling. | ||
throw new Exception("Variable not found."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I skipped this part 😄 flow analysis is getting a little too complicated for me, I don't think I can do that. Maybe we could just return a default value (0 or null)?
@@ -12,78 +12,51 @@ namespace Minsk.CodeAnalysis.Binding | |||
internal sealed class Binder | |||
{ | |||
private readonly DiagnosticBag _diagnostics = new DiagnosticBag(); | |||
private readonly FunctionSymbol _function; | |||
private readonly List<(FunctionSymbol function, BoundBlockStatement body)> _functionBodies = new List<(FunctionSymbol function, BoundBlockStatement body)>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I chose a list of tuples instead of a dictionary here because this collection is never actually used for lookup, it's only ever used for enumeration (which a list is more efficient at).
Sorry :-( Given the amount of merge conflicts I'm going to close this for now. This issue is now tracked as #147. Feel free to submit a new PR for that. |
This PR implements local functions.
The first 3 commits actually implement local functions and the rest is just refactoring & cleanup. If you don't like the followup changes I did there, feel free to only take the first 3 commits and discard the rest. I recommend reviewing this commit by commit. Each commit has a descriptive message to explain what it does and the reason behind it.
The very last commit adds a compilation option for regular/script mode (inspired by Roslyn) and disallows local functions in regular mode because emitting local functions into IL is a lot harder that just evaluating them so that you don't have to do all the work to emit them later.