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

Merge main to features/solutionEvents #64386

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f7da8e8
Update localization-suggestion.md
JoeRobich Sep 21, 2022
3bafe61
Rewrite the code
Cosifne Sep 27, 2022
80dfbf9
Fix the test
Cosifne Sep 27, 2022
2d22f96
Use pooledObject
Cosifne Sep 27, 2022
a3a343c
Fine tune the order
Cosifne Sep 27, 2022
93cb4ed
Merge remote-tracking branch 'upstream/release/dev17.4' into dev/shec…
Cosifne Sep 28, 2022
078f689
Remove the extra blank line
Cosifne Sep 28, 2022
2f725a5
Run the tests
Cosifne Sep 28, 2022
f137696
Remove feature flag
jasonmalinowski Sep 28, 2022
6314fea
Keep the test skipped
Cosifne Sep 28, 2022
30be77a
Make sure token also interects with the span
Cosifne Sep 28, 2022
ec7dbe4
Call ToString() instead of Enum.GetName
jasonmalinowski Sep 28, 2022
936a656
Remove Lazy
jasonmalinowski Sep 28, 2022
a19165c
Call Logger.LogBlock instead of directly calling ETW
jasonmalinowski Sep 28, 2022
709672c
Add comments to explain how to use this
jasonmalinowski Sep 29, 2022
933b669
Merge pull request #64311 from Cosifne/dev/shech/FixStackOverFlowInCl…
Cosifne Sep 29, 2022
f18b597
Bitness specific test conditions (#64373)
tmat Sep 29, 2022
8cbb0c6
Merge pull request #64350 from jasonmalinowski/remove-feature-flag
jasonmalinowski Sep 29, 2022
523b135
Merge pull request #64359 from jasonmalinowski/logger-cleanups
jasonmalinowski Sep 29, 2022
a5373dd
Always bind implicit constructor initializers for explicit constructo…
333fred Sep 29, 2022
5adb79f
Merge pull request #64362 from dotnet/merges/release/dev17.4-to-main
dotnet-bot Sep 29, 2022
24020c2
Merge pull request #64187 from dotnet/dev/jorobich/update-template
JoeRobich Sep 30, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/ISSUE_TEMPLATE/localization-suggestion.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ https://github.com/dotnet/roslyn

<!-- Do not make changes below this line. -->
---
## Roslyn Team Instructions

A Roslyn team member will file a bug through https://aka.ms/ceLocBug which the translation team will consider.

Internal Tracking Issue: {Update with tracking issue URL.}
Internal Tracking Issue: {Update with tracking issue URL.}
253 changes: 251 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3547,11 +3547,15 @@ private BoundNode BindRecordConstructorBody(RecordDeclarationSyntax recordDecl,
Binder bodyBinder = this.GetBinder(recordDecl);
Debug.Assert(bodyBinder != null);

BoundExpressionStatement initializer = null;
BoundExpressionStatement initializer;
if (recordDecl.PrimaryConstructorBaseTypeIfClass is PrimaryConstructorBaseTypeSyntax baseWithArguments)
{
initializer = bodyBinder.BindConstructorInitializer(baseWithArguments, diagnostics);
}
else
{
initializer = bodyBinder.BindImplicitConstructorInitializer(recordDecl, diagnostics);
}

return new BoundConstructorMethodBody(recordDecl,
bodyBinder.GetDeclaredLocalsForScope(recordDecl),
Expand Down Expand Up @@ -3604,7 +3608,7 @@ private BoundNode BindConstructorBody(ConstructorDeclarationSyntax constructor,
// Using BindStatement to bind block to make sure we are reusing results of partial binding in SemanticModel
return new BoundConstructorMethodBody(constructor,
bodyBinder.GetDeclaredLocalsForScope(constructor),
initializer == null ? null : bodyBinder.BindConstructorInitializer(initializer, diagnostics),
initializer == null ? bodyBinder.BindImplicitConstructorInitializer(constructor, diagnostics) : bodyBinder.BindConstructorInitializer(initializer, diagnostics),
constructor.Body == null ? null : (BoundBlock)bodyBinder.BindStatement(constructor.Body, diagnostics),
constructor.ExpressionBody == null ?
null :
Expand Down Expand Up @@ -3636,6 +3640,251 @@ internal virtual BoundExpressionStatement BindConstructorInitializer(Constructor
return constructorInitializer;
}

#nullable enable
internal BoundExpressionStatement? BindImplicitConstructorInitializer(SyntaxNode ctorSyntax, BindingDiagnosticBag diagnostics)
{
BoundExpression? initializerInvocation;
initializerInvocation = BindImplicitConstructorInitializer((MethodSymbol)this.ContainingMember(), diagnostics, Compilation);

if (initializerInvocation is null)
{
return null;
}

// Base WasCompilerGenerated state off of whether constructor is implicitly declared, this will ensure proper instrumentation.
var constructorInitializer = new BoundExpressionStatement(ctorSyntax, initializerInvocation) { WasCompilerGenerated = ((MethodSymbol)ContainingMember()).IsImplicitlyDeclared };
Debug.Assert(initializerInvocation.HasAnyErrors || constructorInitializer.IsConstructorInitializer(), "Please keep this bound node in sync with BoundNodeExtensions.IsConstructorInitializer.");
return constructorInitializer;
}

/// <summary>
/// Bind the implicit constructor initializer of a constructor symbol.
/// </summary>
/// <param name="constructor">Constructor method.</param>
/// <param name="diagnostics">Accumulates errors (e.g. access "this" in constructor initializer).</param>
/// <param name="compilation">Used to retrieve binder.</param>
/// <returns>A bound expression for the constructor initializer call.</returns>
internal static BoundExpression? BindImplicitConstructorInitializer(
MethodSymbol constructor, BindingDiagnosticBag diagnostics, CSharpCompilation compilation)
{
if (constructor.MethodKind != MethodKind.Constructor || constructor.IsExtern)
{
return null;
}

// Note that the base type can be null if we're compiling System.Object in source.
NamedTypeSymbol containingType = constructor.ContainingType;
NamedTypeSymbol baseType = containingType.BaseTypeNoUseSiteDiagnostics;

SourceMemberMethodSymbol? sourceConstructor = constructor as SourceMemberMethodSymbol;
Debug.Assert(sourceConstructor?.SyntaxNode is RecordDeclarationSyntax
|| ((ConstructorDeclarationSyntax?)sourceConstructor?.SyntaxNode)?.Initializer == null);

// The common case is that the type inherits directly from object.
// Also, we might be trying to generate a constructor for an entirely compiler-generated class such
// as a closure class; in that case it is vexing to try to find a suitable binder for the non-existing
// constructor syntax so that we can do unnecessary overload resolution on the non-existing initializer!
// Simply take the early out: bind directly to the parameterless object ctor rather than attempting
// overload resolution.
if ((object)baseType != null)
{
if (baseType.SpecialType == SpecialType.System_Object)
{
return GenerateBaseParameterlessConstructorInitializer(constructor, diagnostics);
}
else if (baseType.IsErrorType() || baseType.IsStatic)
{
// If the base type is bad and there is no initializer then we can just bail.
// We have no expressions we need to analyze to report errors on.
return null;
}
}

if (containingType.IsStructType() || containingType.IsEnumType())
{
return null;
}
else if (constructor is SynthesizedRecordCopyCtor copyCtor)
{
return GenerateBaseCopyConstructorInitializer(copyCtor, diagnostics);
}

// Now, in order to do overload resolution, we're going to need a binder. There are
// two possible situations:
//
// class D1 : B { }
// class D2 : B { D2(int x) { } }
//
// In the first case the binder needs to be the binder associated with
// the *body* of D1 because if the base class ctor is protected, we need
// to be inside the body of a derived class in order for it to be in the
// accessibility domain of the protected base class ctor.
//
// In the second case the binder could be the binder associated with
// the body of D2; since the implicit call to base() will have no arguments
// there is no need to look up "x".
Binder outerBinder;

if ((object?)sourceConstructor == null)
{
// The constructor is implicit. We need to get the binder for the body
// of the enclosing class.
CSharpSyntaxNode containerNode = constructor.GetNonNullSyntaxNode();
BinderFactory binderFactory = compilation.GetBinderFactory(containerNode.SyntaxTree);

if (containerNode is RecordDeclarationSyntax recordDecl)
{
outerBinder = binderFactory.GetInRecordBodyBinder(recordDecl);
}
else
{
SyntaxToken bodyToken = GetImplicitConstructorBodyToken(containerNode);
outerBinder = binderFactory.GetBinder(containerNode, bodyToken.Position);
}
}
else
{
BinderFactory binderFactory = compilation.GetBinderFactory(sourceConstructor.SyntaxTree);

switch (sourceConstructor.SyntaxNode)
{
case ConstructorDeclarationSyntax ctorDecl:
// We have a ctor in source but no explicit constructor initializer. We can't just use the binder for the
// type containing the ctor because the ctor might be marked unsafe. Use the binder for the parameter list
// as an approximation - the extra symbols won't matter because there are no identifiers to bind.

outerBinder = binderFactory.GetBinder(ctorDecl.ParameterList);
break;

case RecordDeclarationSyntax recordDecl:
outerBinder = binderFactory.GetInRecordBodyBinder(recordDecl);
break;

default:
throw ExceptionUtilities.Unreachable;
}
}

// wrap in ConstructorInitializerBinder for appropriate errors
// Handle scoping for possible pattern variables declared in the initializer
Binder initializerBinder = outerBinder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.ConstructorInitializer, constructor);

return initializerBinder.BindConstructorInitializer(null, constructor, diagnostics);
}

private static SyntaxToken GetImplicitConstructorBodyToken(CSharpSyntaxNode containerNode)
{
return ((BaseTypeDeclarationSyntax)containerNode).OpenBraceToken;
}

internal static BoundCall? GenerateBaseParameterlessConstructorInitializer(MethodSymbol constructor, BindingDiagnosticBag diagnostics)
{
NamedTypeSymbol baseType = constructor.ContainingType.BaseTypeNoUseSiteDiagnostics;
MethodSymbol? baseConstructor = null;
LookupResultKind resultKind = LookupResultKind.Viable;
Location diagnosticsLocation = constructor.Locations.IsEmpty ? NoLocation.Singleton : constructor.Locations[0];

foreach (MethodSymbol ctor in baseType.InstanceConstructors)
{
if (ctor.ParameterCount == 0)
{
baseConstructor = ctor;
break;
}
}

// UNDONE: If this happens then something is deeply wrong. Should we give a better error?
if ((object?)baseConstructor == null)
{
diagnostics.Add(ErrorCode.ERR_BadCtorArgCount, diagnosticsLocation, baseType, /*desired param count*/ 0);
return null;
}

if (Binder.ReportUseSite(baseConstructor, diagnostics, diagnosticsLocation))
{
return null;
}

// UNDONE: If this happens then something is deeply wrong. Should we give a better error?
bool hasErrors = false;
var useSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(diagnostics, constructor.ContainingAssembly);
if (!AccessCheck.IsSymbolAccessible(baseConstructor, constructor.ContainingType, ref useSiteInfo))
{
diagnostics.Add(ErrorCode.ERR_BadAccess, diagnosticsLocation, baseConstructor);
resultKind = LookupResultKind.Inaccessible;
hasErrors = true;
}

diagnostics.Add(diagnosticsLocation, useSiteInfo);

CSharpSyntaxNode syntax = constructor.GetNonNullSyntaxNode();

BoundExpression receiver = new BoundThisReference(syntax, constructor.ContainingType) { WasCompilerGenerated = true };
return new BoundCall(
syntax: syntax,
receiverOpt: receiver,
method: baseConstructor,
arguments: ImmutableArray<BoundExpression>.Empty,
argumentNamesOpt: ImmutableArray<string>.Empty,
argumentRefKindsOpt: ImmutableArray<RefKind>.Empty,
isDelegateCall: false,
expanded: false,
invokedAsExtensionMethod: false,
argsToParamsOpt: ImmutableArray<int>.Empty,
defaultArguments: BitVector.Empty,
resultKind: resultKind,
type: baseConstructor.ReturnType,
hasErrors: hasErrors)
{ WasCompilerGenerated = true };
}

private static BoundCall? GenerateBaseCopyConstructorInitializer(SynthesizedRecordCopyCtor constructor, BindingDiagnosticBag diagnostics)
{
NamedTypeSymbol containingType = constructor.ContainingType;
NamedTypeSymbol baseType = containingType.BaseTypeNoUseSiteDiagnostics;
Location diagnosticsLocation = constructor.Locations.FirstOrNone();

var useSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(diagnostics, containingType.ContainingAssembly);
MethodSymbol? baseConstructor = SynthesizedRecordCopyCtor.FindCopyConstructor(baseType, containingType, ref useSiteInfo);

if (baseConstructor is null)
{
diagnostics.Add(ErrorCode.ERR_NoCopyConstructorInBaseType, diagnosticsLocation, baseType);
return null;
}

var constructorUseSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(diagnostics, constructor.ContainingAssembly);
constructorUseSiteInfo.Add(baseConstructor.GetUseSiteInfo());
if (Binder.ReportConstructorUseSiteDiagnostics(diagnosticsLocation, diagnostics, suppressUnsupportedRequiredMembersError: constructor.HasSetsRequiredMembers, constructorUseSiteInfo))
{
return null;
}

diagnostics.Add(diagnosticsLocation, useSiteInfo);

CSharpSyntaxNode syntax = constructor.GetNonNullSyntaxNode();
BoundExpression receiver = new BoundThisReference(syntax, constructor.ContainingType) { WasCompilerGenerated = true };
BoundExpression argument = new BoundParameter(syntax, constructor.Parameters[0]);

return new BoundCall(
syntax: syntax,
receiverOpt: receiver,
method: baseConstructor,
arguments: ImmutableArray.Create(argument),
argumentNamesOpt: default,
argumentRefKindsOpt: default,
isDelegateCall: false,
expanded: false,
invokedAsExtensionMethod: false,
argsToParamsOpt: default,
defaultArguments: default,
resultKind: LookupResultKind.Viable,
type: baseConstructor.ReturnType,
hasErrors: false)
{ WasCompilerGenerated = true };
}
#nullable disable

private BoundNode BindMethodBody(CSharpSyntaxNode declaration, BlockSyntax blockBody, ArrowExpressionClauseSyntax expressionBody, BindingDiagnosticBag diagnostics)
{
if (blockBody == null && expressionBody == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,19 @@ public override BoundNode VisitAwaitableInfo(BoundAwaitableInfo node)
return null;
}

public override BoundNode VisitConstructorMethodBody(BoundConstructorMethodBody node)
{
// The implicit base call shares a syntax node with the constructor body, and we don't want to
// put it as the lower bound node as it will give incorrect results.
if (node.Syntax != node.Initializer?.Syntax)
{
this.Visit(node.Initializer);
}
this.Visit(node.BlockBody);
this.Visit(node.ExpressionBody);
return null;
}

public override BoundNode VisitBinaryOperator(BoundBinaryOperator node)
{
throw ExceptionUtilities.Unreachable;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState,

// explicit base constructor call
Debug.Assert(ContainingType.BaseTypeNoUseSiteDiagnostics.SpecialType == SpecialType.System_Object);
BoundExpression call = MethodCompiler.GenerateBaseParameterlessConstructorInitializer(this, diagnostics);
BoundExpression call = Binder.GenerateBaseParameterlessConstructorInitializer(this, diagnostics);
if (call == null)
{
// This may happen if Object..ctor is not found or is inaccessible
Expand Down
Loading