-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Async main codegen #18472
Async main codegen #18472
Changes from 25 commits
e27fb34
b4cee90
61e4705
39a8600
0cb90e3
4af405a
5a3fbcf
b16987f
8f0e223
ed6271d
578e811
92d3bfc
af675d3
1f71b66
671c53b
e99bf98
1114921
21a892c
c692009
9144795
cd2488e
d0675b1
04b5380
112f4fe
f532f23
617c791
5f177ba
d1e3e72
39cf05d
41cddf9
90edc8f
0407c20
8acedcc
9cbf02c
fe158be
89a15d8
1465d2c
2fc8c62
7d01d2e
e9e2875
81b01e2
4cd16ea
8a8afe2
1c2c643
c8b7b07
d975ba2
bad2923
85de26c
f121bb4
0a84b1d
e4e355e
b290268
eb68cea
247e05d
8e60326
881dea0
1d2b4db
3e8237a
2047833
7187917
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1454,7 +1454,9 @@ private MethodSymbol FindEntryPoint(CancellationToken cancellationToken, out Imm | |
} | ||
|
||
DiagnosticBag warnings = DiagnosticBag.GetInstance(); | ||
DiagnosticBag possibleAsyncMainDiagnostics = DiagnosticBag.GetInstance(); | ||
var viableEntryPoints = ArrayBuilder<MethodSymbol>.GetInstance(); | ||
bool asyncOk = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider replacing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've actually removed all of this |
||
foreach (var candidate in entryPointCandidates) | ||
{ | ||
if (!candidate.HasEntryPointSignature(this)) | ||
|
@@ -1474,7 +1476,7 @@ private MethodSymbol FindEntryPoint(CancellationToken cancellationToken, out Imm | |
if (candidate.IsAsync) | ||
{ | ||
// PROTOTYPE(async-main): Get the diagnostic to point to a smaller syntax piece. | ||
CheckFeatureAvailability(candidate.DeclaringSyntaxReferences.Single().GetSyntax(), MessageID.IDS_FeatureAsyncMain, diagnostics); | ||
asyncOk &= CheckFeatureAvailability(candidate.DeclaringSyntaxReferences.Single().GetSyntax(), MessageID.IDS_FeatureAsyncMain, possibleAsyncMainDiagnostics); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It feels like if the feature is not available we should add a warning into There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As above, removed. |
||
} | ||
|
||
viableEntryPoints.Add(candidate); | ||
|
@@ -1488,7 +1490,11 @@ private MethodSymbol FindEntryPoint(CancellationToken cancellationToken, out Imm | |
warnings.Free(); | ||
|
||
MethodSymbol entryPoint = null; | ||
if (viableEntryPoints.Count == 0) | ||
|
||
int nonAsyncCount = viableEntryPoints.Count(c => !c.IsAsync); | ||
int asyncCount = viableEntryPoints.Count - nonAsyncCount; | ||
|
||
if (nonAsyncCount == 0 && asyncCount == 0) | ||
{ | ||
if ((object)mainType == null) | ||
{ | ||
|
@@ -1499,22 +1505,31 @@ private MethodSymbol FindEntryPoint(CancellationToken cancellationToken, out Imm | |
diagnostics.Add(ErrorCode.ERR_NoMainInClass, mainType.Locations.First(), mainType); | ||
} | ||
} | ||
else if (viableEntryPoints.Count > 1) | ||
else if (nonAsyncCount == 1) | ||
{ | ||
entryPoint = viableEntryPoints.Single(c => !c.IsAsync); | ||
} | ||
else if (nonAsyncCount == 0 && asyncCount == 1) | ||
{ | ||
diagnostics.AddRange(possibleAsyncMainDiagnostics); | ||
entryPoint = viableEntryPoints.Single(); | ||
} | ||
else if (nonAsyncCount == 0 && !asyncOk) | ||
{ | ||
diagnostics.AddRange(possibleAsyncMainDiagnostics); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we testing this case? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I went and did some more testing, and apparently all of this complex logic isn't actually very useful. In shipping csc, there is no way to have an async method that meets the entrypoint requirements without compiler errors. So all of this stuff that was supposed to be for backcompat is pretty useless... |
||
} | ||
else | ||
{ | ||
viableEntryPoints.Sort(LexicalOrderSymbolComparer.Instance); | ||
var info = new CSDiagnosticInfo( | ||
ErrorCode.ERR_MultipleEntryPoints, | ||
args: Array.Empty<object>(), | ||
symbols: viableEntryPoints.OfType<Symbol>().AsImmutable(), | ||
additionalLocations: viableEntryPoints.Select(m => m.Locations.First()).OfType<Location>().AsImmutable()); | ||
|
||
diagnostics.Add(new CSDiagnostic(info, viableEntryPoints.First().Locations.First())); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. Done. |
||
else | ||
{ | ||
entryPoint = viableEntryPoints[0]; | ||
} | ||
|
||
possibleAsyncMainDiagnostics.Free(); | ||
viableEntryPoints.Free(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Please keep the empty line. Since this is the only change in the file, consider reverting it to the original state.#Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok |
||
return entryPoint; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -184,6 +184,8 @@ public static void CompileMethodBodies( | |
} | ||
} | ||
|
||
// Returns the MethodSymbol for the assembly entrypoint. If the user has a Task returning main, | ||
// this function returns the synthesized Main MethodSymbol. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to specify the "MoveNext" as an entry point for debug purposes? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we have to specify two entry points - actual and for debugging purposes, it would be a different change, I guess. In reply to: 111274974 [](ancestors = 111274974) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll manually test this and see if I run into anything. |
||
private static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModuleBuilder moduleBeingBuilt, bool hasDeclarationErrors, DiagnosticBag diagnostics, CancellationToken cancellationToken) | ||
{ | ||
var entryPointAndDiagnostics = compilation.GetEntryPointAndDiagnostics(cancellationToken); | ||
|
@@ -194,9 +196,26 @@ private static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModul | |
|
||
Debug.Assert(!entryPointAndDiagnostics.Diagnostics.IsDefault); | ||
diagnostics.AddRange(entryPointAndDiagnostics.Diagnostics); | ||
|
||
var entryPoint = entryPointAndDiagnostics.MethodSymbol; | ||
var synthesizedEntryPoint = entryPoint as SynthesizedEntryPointSymbol; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should add a comment to the top of the function noting that this returns the real entry point vs. the user defined one. The name of the function is ambiguous with this feature now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
if ((object)entryPoint == null) | ||
{ | ||
Debug.Assert(entryPointAndDiagnostics.Diagnostics.HasAnyErrors() || !compilation.Options.Errors.IsDefaultOrEmpty); | ||
return null; | ||
} | ||
|
||
// entryPoint can be a SynthesizedEntryPointSymbol if a script is being compiled. | ||
SynthesizedEntryPointSymbol synthesizedEntryPoint = entryPoint as SynthesizedEntryPointSymbol; | ||
if ((object)synthesizedEntryPoint == null && entryPoint.HasAsyncMainReturnType(compilation) && compilation.LanguageVersion >= LanguageVersion.CSharp7_1) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we reporting an error for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, but only if there are no non-async mains available. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really need to check There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, good point. Done. |
||
{ | ||
synthesizedEntryPoint = new SynthesizedEntryPointSymbol.AsyncForwardEntryPoint(compilation, diagnostics, entryPoint.ContainingType, entryPoint); | ||
entryPoint = synthesizedEntryPoint; | ||
if ((object)moduleBeingBuilt != null) | ||
{ | ||
moduleBeingBuilt.AddSynthesizedDefinition(entryPoint.ContainingType, synthesizedEntryPoint); | ||
} | ||
} | ||
|
||
if (((object)synthesizedEntryPoint != null) && | ||
(moduleBeingBuilt != null) && | ||
!hasDeclarationErrors && | ||
|
@@ -221,7 +240,6 @@ private static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModul | |
moduleBeingBuilt.SetMethodBody(synthesizedEntryPoint, emittedBody); | ||
} | ||
|
||
Debug.Assert((object)entryPoint != null || entryPointAndDiagnostics.Diagnostics.HasAnyErrors() || !compilation.Options.Errors.IsDefaultOrEmpty); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It is not clear why this assert is removed.#Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The assert wasn't deleted, it was moved upwards in the file to line 203 |
||
return entryPoint; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -595,6 +595,29 @@ internal bool IsEntryPointCandidate | |
get { return IsStatic && Name == WellKnownMemberNames.EntryPointMethodName; } | ||
} | ||
|
||
internal bool HasAsyncMainReturnType(CSharpCompilation compilation) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This looks like a very specialized helper, I wouldn't put it into MethodSymbol. Consider putting it next to the consumer and making it private. Also, the name is somewhat confusing because the method doesn't care whether the method is async or not. It only checks the return type for Task. Consider renaming the method to reflect that. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see that the method is used in more than one place, it is fine to keep it here, I guess.#Closed #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll move it, but I think the name is fine as-is. "HasAsyncMainReturnType" tells me that the return type needs to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Then the name should covey just that, for example ReturnsTaskOrTaskOfInt, describes exactly what to expect from the function. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed name. I left it in MethodSymbol because MethodSymbol also has |
||
{ | ||
var namedType = ReturnType as NamedTypeSymbol; | ||
if ((object)namedType == null) | ||
{ | ||
return false; | ||
} | ||
else if (namedType.ConstructedFrom == compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task)) | ||
{ | ||
// Change this to `namedType.IsNonGenericTaskType` if you want to support "task-like" objects. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Should this be a PROTOTYPE comment?#Closed #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right now we are making a conscious decision to ban task-likes. I left the comment in case we wanted to loosen the rules later on. |
||
return true; | ||
} | ||
else if (namedType.ConstructedFrom == compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task_T)) | ||
{ | ||
// Change this to `namedType.IsGenericTaskType` if you want to support "task-like" objects. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Should this be a PROTOTYPE comment?#Closed #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right now we are making a conscious decision to ban task-likes. I left the comment in case we wanted to loosen the rules later on. |
||
return namedType.TypeArguments[0].SpecialType == SpecialType.System_Int32; | ||
} | ||
else | ||
{ | ||
return false; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Checks if the method has an entry point compatible signature, i.e. | ||
/// - the return type is either void, int, <see cref="System.Threading.Tasks.Task" />, | ||
|
@@ -603,37 +626,13 @@ internal bool IsEntryPointCandidate | |
/// </summary> | ||
internal bool HasEntryPointSignature(CSharpCompilation compilation) | ||
{ | ||
|
||
bool IsAsyncMainReturnType(TypeSymbol type) | ||
{ | ||
var namedType = type as NamedTypeSymbol; | ||
if (namedType == null) | ||
{ | ||
return false; | ||
} | ||
else if (namedType.ConstructedFrom == compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task)) | ||
{ | ||
// Change this to `namedType.IsNonGenericTaskType` if you want to support "task-like" objects. | ||
return true; | ||
} | ||
else if (namedType.ConstructedFrom == compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task_T)) | ||
{ | ||
// Change this to `namedType.IsGenericTaskType` if you want to support "task-like" objects. | ||
return namedType.TypeArguments[0].SpecialType == SpecialType.System_Int32; | ||
} | ||
else | ||
{ | ||
return false; | ||
} | ||
} | ||
|
||
if (IsVararg) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Empty lines above. |
||
{ | ||
return false; | ||
} | ||
|
||
TypeSymbol returnType = ReturnType; | ||
bool isAsyncMainReturnType = IsAsyncMainReturnType(returnType); | ||
bool isAsyncMainReturnType = HasAsyncMainReturnType(compilation); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The name of the local is confusing, it implies that the method is async, yet it may disagree with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
if (returnType.SpecialType != SpecialType.System_Int32 && returnType.SpecialType != SpecialType.System_Void && !isAsyncMainReturnType) | ||
{ | ||
return false; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Diagnostics; | ||
|
@@ -301,12 +302,17 @@ private static void ReportUseSiteDiagnostics(Symbol symbol, DiagnosticBag diagno | |
} | ||
} | ||
|
||
private static MethodSymbol GetRequiredMethod(TypeSymbol type, string methodName, DiagnosticBag diagnostics) | ||
private static MethodSymbol GetRequiredMethod(TypeSymbol type, string methodName, DiagnosticBag diagnostics, Location location = null) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This helper doesn't report use-site diagnostics for the member it returns. The errors should be reported. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add tests for that as well. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How do you do that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
You can take a look at the Binder.GetWellKnownTypeMember helper as an example. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is a use-site diagnostic? How do you inject a "use-site-diagnostic" into a document that has no real "uses" of the synthesized main? In reply to: 111510678 [](ancestors = 111510678) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Are we still using this function for the purpose of this PR? If not, consider reverting changes in it. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
{ | ||
if ((object)location == null) | ||
{ | ||
location = NoLocation.Singleton; | ||
} | ||
|
||
var method = type.GetMembers(methodName).SingleOrDefault() as MethodSymbol; | ||
if ((object)method == null) | ||
{ | ||
diagnostics.Add(ErrorCode.ERR_MissingPredefinedMember, NoLocation.Singleton, type, methodName); | ||
diagnostics.Add(ErrorCode.ERR_MissingPredefinedMember, location, type, methodName); | ||
} | ||
return method; | ||
} | ||
|
@@ -329,6 +335,123 @@ private static BoundCall CreateParameterlessCall(CSharpSyntaxNode syntax, BoundE | |
{ WasCompilerGenerated = true }; | ||
} | ||
|
||
// A synthesized entrypoint that forwards all calls to an async Main Method | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Please use standard doc comments.#Closed #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
internal sealed class AsyncForwardEntryPoint : SynthesizedEntryPointSymbol | ||
{ | ||
// The user-defined asynchronous main method. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Please use standard doc comments.#Closed #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
private readonly MethodSymbol _userMain; | ||
|
||
private readonly MethodSymbol _getAwaiterMethod; | ||
private readonly MethodSymbol _getResultMethod; | ||
|
||
private readonly ImmutableArray<ParameterSymbol> _parameters; | ||
|
||
// Task -> Void | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Please use standard doc comments.#Closed #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
// Task<_> -> Int32 | ||
private static TypeSymbol TranslateReturnType(CSharpCompilation compilation, TypeSymbol returnType) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
According to the C# language specification type of the task's result is determined based on the return type of the GetResult method, rather than based on the shape of the task type itself. Implementation of this function doesn't follow this. #Closed |
||
{ | ||
return returnType.IsGenericTaskType(compilation) | ||
? compilation.GetSpecialType(SpecialType.System_Int32) | ||
: compilation.GetSpecialType(SpecialType.System_Void); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should be protected at this point, as this code is only executed if an async-main entrypoint was found, which can't happen if GetSpecialType is broken. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It feels like this can fail without producing any errors, we should be using a helper from the Binder instead that makes sure the right diagnostics is reported. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add tests for cases when int or void are missing (two separate cases). #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe that this code can't fail due to the checks in IsEntryPointSignature. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I see nothing in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
|
||
internal AsyncForwardEntryPoint(CSharpCompilation compilation, DiagnosticBag diagnosticBag, NamedTypeSymbol containingType, MethodSymbol userMain) : | ||
base(containingType, TranslateReturnType(compilation, userMain.ReturnType)) | ||
{ | ||
// There should be no way for a userMain to be passed in unless it already passed the | ||
// parameter checks for determining entrypoint validity. | ||
Debug.Assert(userMain.ParameterCount == 0 || userMain.ParameterCount == 1); | ||
|
||
_userMain = userMain; | ||
_parameters = SynthesizedParameterSymbol.DeriveParameters(userMain, this); | ||
|
||
var userMainLocation = userMain.DeclaringSyntaxReferences.SingleOrDefault()?.GetLocation(); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Double blank line. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
_getAwaiterMethod = GetRequiredMethod(_userMain.ReturnType, WellKnownMemberNames.GetAwaiter, diagnosticBag, userMainLocation); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It looks like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, it looks like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
How do I get a Binder so that I can reuse this method? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Let's discuss this offline. #Closed |
||
if ((object)_getAwaiterMethod != null) | ||
{ | ||
_getResultMethod = GetRequiredMethod(_getAwaiterMethod.ReturnType, WellKnownMemberNames.GetResult, diagnosticBag, userMainLocation); | ||
} | ||
} | ||
|
||
public override string Name => MainName; | ||
|
||
public override ImmutableArray<ParameterSymbol> Parameters => _parameters; | ||
|
||
internal override BoundBlock CreateBody() | ||
{ | ||
var syntax = this.GetSyntax(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Does this return the root of CSharpSyntaxTree.Dummy? It feels like we might want to use more specific node. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does. Just like the rest of the synthesized entry points. What other tree would we use? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We should use syntax corresponding to the method we are wrapping. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's no equivalent syntax from what we've made to what we're producing. How would red squiggles all over their async main that don't correspond to the actual error be helpful? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Actually there is - it is the declaration of the user's Main that we are wrapping. Pretty much every time we synthesize code, there is a user syntax that is related to that and we are using that syntax rather than providing no location at all. You are using bad code as a template, given the amount of issues with it, it is a prototype quality code at best - some experiments around scripting.
First of all they won't be "all over their async main", I would expect as to squiggle just its name, this what we usually do when we report errors for a declaration. And this will be helpful as it would point the declaration that triggered the errors, i.e. the source of the problem. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Which specific node? What is the point of having a specific "dummy" node? In reply to: 111511103 [](ancestors = 111511103) |
||
|
||
var arguments = Parameters.SelectAsArray(p => (BoundExpression)new BoundParameter(syntax, p, p.Type)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Consider using SyntheticBoundNodeFactory to synthesize the tree. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The rest of this file uses manual tree creation. If this is an anti-pattern, the whole file could be fixed in a separate PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
// Main(args) or Main() | ||
BoundCall userMainInvocation = new BoundCall( | ||
syntax: this.GetSyntax(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
receiverOpt: null, | ||
method: _userMain, | ||
arguments: arguments, | ||
argumentNamesOpt: default(ImmutableArray<string>), | ||
argumentRefKindsOpt: default(ImmutableArray<RefKind>), | ||
isDelegateCall: false, | ||
expanded: false, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider testing synthesized call to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you expand on what you mean by this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've added a test for params |
||
invokedAsExtensionMethod: false, | ||
argsToParamsOpt: default(ImmutableArray<int>), | ||
resultKind: LookupResultKind.Viable, | ||
type: _userMain.ReturnType) | ||
{ WasCompilerGenerated = true }; | ||
|
||
// GetAwaiter().GetResult() | ||
BoundCall getAwaiterGetResult = | ||
CreateParameterlessCall( | ||
syntax: syntax, | ||
method: _getResultMethod, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can CreateBody be called if the constructor for AsyncForwardEntryPoint added an error to the diagnostic bag? If so this would end up passing a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
receiver: CreateParameterlessCall( | ||
syntax: syntax, | ||
method: _getAwaiterMethod, | ||
receiver: userMainInvocation | ||
) | ||
); | ||
|
||
if (ReturnsVoid) | ||
{ | ||
return new BoundBlock( | ||
syntax: syntax, | ||
locals: ImmutableArray<LocalSymbol>.Empty, | ||
statements: ImmutableArray.Create<BoundStatement>( | ||
new BoundExpressionStatement( | ||
syntax: syntax, | ||
expression: getAwaiterGetResult | ||
) | ||
{ WasCompilerGenerated = true }, | ||
new BoundReturnStatement( | ||
syntax: syntax, | ||
refKind: RefKind.None, | ||
expressionOpt: null | ||
) | ||
{ WasCompilerGenerated = true } | ||
) | ||
) | ||
{ WasCompilerGenerated = true }; | ||
|
||
} | ||
else | ||
{ | ||
return new BoundBlock( | ||
syntax: syntax, | ||
locals: ImmutableArray<LocalSymbol>.Empty, | ||
statements: ImmutableArray.Create<BoundStatement>( | ||
new BoundReturnStatement( | ||
syntax: syntax, | ||
refKind: RefKind.None, | ||
expressionOpt: getAwaiterGetResult | ||
) | ||
) | ||
) | ||
{ WasCompilerGenerated = true }; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should these There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would expect all of them to have WasCompilerGenerated = true, so I'll change that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
} | ||
} | ||
|
||
private sealed class ScriptEntryPoint : SynthesizedEntryPointSymbol | ||
{ | ||
private readonly MethodSymbol _getAwaiterMethod; | ||
|
@@ -344,15 +467,9 @@ internal ScriptEntryPoint(NamedTypeSymbol containingType, TypeSymbol returnType, | |
_getResultMethod = getResultMethod; | ||
} | ||
|
||
public override string Name | ||
{ | ||
get { return MainName; } | ||
} | ||
public override string Name => MainName; | ||
|
||
public override ImmutableArray<ParameterSymbol> Parameters | ||
{ | ||
get { return ImmutableArray<ParameterSymbol>.Empty; } | ||
} | ||
public override ImmutableArray<ParameterSymbol> Parameters => ImmutableArray<ParameterSymbol>.Empty; | ||
|
||
// private static void <Main>() | ||
// { | ||
|
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.
It doesn't look like the return value is used.
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.
Oh yep, not used anymore.
Fixed
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.
Fixed