未验证 提交 89b4f606 编写于 作者: C Charles Stoner 提交者: GitHub

Allow conversion of collection initializer Add extension method this arg (#38732)

上级 b5d33e28
......@@ -478,6 +478,7 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin
return true;
case BoundKind.ImplicitReceiver:
case BoundKind.ObjectOrCollectionValuePlaceholder:
Debug.Assert(!RequiresRefAssignableVariable(valueKind));
return true;
......@@ -850,7 +851,7 @@ private bool CheckEventValueKind(BoundEventAccess boundEvent, BindValueKind valu
private bool CheckIsValidReceiverForVariable(SyntaxNode node, BoundExpression receiver, BindValueKind kind, DiagnosticBag diagnostics)
{
Debug.Assert(receiver != null);
return Flags.Includes(BinderFlags.ObjectInitializerMember) && receiver.Kind == BoundKind.ImplicitReceiver ||
return Flags.Includes(BinderFlags.ObjectInitializerMember) && receiver.Kind == BoundKind.ObjectOrCollectionValuePlaceholder ||
CheckValueKind(node, receiver, kind, true, diagnostics);
}
......@@ -2467,6 +2468,7 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin
return scopeOfTheContainingExpression;
case BoundKind.ImplicitReceiver:
case BoundKind.ObjectOrCollectionValuePlaceholder:
// binder uses this as a placeholder when binding members inside an object initializer
// just say it does not escape anywhere, so that we do not get false errors.
return scopeOfTheContainingExpression;
......
......@@ -4149,19 +4149,15 @@ private BoundExpression MakeBadExpressionForObjectCreation(ObjectCreationExpress
Debug.Assert(syntax != null);
Debug.Assert((object)type != null);
var implicitReceiver = new BoundObjectOrCollectionValuePlaceholder(typeSyntax, type) { WasCompilerGenerated = true };
switch (syntax.Kind())
{
case SyntaxKind.ObjectInitializerExpression:
{
var implicitReceiver = new BoundImplicitReceiver(typeSyntax, type) { WasCompilerGenerated = true };
return BindObjectInitializerExpression(syntax, type, diagnostics, implicitReceiver);
}
return BindObjectInitializerExpression(syntax, type, diagnostics, implicitReceiver);
case SyntaxKind.CollectionInitializerExpression:
{
var implicitReceiver = new BoundImplicitReceiver(typeSyntax, type) { WasCompilerGenerated = true };
return BindCollectionInitializerExpression(syntax, type, diagnostics, implicitReceiver);
}
return BindCollectionInitializerExpression(syntax, type, diagnostics, implicitReceiver);
default:
throw ExceptionUtilities.Unreachable;
......@@ -4191,7 +4187,7 @@ private BoundExpression MakeBadExpressionForObjectCreation(ObjectCreationExpress
InitializerExpressionSyntax initializerSyntax,
TypeSymbol initializerType,
DiagnosticBag diagnostics,
BoundImplicitReceiver implicitReceiver)
BoundObjectOrCollectionValuePlaceholder implicitReceiver)
{
// SPEC: 7.6.10.2 Object initializers
//
......@@ -4225,15 +4221,15 @@ private BoundExpression MakeBadExpressionForObjectCreation(ObjectCreationExpress
ReportDuplicateObjectMemberInitializers(boundMemberInitializer, memberNameMap, diagnostics);
}
return new BoundObjectInitializerExpression(initializerSyntax, initializerBuilder.ToImmutableAndFree(), initializerType);
return new BoundObjectInitializerExpression(initializerSyntax, implicitReceiver, initializerBuilder.ToImmutableAndFree(), initializerType);
}
private BoundExpression BindObjectInitializerMemberAssignment(
ExpressionSyntax memberInitializer,
TypeSymbol initializerType,
Binder objectInitializerMemberBinder,
DiagnosticBag diagnostics,
BoundImplicitReceiver implicitReceiver)
ExpressionSyntax memberInitializer,
TypeSymbol initializerType,
Binder objectInitializerMemberBinder,
DiagnosticBag diagnostics,
BoundObjectOrCollectionValuePlaceholder implicitReceiver)
{
// SPEC: A member initializer that specifies an expression after the equals sign is processed in the same way as an assignment (spec 7.17.1) to the field or property.
......@@ -4291,7 +4287,7 @@ private BoundExpression MakeBadExpressionForObjectCreation(ObjectCreationExpress
// returns BadBoundExpression or BoundObjectInitializerMember
private BoundExpression BindObjectInitializerMember(
AssignmentExpressionSyntax namedAssignment,
BoundImplicitReceiver implicitReceiver,
BoundObjectOrCollectionValuePlaceholder implicitReceiver,
DiagnosticBag diagnostics)
{
BoundExpression boundMember;
......@@ -4481,7 +4477,7 @@ private BoundExpression MakeBadExpressionForObjectCreation(ObjectCreationExpress
private BoundExpression BadObjectInitializerMemberAccess(
BoundExpression boundMember,
BoundImplicitReceiver implicitReceiver,
BoundObjectOrCollectionValuePlaceholder implicitReceiver,
ExpressionSyntax memberNameSyntax,
DiagnosticBag diagnostics,
BindValueKind valueKind,
......@@ -4552,7 +4548,7 @@ private static void ReportDuplicateObjectMemberInitializers(BoundExpression boun
InitializerExpressionSyntax initializerSyntax,
TypeSymbol initializerType,
DiagnosticBag diagnostics,
BoundImplicitReceiver implicitReceiver)
BoundObjectOrCollectionValuePlaceholder implicitReceiver)
{
// SPEC: 7.6.10.3 Collection initializers
//
......@@ -4599,7 +4595,7 @@ private static void ReportDuplicateObjectMemberInitializers(BoundExpression boun
initializerBuilder.Add(boundElementInitializer);
}
return new BoundCollectionInitializerExpression(initializerSyntax, initializerBuilder.ToImmutableAndFree(), initializerType);
return new BoundCollectionInitializerExpression(initializerSyntax, implicitReceiver, initializerBuilder.ToImmutableAndFree(), initializerType);
}
private bool CollectionInitializerTypeImplementsIEnumerable(TypeSymbol initializerType, CSharpSyntaxNode node, DiagnosticBag diagnostics)
......@@ -4637,7 +4633,7 @@ private bool CollectionInitializerTypeImplementsIEnumerable(TypeSymbol initializ
bool hasEnumerableInitializerType,
Binder collectionInitializerAddMethodBinder,
DiagnosticBag diagnostics,
BoundImplicitReceiver implicitReceiver)
BoundObjectOrCollectionValuePlaceholder implicitReceiver)
{
// SPEC: Each element initializer specifies an element to be added to the collection object being initialized, and consists of
// SPEC: a list of expressions enclosed by { and } tokens and separated by commas.
......@@ -4681,7 +4677,7 @@ private bool CollectionInitializerTypeImplementsIEnumerable(TypeSymbol initializ
DiagnosticBag diagnostics,
bool hasEnumerableInitializerType,
Binder collectionInitializerAddMethodBinder = null,
BoundImplicitReceiver implicitReceiver = null)
BoundObjectOrCollectionValuePlaceholder implicitReceiver = null)
{
var elementInitializerExpressions = elementInitializer.Expressions;
......@@ -4721,7 +4717,7 @@ private BoundExpression BindUnexpectedComplexElementInitializer(InitializerExpre
bool hasEnumerableInitializerType,
Binder collectionInitializerAddMethodBinder,
DiagnosticBag diagnostics,
BoundImplicitReceiver implicitReceiver)
BoundObjectOrCollectionValuePlaceholder implicitReceiver)
{
// SPEC: For each specified element in order, the collection initializer invokes an Add method on the target object
// SPEC: with the expression list of the element initializer as argument list, applying normal overload resolution for each invocation.
......
......@@ -111,6 +111,10 @@
<Node Name="BoundDisposableValuePlaceholder" Base="BoundValuePlaceholderBase">
</Node>
<!-- The implicit collection in an object or collection initializer expression. -->
<Node Name="BoundObjectOrCollectionValuePlaceholder" Base="BoundValuePlaceholderBase">
</Node>
<!-- only used by codegen -->
<Node Name="BoundDup" Base="BoundExpression">
<!-- when duplicating a local or parameter, must remember original ref kind -->
......@@ -1604,7 +1608,8 @@
<AbstractNode Name="BoundObjectInitializerExpressionBase" Base="BoundExpression">
<!-- Non-null type is required for this node kind -->
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
<!-- An expression placeholder value representing the initialized object or collection. -->
<Field Name="Placeholder" Type="BoundObjectOrCollectionValuePlaceholder" Null="disallow" />
<Field Name="Initializers" Type="ImmutableArray&lt;BoundExpression&gt;"/>
</AbstractNode>
......
......@@ -2,9 +2,7 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using System.Diagnostics;
namespace Microsoft.CodeAnalysis.CSharp
......@@ -35,15 +33,12 @@ public virtual TypeSymbol VisitType(TypeSymbol type)
System.Diagnostics.Debug.Assert(item != null);
var visited = this.Visit(item);
if (item != visited)
if (newList == null && item != visited)
{
if (newList == null)
newList = ArrayBuilder<T>.GetInstance();
if (i > 0)
{
newList = ArrayBuilder<T>.GetInstance();
if (i > 0)
{
newList.AddRange(list, i);
}
newList.AddRange(list, i);
}
}
......
......@@ -2858,6 +2858,11 @@ public override BoundNode VisitDeconstructValuePlaceholder(BoundDeconstructValue
return null;
}
public override BoundNode VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node)
{
return null;
}
public override sealed BoundNode VisitOutVariablePendingInference(OutVariablePendingInference node)
{
throw ExceptionUtilities.Unreachable;
......
......@@ -1819,11 +1819,11 @@ public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpre
private void VisitObjectCreationInitializer(Symbol containingSymbol, int containingSlot, BoundExpression node)
{
TakeIncrementalSnapshot(node);
switch (node.Kind)
switch (node)
{
case BoundKind.ObjectInitializerExpression:
case BoundObjectInitializerExpression objectInitializer:
checkImplicitReceiver();
foreach (var initializer in ((BoundObjectInitializerExpression)node).Initializers)
foreach (var initializer in objectInitializer.Initializers)
{
switch (initializer.Kind)
{
......@@ -1835,10 +1835,11 @@ private void VisitObjectCreationInitializer(Symbol containingSymbol, int contain
break;
}
}
SetNotNullResult(objectInitializer.Placeholder);
break;
case BoundKind.CollectionInitializerExpression:
case BoundCollectionInitializerExpression collectionInitializer:
checkImplicitReceiver();
foreach (var initializer in ((BoundCollectionInitializerExpression)node).Initializers)
foreach (var initializer in collectionInitializer.Initializers)
{
switch (initializer.Kind)
{
......@@ -1850,6 +1851,7 @@ private void VisitObjectCreationInitializer(Symbol containingSymbol, int contain
break;
}
}
SetNotNullResult(collectionInitializer.Placeholder);
break;
default:
Debug.Assert((object)containingSymbol != null);
......@@ -1915,7 +1917,7 @@ private new void VisitCollectionElementInitializer(BoundCollectionElementInitial
(var reinferredMethod, _, _) = VisitArguments(node, node.Arguments, refKindsOpt: default, node.AddMethod, node.ArgsToParamsOpt, node.Expanded, node.InvokedAsExtensionMethod);
if (node.ImplicitReceiverOpt != null)
{
Debug.Assert(node.ImplicitReceiverOpt.Kind == BoundKind.ImplicitReceiver);
Debug.Assert(node.ImplicitReceiverOpt.Kind == BoundKind.ObjectOrCollectionValuePlaceholder);
SetAnalyzedNullability(node.ImplicitReceiverOpt, new VisitResult(node.ImplicitReceiverOpt.Type, NullableAnnotation.NotAnnotated, NullableFlowState.NotNull));
}
SetUnknownResultNullability(node);
......@@ -7688,6 +7690,12 @@ public override BoundNode VisitDeconstructValuePlaceholder(BoundDeconstructValue
return null;
}
public override BoundNode VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node)
{
SetNotNullResult(node);
return null;
}
protected override string Dump(LocalState state)
{
if (!state.Reachable)
......
......@@ -29,6 +29,7 @@ internal enum BoundKind: byte
TupleOperandPlaceholder,
AwaitableValuePlaceholder,
DisposableValuePlaceholder,
ObjectOrCollectionValuePlaceholder,
Dup,
PassByCopy,
BadExpression,
......@@ -548,6 +549,39 @@ public BoundDisposableValuePlaceholder Update(TypeSymbol type)
}
}
internal sealed partial class BoundObjectOrCollectionValuePlaceholder : BoundValuePlaceholderBase
{
public BoundObjectOrCollectionValuePlaceholder(SyntaxNode syntax, TypeSymbol type, bool hasErrors)
: base(BoundKind.ObjectOrCollectionValuePlaceholder, syntax, type, hasErrors)
{
Debug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)");
}
public BoundObjectOrCollectionValuePlaceholder(SyntaxNode syntax, TypeSymbol type)
: base(BoundKind.ObjectOrCollectionValuePlaceholder, syntax, type)
{
Debug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)");
}
[DebuggerStepThrough]
public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitObjectOrCollectionValuePlaceholder(this);
public BoundObjectOrCollectionValuePlaceholder Update(TypeSymbol type)
{
if (!TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything))
{
var result = new BoundObjectOrCollectionValuePlaceholder(this.Syntax, type, this.HasErrors);
result.CopyAttributes(this);
return result;
}
return this;
}
}
internal sealed partial class BoundDup : BoundExpression
{
public BoundDup(SyntaxNode syntax, RefKind refKind, TypeSymbol? type, bool hasErrors)
......@@ -5626,28 +5660,33 @@ public BoundNoPiaObjectCreationExpression Update(string? guidString, BoundObject
internal abstract partial class BoundObjectInitializerExpressionBase : BoundExpression
{
protected BoundObjectInitializerExpressionBase(BoundKind kind, SyntaxNode syntax, ImmutableArray<BoundExpression> initializers, TypeSymbol type, bool hasErrors = false)
protected BoundObjectInitializerExpressionBase(BoundKind kind, SyntaxNode syntax, BoundObjectOrCollectionValuePlaceholder placeholder, ImmutableArray<BoundExpression> initializers, TypeSymbol type, bool hasErrors = false)
: base(kind, syntax, type, hasErrors)
{
Debug.Assert(placeholder is object, "Field 'placeholder' cannot be null (make the type nullable in BoundNodes.xml to remove this check)");
Debug.Assert(!initializers.IsDefault, "Field 'initializers' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)");
this.Placeholder = placeholder;
this.Initializers = initializers;
}
public new TypeSymbol Type => base.Type!;
public BoundObjectOrCollectionValuePlaceholder Placeholder { get; }
public ImmutableArray<BoundExpression> Initializers { get; }
}
internal sealed partial class BoundObjectInitializerExpression : BoundObjectInitializerExpressionBase
{
public BoundObjectInitializerExpression(SyntaxNode syntax, ImmutableArray<BoundExpression> initializers, TypeSymbol type, bool hasErrors = false)
: base(BoundKind.ObjectInitializerExpression, syntax, initializers, type, hasErrors || initializers.HasErrors())
public BoundObjectInitializerExpression(SyntaxNode syntax, BoundObjectOrCollectionValuePlaceholder placeholder, ImmutableArray<BoundExpression> initializers, TypeSymbol type, bool hasErrors = false)
: base(BoundKind.ObjectInitializerExpression, syntax, placeholder, initializers, type, hasErrors || placeholder.HasErrors() || initializers.HasErrors())
{
Debug.Assert(placeholder is object, "Field 'placeholder' cannot be null (make the type nullable in BoundNodes.xml to remove this check)");
Debug.Assert(!initializers.IsDefault, "Field 'initializers' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)");
......@@ -5656,11 +5695,11 @@ public BoundObjectInitializerExpression(SyntaxNode syntax, ImmutableArray<BoundE
[DebuggerStepThrough]
public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitObjectInitializerExpression(this);
public BoundObjectInitializerExpression Update(ImmutableArray<BoundExpression> initializers, TypeSymbol type)
public BoundObjectInitializerExpression Update(BoundObjectOrCollectionValuePlaceholder placeholder, ImmutableArray<BoundExpression> initializers, TypeSymbol type)
{
if (initializers != this.Initializers || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything))
if (placeholder != this.Placeholder || initializers != this.Initializers || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything))
{
var result = new BoundObjectInitializerExpression(this.Syntax, initializers, type, this.HasErrors);
var result = new BoundObjectInitializerExpression(this.Syntax, placeholder, initializers, type, this.HasErrors);
result.CopyAttributes(this);
return result;
}
......@@ -5774,10 +5813,11 @@ public BoundDynamicObjectInitializerMember Update(string memberName, TypeSymbol
internal sealed partial class BoundCollectionInitializerExpression : BoundObjectInitializerExpressionBase
{
public BoundCollectionInitializerExpression(SyntaxNode syntax, ImmutableArray<BoundExpression> initializers, TypeSymbol type, bool hasErrors = false)
: base(BoundKind.CollectionInitializerExpression, syntax, initializers, type, hasErrors || initializers.HasErrors())
public BoundCollectionInitializerExpression(SyntaxNode syntax, BoundObjectOrCollectionValuePlaceholder placeholder, ImmutableArray<BoundExpression> initializers, TypeSymbol type, bool hasErrors = false)
: base(BoundKind.CollectionInitializerExpression, syntax, placeholder, initializers, type, hasErrors || placeholder.HasErrors() || initializers.HasErrors())
{
Debug.Assert(placeholder is object, "Field 'placeholder' cannot be null (make the type nullable in BoundNodes.xml to remove this check)");
Debug.Assert(!initializers.IsDefault, "Field 'initializers' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)");
......@@ -5786,11 +5826,11 @@ public BoundCollectionInitializerExpression(SyntaxNode syntax, ImmutableArray<Bo
[DebuggerStepThrough]
public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitCollectionInitializerExpression(this);
public BoundCollectionInitializerExpression Update(ImmutableArray<BoundExpression> initializers, TypeSymbol type)
public BoundCollectionInitializerExpression Update(BoundObjectOrCollectionValuePlaceholder placeholder, ImmutableArray<BoundExpression> initializers, TypeSymbol type)
{
if (initializers != this.Initializers || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything))
if (placeholder != this.Placeholder || initializers != this.Initializers || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything))
{
var result = new BoundCollectionInitializerExpression(this.Syntax, initializers, type, this.HasErrors);
var result = new BoundCollectionInitializerExpression(this.Syntax, placeholder, initializers, type, this.HasErrors);
result.CopyAttributes(this);
return result;
}
......@@ -7331,6 +7371,8 @@ internal R VisitInternal(BoundNode node, A arg)
return VisitAwaitableValuePlaceholder((BoundAwaitableValuePlaceholder)node, arg);
case BoundKind.DisposableValuePlaceholder:
return VisitDisposableValuePlaceholder((BoundDisposableValuePlaceholder)node, arg);
case BoundKind.ObjectOrCollectionValuePlaceholder:
return VisitObjectOrCollectionValuePlaceholder((BoundObjectOrCollectionValuePlaceholder)node, arg);
case BoundKind.Dup:
return VisitDup((BoundDup)node, arg);
case BoundKind.PassByCopy:
......@@ -7701,6 +7743,7 @@ internal abstract partial class BoundTreeVisitor<A,R>
public virtual R VisitTupleOperandPlaceholder(BoundTupleOperandPlaceholder node, A arg) => this.DefaultVisit(node, arg);
public virtual R VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node, A arg) => this.DefaultVisit(node, arg);
public virtual R VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node, A arg) => this.DefaultVisit(node, arg);
public virtual R VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node, A arg) => this.DefaultVisit(node, arg);
public virtual R VisitDup(BoundDup node, A arg) => this.DefaultVisit(node, arg);
public virtual R VisitPassByCopy(BoundPassByCopy node, A arg) => this.DefaultVisit(node, arg);
public virtual R VisitBadExpression(BoundBadExpression node, A arg) => this.DefaultVisit(node, arg);
......@@ -7890,6 +7933,7 @@ internal abstract partial class BoundTreeVisitor
public virtual BoundNode? VisitTupleOperandPlaceholder(BoundTupleOperandPlaceholder node) => this.DefaultVisit(node);
public virtual BoundNode? VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node) => this.DefaultVisit(node);
public virtual BoundNode? VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node) => this.DefaultVisit(node);
public virtual BoundNode? VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node) => this.DefaultVisit(node);
public virtual BoundNode? VisitDup(BoundDup node) => this.DefaultVisit(node);
public virtual BoundNode? VisitPassByCopy(BoundPassByCopy node) => this.DefaultVisit(node);
public virtual BoundNode? VisitBadExpression(BoundBadExpression node) => this.DefaultVisit(node);
......@@ -8095,6 +8139,7 @@ internal abstract partial class BoundTreeWalker: BoundTreeVisitor
public override BoundNode? VisitTupleOperandPlaceholder(BoundTupleOperandPlaceholder node) => null;
public override BoundNode? VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node) => null;
public override BoundNode? VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node) => null;
public override BoundNode? VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node) => null;
public override BoundNode? VisitDup(BoundDup node) => null;
public override BoundNode? VisitPassByCopy(BoundPassByCopy node)
{
......@@ -8703,6 +8748,7 @@ internal abstract partial class BoundTreeWalker: BoundTreeVisitor
}
public override BoundNode? VisitObjectInitializerExpression(BoundObjectInitializerExpression node)
{
this.Visit(node.Placeholder);
this.VisitList(node.Initializers);
return null;
}
......@@ -8714,6 +8760,7 @@ internal abstract partial class BoundTreeWalker: BoundTreeVisitor
public override BoundNode? VisitDynamicObjectInitializerMember(BoundDynamicObjectInitializerMember node) => null;
public override BoundNode? VisitCollectionInitializerExpression(BoundCollectionInitializerExpression node)
{
this.Visit(node.Placeholder);
this.VisitList(node.Initializers);
return null;
}
......@@ -8952,6 +8999,11 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor
TypeSymbol type = this.VisitType(node.Type);
return node.Update(type);
}
public override BoundNode? VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node)
{
TypeSymbol type = this.VisitType(node.Type);
return node.Update(type);
}
public override BoundNode? VisitDup(BoundDup node)
{
TypeSymbol type = this.VisitType(node.Type);
......@@ -9721,9 +9773,10 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor
}
public override BoundNode? VisitObjectInitializerExpression(BoundObjectInitializerExpression node)
{
BoundObjectOrCollectionValuePlaceholder placeholder = (BoundObjectOrCollectionValuePlaceholder)this.Visit(node.Placeholder);
ImmutableArray<BoundExpression> initializers = this.VisitList(node.Initializers);
TypeSymbol type = this.VisitType(node.Type);
return node.Update(initializers, type);
return node.Update(placeholder, initializers, type);
}
public override BoundNode? VisitObjectInitializerMember(BoundObjectInitializerMember node)
{
......@@ -9740,9 +9793,10 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor
}
public override BoundNode? VisitCollectionInitializerExpression(BoundCollectionInitializerExpression node)
{
BoundObjectOrCollectionValuePlaceholder placeholder = (BoundObjectOrCollectionValuePlaceholder)this.Visit(node.Placeholder);
ImmutableArray<BoundExpression> initializers = this.VisitList(node.Initializers);
TypeSymbol type = this.VisitType(node.Type);
return node.Update(initializers, type);
return node.Update(placeholder, initializers, type);
}
public override BoundNode? VisitCollectionElementInitializer(BoundCollectionElementInitializer node)
{
......@@ -10078,6 +10132,18 @@ public NullabilityRewriter(ImmutableDictionary<BoundExpression, (NullabilityInfo
return updatedNode;
}
public override BoundNode? VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node)
{
if (!_updatedNullabilities.TryGetValue(node, out (NullabilityInfo Info, TypeSymbol Type) infoAndType))
{
return node;
}
BoundObjectOrCollectionValuePlaceholder updatedNode = node.Update(infoAndType.Type);
updatedNode.TopLevelNullability = infoAndType.Info;
return updatedNode;
}
public override BoundNode? VisitDup(BoundDup node)
{
if (!_updatedNullabilities.TryGetValue(node, out (NullabilityInfo Info, TypeSymbol Type) infoAndType))
......@@ -11347,17 +11413,18 @@ public NullabilityRewriter(ImmutableDictionary<BoundExpression, (NullabilityInfo
public override BoundNode? VisitObjectInitializerExpression(BoundObjectInitializerExpression node)
{
BoundObjectOrCollectionValuePlaceholder placeholder = (BoundObjectOrCollectionValuePlaceholder)this.Visit(node.Placeholder);
ImmutableArray<BoundExpression> initializers = this.VisitList(node.Initializers);
BoundObjectInitializerExpression updatedNode;
if (_updatedNullabilities.TryGetValue(node, out (NullabilityInfo Info, TypeSymbol Type) infoAndType))
{
updatedNode = node.Update(initializers, infoAndType.Type);
updatedNode = node.Update(placeholder, initializers, infoAndType.Type);
updatedNode.TopLevelNullability = infoAndType.Info;
}
else
{
updatedNode = node.Update(initializers, node.Type);
updatedNode = node.Update(placeholder, initializers, node.Type);
}
return updatedNode;
}
......@@ -11393,17 +11460,18 @@ public NullabilityRewriter(ImmutableDictionary<BoundExpression, (NullabilityInfo
public override BoundNode? VisitCollectionInitializerExpression(BoundCollectionInitializerExpression node)
{
BoundObjectOrCollectionValuePlaceholder placeholder = (BoundObjectOrCollectionValuePlaceholder)this.Visit(node.Placeholder);
ImmutableArray<BoundExpression> initializers = this.VisitList(node.Initializers);
BoundCollectionInitializerExpression updatedNode;
if (_updatedNullabilities.TryGetValue(node, out (NullabilityInfo Info, TypeSymbol Type) infoAndType))
{
updatedNode = node.Update(initializers, infoAndType.Type);
updatedNode = node.Update(placeholder, initializers, infoAndType.Type);
updatedNode.TopLevelNullability = infoAndType.Info;
}
else
{
updatedNode = node.Update(initializers, node.Type);
updatedNode = node.Update(placeholder, initializers, node.Type);
}
return updatedNode;
}
......@@ -12027,6 +12095,13 @@ private BoundTreeDumperNodeProducer()
new TreeDumperNode("hasErrors", node.HasErrors, null)
}
);
public override TreeDumperNode VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node, object? arg) => new TreeDumperNode("objectOrCollectionValuePlaceholder", null, new TreeDumperNode[]
{
new TreeDumperNode("type", node.Type, null),
new TreeDumperNode("isSuppressed", node.IsSuppressed, null),
new TreeDumperNode("hasErrors", node.HasErrors, null)
}
);
public override TreeDumperNode VisitDup(BoundDup node, object? arg) => new TreeDumperNode("dup", null, new TreeDumperNode[]
{
new TreeDumperNode("refKind", node.RefKind, null),
......@@ -13236,6 +13311,7 @@ public override TreeDumperNode VisitYieldBreakStatement(BoundYieldBreakStatement
);
public override TreeDumperNode VisitObjectInitializerExpression(BoundObjectInitializerExpression node, object? arg) => new TreeDumperNode("objectInitializerExpression", null, new TreeDumperNode[]
{
new TreeDumperNode("placeholder", null, new TreeDumperNode[] { Visit(node.Placeholder, null) }),
new TreeDumperNode("initializers", null, from x in node.Initializers select Visit(x, null)),
new TreeDumperNode("type", node.Type, null),
new TreeDumperNode("isSuppressed", node.IsSuppressed, null),
......@@ -13269,6 +13345,7 @@ public override TreeDumperNode VisitYieldBreakStatement(BoundYieldBreakStatement
);
public override TreeDumperNode VisitCollectionInitializerExpression(BoundCollectionInitializerExpression node, object? arg) => new TreeDumperNode("collectionInitializerExpression", null, new TreeDumperNode[]
{
new TreeDumperNode("placeholder", null, new TreeDumperNode[] { Visit(node.Placeholder, null) }),
new TreeDumperNode("initializers", null, from x in node.Initializers select Visit(x, null)),
new TreeDumperNode("type", node.Type, null),
new TreeDumperNode("isSuppressed", node.IsSuppressed, null),
......
......@@ -202,7 +202,10 @@ private BoundExpression VisitExpressionImpl(BoundExpression node)
visited.Type.Equals(node.Type, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes) ||
IsUnusedDeconstruction(node));
if (visited != null && visited != node)
if (visited != null &&
visited != node &&
node.Kind != BoundKind.ImplicitReceiver &&
node.Kind != BoundKind.ObjectOrCollectionValuePlaceholder)
{
if (!CanBePassedByReference(node) && CanBePassedByReference(visited))
{
......@@ -294,6 +297,16 @@ public override BoundNode VisitDeconstructValuePlaceholder(BoundDeconstructValue
return PlaceholderReplacement(node);
}
public override BoundNode VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node)
{
if (_inExpressionLambda)
{
// Expression trees do not include the 'this' argument for members.
return node;
}
return PlaceholderReplacement(node);
}
/// <summary>
/// Returns substitution currently used by the rewriter for a placeholder node.
/// Each occurrence of the placeholder node is replaced with the node returned.
......
......@@ -16,13 +16,14 @@ internal sealed partial class LocalRewriter
{
private static BoundObjectInitializerExpressionBase UpdateInitializers(BoundObjectInitializerExpressionBase initializerExpression, ImmutableArray<BoundExpression> newInitializers)
{
if (initializerExpression.Kind == BoundKind.ObjectInitializerExpression)
switch (initializerExpression)
{
return ((BoundObjectInitializerExpression)initializerExpression).Update(newInitializers, initializerExpression.Type);
}
else
{
return ((BoundCollectionInitializerExpression)initializerExpression).Update(newInitializers, initializerExpression.Type);
case BoundObjectInitializerExpression objectInitializer:
return objectInitializer.Update(objectInitializer.Placeholder, newInitializers, initializerExpression.Type);
case BoundCollectionInitializerExpression collectionInitializer:
return collectionInitializer.Update(collectionInitializer.Placeholder, newInitializers, initializerExpression.Type);
default:
throw ExceptionUtilities.UnexpectedValue(initializerExpression.Kind);
}
}
......@@ -36,14 +37,24 @@ private static BoundObjectInitializerExpressionBase UpdateInitializers(BoundObje
Debug.Assert(!_inExpressionLambda);
Debug.Assert(rewrittenReceiver != null);
switch (initializerExpression.Kind)
switch (initializerExpression)
{
case BoundKind.ObjectInitializerExpression:
AddObjectInitializers(ref dynamicSiteInitializers, ref temps, result, rewrittenReceiver, ((BoundObjectInitializerExpression)initializerExpression).Initializers);
case BoundObjectInitializerExpression objectInitializer:
{
var placeholder = objectInitializer.Placeholder;
AddPlaceholderReplacement(placeholder, rewrittenReceiver);
AddObjectInitializers(ref dynamicSiteInitializers, ref temps, result, rewrittenReceiver, objectInitializer.Initializers);
RemovePlaceholderReplacement(placeholder);
}
return;
case BoundKind.CollectionInitializerExpression:
AddCollectionInitializers(ref dynamicSiteInitializers, result, rewrittenReceiver, ((BoundCollectionInitializerExpression)initializerExpression).Initializers);
case BoundCollectionInitializerExpression collectionInitializer:
{
var placeholder = collectionInitializer.Placeholder;
AddPlaceholderReplacement(placeholder, rewrittenReceiver);
AddCollectionInitializers(ref dynamicSiteInitializers, result, rewrittenReceiver, collectionInitializer.Initializers);
RemovePlaceholderReplacement(placeholder);
}
return;
default:
......@@ -174,11 +185,9 @@ private BoundExpression MakeCollectionInitializer(BoundExpression rewrittenRecei
if (initializer.InvokedAsExtensionMethod)
{
// the add method was found as an extension method. Replace the implicit receiver (first argument) with the rewritten receiver.
Debug.Assert(addMethod.IsStatic && addMethod.IsExtensionMethod);
Debug.Assert(rewrittenArguments[0].Kind == BoundKind.ImplicitReceiver);
Debug.Assert(addMethod.IsStatic);
Debug.Assert(addMethod.IsExtensionMethod);
Debug.Assert(!_inExpressionLambda, "Expression trees do not support extension Add");
rewrittenArguments = rewrittenArguments.SetItem(0, rewrittenReceiver);
rewrittenReceiver = null;
}
......
......@@ -38,7 +38,8 @@ public IOperation Create(BoundNode boundNode)
// implicit receiver can be shared between multiple bound nodes.
// always return cloned one
if (boundNode.Kind == BoundKind.ImplicitReceiver)
if (boundNode.Kind == BoundKind.ImplicitReceiver ||
boundNode.Kind == BoundKind.ObjectOrCollectionValuePlaceholder)
{
return OperationCloner.CloneOperation(CreateInternal(boundNode));
}
......@@ -287,6 +288,8 @@ internal IOperation CreateInternal(BoundNode boundNode)
return CreateBoundSwitchExpressionArmOperation((BoundSwitchExpressionArm)boundNode);
case BoundKind.UsingLocalDeclarations:
return CreateUsingLocalDeclarationsOperation((BoundUsingLocalDeclarations)boundNode);
case BoundKind.ObjectOrCollectionValuePlaceholder:
return CreateCollectionValuePlaceholderOperation((BoundObjectOrCollectionValuePlaceholder)boundNode);
case BoundKind.Attribute:
case BoundKind.ArgList:
......@@ -652,7 +655,7 @@ internal IOperation CreateBoundDynamicInvocationExpressionReceiver(BoundNode rec
{
switch (receiver)
{
case BoundImplicitReceiver implicitReceiver:
case BoundObjectOrCollectionValuePlaceholder implicitReceiver:
return CreateBoundDynamicMemberAccessOperation(implicitReceiver, typeArgumentsOpt: ImmutableArray<TypeSymbol>.Empty, memberName: "Add",
implicitReceiver.Syntax, type: null, value: default, isImplicit: true);
......@@ -2092,5 +2095,15 @@ internal IOperation CreatePropertySubpatternMember(Symbol symbol, ITypeSymbol ma
return OperationFactory.CreateInvalidOperation(_semanticModel, nameSyntax, ImmutableArray<IOperation>.Empty, isImplicit);
}
}
private IInstanceReferenceOperation CreateCollectionValuePlaceholderOperation(BoundObjectOrCollectionValuePlaceholder placeholder)
{
InstanceReferenceKind referenceKind = InstanceReferenceKind.ImplicitReceiver;
SyntaxNode syntax = placeholder.Syntax;
ITypeSymbol type = placeholder.Type;
Optional<object> constantValue = ConvertToOptional(placeholder.ConstantValue);
bool isImplicit = placeholder.WasCompilerGenerated;
return new InstanceReferenceOperation(referenceKind, _semanticModel, syntax, type, constantValue, isImplicit);
}
}
}
......@@ -354,7 +354,7 @@ internal static ImmutableArray<BoundNode> CreateInvalidChildrenFromArgumentsExpr
&& (!receiverOpt.WasCompilerGenerated
|| (receiverOpt.Kind != BoundKind.ThisReference
&& receiverOpt.Kind != BoundKind.BaseReference
&& receiverOpt.Kind != BoundKind.ImplicitReceiver)))
&& receiverOpt.Kind != BoundKind.ObjectOrCollectionValuePlaceholder)))
{
builder.Add(receiverOpt);
}
......
// 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.Collections.Generic;
using System.IO;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
......@@ -692,9 +691,9 @@ public static void Main(string[] args)
new[] { source, ExpressionTestLibrary },
expectedOutput: @"k");
}
[WorkItem(544029, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544029")]
[Fact]
public void ArrayCreation()
{
var source =
......@@ -717,9 +716,9 @@ public static void Main(string[] args)
new[] { source, ExpressionTestLibrary },
expectedOutput: @"k");
}
[WorkItem(544030, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544030")]
[Fact]
public void ArrayInitialization()
{
var source =
......@@ -739,9 +738,9 @@ public static void Main(string[] args)
new[] { source, ExpressionTestLibrary },
expectedOutput: @"k");
}
[WorkItem(544112, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544112")]
[Fact]
public void CS0838ERR_ExpressionTreeContainsMultiDimensionalArrayInitializer()
{
var source =
......@@ -760,9 +759,9 @@ public static void Main(string[] args)
Diagnostic(ErrorCode.ERR_ExpressionTreeContainsMultiDimensionalArrayInitializer, "new[,] {{ i }}")
);
}
[WorkItem(544031, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544031")]
[Fact]
public void ArrayLength()
{
var source =
......@@ -785,9 +784,9 @@ public static void Main(string[] args)
new[] { source, ExpressionTestLibrary },
expectedOutput: @"k");
}
[WorkItem(544032, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544032")]
[Fact]
public void AsOperator()
{
var source =
......@@ -807,9 +806,9 @@ public static void Main(string[] args)
new[] { source, ExpressionTestLibrary },
expectedOutput: "k");
}
[WorkItem(544034, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544034")]
[Fact]
public void BaseReference()
{
var source =
......@@ -915,9 +914,9 @@ public static void Main(string[] args)
new[] { source, ExpressionTestLibrary },
expectedOutput: "k");
}
[WorkItem(544036, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544036")]
[Fact]
public void Subtract()
{
var source =
......@@ -970,9 +969,9 @@ public static void Main(string[] args)
new[] { source, ExpressionTestLibrary },
expectedOutput: "k");
}
[WorkItem(544037, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544037")]
[Fact]
public void Divide()
{
var source =
......@@ -1022,9 +1021,9 @@ public static void Main(string[] args)
new[] { source, ExpressionTestLibrary },
expectedOutput: "k");
}
[WorkItem(544038, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544038")]
[Fact]
public void Remainder()
{
var source =
......@@ -1074,9 +1073,9 @@ public static void Main(string[] args)
new[] { source, ExpressionTestLibrary },
expectedOutput: "k");
}
[WorkItem(544041, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544041")]
[Fact]
public void And()
{
var source =
......@@ -1126,9 +1125,9 @@ public static void Main(string[] args)
new[] { source, ExpressionTestLibrary },
expectedOutput: "k");
}
[WorkItem(544042, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544042")]
[Fact]
public void ExclusiveOr()
{
var source =
......@@ -1178,9 +1177,9 @@ public static void Main(string[] args)
new[] { source, ExpressionTestLibrary },
expectedOutput: "k");
}
[WorkItem(544043, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544043")]
[Fact]
public void BitwiseOr()
{
var source =
......@@ -1230,9 +1229,9 @@ public static void Main(string[] args)
new[] { source, ExpressionTestLibrary },
expectedOutput: "k");
}
[WorkItem(544039, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544039"), WorkItem(544040, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544040")]
[Fact]
public void MoreBinaryOperators()
{
var source =
......@@ -1276,9 +1275,9 @@ public static void Main(string[] args)
new[] { source, ExpressionTestLibrary },
expectedOutput: "k");
}
[WorkItem(544059, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544059")]
[Fact]
public void UnaryOperators()
{
var source =
......@@ -1325,8 +1324,8 @@ public static void Main(string[] args)
new[] { source, ExpressionTestLibrary },
expectedOutput: "k");
}
[Fact]
[Fact]
public void GrabBag01()
{
var source =
......@@ -1387,9 +1386,9 @@ public static void Main2<T>(string expected)
new[] { source, ExpressionTestLibrary },
expectedOutput: "123k");
}
[WorkItem(546147, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546147")]
[Fact]
public void DelegateInvoke()
{
var source =
......@@ -1411,8 +1410,8 @@ static void Main()
@"Invoke(MemberAccess(Constant(P+<>c__DisplayClass0_0 Type:P+<>c__DisplayClass0_0).f Type:System.Func`2[System.Int32,System.Int32])(Constant(12 Type:System.Int32)) Type:System.Int32)
() => Invoke(value(P+<>c__DisplayClass0_0).f, 12)");
}
[ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/mono/mono/issues/10838")]
[ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/mono/mono/issues/10838")]
public void GrabBag02()
{
var source =
......@@ -1510,8 +1509,8 @@ public static void Main(string[] args)
new[] { source, ExpressionTestLibrary },
expectedOutput: "k");
}
[Fact]
[Fact]
public void UnsafeExprTree()
{
var source =
......@@ -1539,9 +1538,9 @@ public unsafe static void Main(string[] args)
Diagnostic(ErrorCode.ERR_ExpressionTreeContainsPointerOp, "sizeof(S)")
);
}
[WorkItem(544044, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544044")]
[Fact]
public void CollectionInitialization()
{
var source =
......@@ -1561,9 +1560,9 @@ public static void Main(string[] args)
new[] { source, ExpressionTestLibrary },
expectedOutput: "k");
}
[WorkItem(544390, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544390")]
[Fact]
public void ObjectInitialization()
{
var source =
......@@ -1629,8 +1628,8 @@ public static void Main(string[] args)
new[] { source, ExpressionTestLibrary },
expectedOutput: expectedOutput);
}
[Fact]
[Fact]
public void Lambda()
{
var source =
......@@ -1658,9 +1657,9 @@ public static void Main(string[] args)
new[] { source, ExpressionTestLibrary },
expectedOutput: "k");
}
[WorkItem(544218, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544218")]
[Fact]
public void Linq()
{
var source =
......@@ -1683,8 +1682,8 @@ static void Main()
new[] { source },
expectedOutput: "s => s.SelectMany(x => s, (x, y) => new <>f__AnonymousType0`2(x = x, y = y)).OrderByDescending(<>h__TransparentIdentifier0 => <>h__TransparentIdentifier0.x).Select(<>h__TransparentIdentifier0 => <>h__TransparentIdentifier0.x)");
}
[Fact]
[Fact]
public void Enum()
{
var source =
......@@ -3379,7 +3378,6 @@ static void Main()
expectedOutput: TrimExpectedOutput(expectedOutput));
}
[WorkItem(544442, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544442")]
[WorkItem(4593, "https://github.com/dotnet/roslyn/issues/4593")]
[Fact]
......@@ -6195,7 +6193,7 @@ public static void Main(string[] args)
#region helpers
public string TrimExpectedOutput(string expectedOutput)
private static string TrimExpectedOutput(string expectedOutput)
{
char[] delimit = { '\n' };
string trimmedOutput = null;
......
// 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 Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.CSharp.UnitTests.Emit;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen
{
public partial class ObjectAndCollectionInitializerTests : EmitMetadataTestBase
public class ObjectAndCollectionInitializerTests : EmitMetadataTestBase
{
#region "Object Initializer Tests"
......@@ -3409,6 +3405,87 @@ static int Index
2
3");
}
[Fact]
[WorkItem(38726, "https://github.com/dotnet/roslyn/issues/38726")]
public void CollectionInitializerBoxingConversion_01()
{
var source =
@"using System;
using System.Collections;
interface IAppend
{
void Append(object o);
}
struct S1
{
internal S2 S2;
}
struct S2 : IEnumerable, IAppend
{
IEnumerator IEnumerable.GetEnumerator() => null;
void IAppend.Append(object o) { }
}
static class Program
{
static void Add(this IAppend x, object y)
{
x.Append(y);
Console.Write(y);
}
static void Main()
{
_ = new S2() { 1, 2 };
_ = new S1() { S2 = { 3, 4 } };
}
}";
var comp = CSharpTestBase.CreateCompilation(source, options: TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: "1234");
}
[Fact]
[WorkItem(38726, "https://github.com/dotnet/roslyn/issues/38726")]
public void CollectionInitializerBoxingConversion_02()
{
var source =
@"using System;
using System.Collections;
interface IAppend
{
void Append(object o);
}
struct S : IEnumerable, IAppend
{
IEnumerator IEnumerable.GetEnumerator() => null;
void IAppend.Append(object o) { }
}
static class Program
{
static void Add(this IAppend x, object y)
{
x.Append(y);
Console.Write(y);
}
static T F<T>() where T : IEnumerable, IAppend, new()
{
return new T() { 1, 2 };
}
static void Main()
{
_ = F<S>();
}
}";
var comp = CSharpTestBase.CreateCompilation(source, options: TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: "12");
}
#endregion
}
}
......@@ -1304,6 +1304,64 @@ void M()
VerifyOperationTreeAndDiagnosticsForTest<InitializerExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics);
}
[CompilerTrait(CompilerFeature.IOperation)]
[Fact]
public void ObjectCreationCollectionInitializerBoxingConversion()
{
string source = @"
using System.Collections;
struct S : IEnumerable
{
IEnumerator IEnumerable.GetEnumerator() => null;
}
static class Program
{
static void Add(this object x, int y) { }
static void Main()
{
_ = /*<bind>*/new S() { 1, 2 }/*</bind>*/;
}
}";
var expectedDiagnostics = DiagnosticDescription.None;
string expectedOperationTree = @"
IObjectCreationOperation (Constructor: S..ctor()) (OperationKind.ObjectCreation, Type: S) (Syntax: 'new S() { 1, 2 }')
Arguments(0)
Initializer:
IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: S) (Syntax: '{ 1, 2 }')
Initializers(2):
IInvocationOperation (void Program.Add(this System.Object x, System.Int32 y)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '1')
Instance Receiver:
null
Arguments(2):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'S')
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'S')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: S, IsImplicit) (Syntax: 'S')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: y) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '1')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
IInvocationOperation (void Program.Add(this System.Object x, System.Int32 y)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '2')
Instance Receiver:
null
Arguments(2):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'S')
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'S')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: S, IsImplicit) (Syntax: 'S')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: y) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '2')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)";
VerifyOperationTreeAndDiagnosticsForTest<ObjectCreationExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics);
}
[CompilerTrait(CompilerFeature.IOperation)]
[Fact]
public void ObjectCreationNestedObjectInitializerNoSet()
......
......@@ -2608,10 +2608,13 @@ class C
static void M()
{
var x = new C //-typeExpression: C
//-objectOrCollectionValuePlaceholder: C
{
A = //-objectInitializerMember: dynamic
//-objectOrCollectionValuePlaceholder: dynamic
{
B = //-dynamicObjectInitializerMember: dynamic
//-objectOrCollectionValuePlaceholder: dynamic
{
C = 3 //-dynamicObjectInitializerMember: dynamic
//-literal: int
......@@ -2650,11 +2653,12 @@ static void M()
{
var z = new C() //-typeExpression: C
{
{ d }, //-implicitReceiver: C
{ d }, //-objectOrCollectionValuePlaceholder: C
//-objectOrCollectionValuePlaceholder: C
//-fieldAccess: dynamic
//-dynamicCollectionElementInitializer: dynamic
{ d, d, d }, //-implicitReceiver: C
{ d, d, d }, //-objectOrCollectionValuePlaceholder: C
//-fieldAccess: dynamic
//-fieldAccess: dynamic
//-fieldAccess: dynamic
......
......@@ -116866,5 +116866,30 @@ static void F<T>() where T : class, new()
// T x = null; // 1
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(17, 15));
}
[Fact]
[WorkItem(38726, "https://github.com/dotnet/roslyn/issues/38726")]
public void CollectionInitializerBoxingConversion()
{
var source =
@"#nullable enable
using System.Collections;
struct S : IEnumerable
{
IEnumerator IEnumerable.GetEnumerator() => null!;
}
static class Program
{
static void Add(this object x, object y)
{
}
static T F<T>() where T : IEnumerable, new()
{
return new T() { 1, 2 };
}
}";
var comp = CreateCompilation(source);
comp.VerifyDiagnostics();
}
}
}
......@@ -385,7 +385,6 @@ public void CollectionInitializerTest_DynamicType()
string source = @"
using System;
using System.Collections.Generic;
using System.Collections;
class Test
{
......@@ -436,13 +435,8 @@ Type Arguments(0)
VerifyOperationTreeAndDiagnosticsForTest<ObjectCreationExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics);
// TODO: This should produce no diagnostics.
// The 'info' message is ONLY used for IDE (NOT show up in console)
CompileAndVerify(source, references: new MetadataReference[] { CSharpRef }).
VerifyDiagnostics(
// (4,1): info CS8019: Unnecessary using directive.
// using System.Collections;
Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System.Collections;"));
VerifyDiagnostics();
}
[CompilerTrait(CompilerFeature.IOperation)]
......
......@@ -118,8 +118,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
' even if T is known to be a value type or reference type. This matches Dev10 VB.
If _inExpressionLambda Then
' NOTE: is we are in expression lambda, we want to keep BoundNewT
' NOTE: node, but we need to rewrite initializers if any
' NOTE: If we are in expression lambda, we want to keep BoundNewT
' NOTE: node, but we need to rewrite initializers if any.
If node.InitializerOpt IsNot Nothing Then
Return VisitObjectCreationInitializer(node.InitializerOpt, node, node)
......
......@@ -855,6 +855,77 @@ End Class
}
]]>)
End Sub
<Fact>
Public Sub CollectionInitializerBoxingConversion_01()
Dim source =
"Imports System
Imports System.Collections
Imports System.Runtime.CompilerServices
Interface IAppend
Sub Append(o As Object)
End Interface
Structure S
Implements IEnumerable, IAppend
Private Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
Return Nothing
End Function
Private Sub Append(o As Object) Implements IAppend.Append
End Sub
End Structure
Module Program
<Extension>
Sub Add(x As IAppend, y As Object)
x.Append(y)
Console.Write(y)
End Sub
Sub Main()
Dim s = New S() From { 1, 2 }
End Sub
End Module"
Dim comp = CreateCompilation(source, options:=TestOptions.ReleaseExe)
CompileAndVerify(comp, expectedOutput:="12")
End Sub
<Fact>
Public Sub CollectionInitializerBoxingConversion_02()
Dim source =
"Imports System
Imports System.Collections
Imports System.Runtime.CompilerServices
Interface IAppend
Sub Append(o As Object)
End Interface
Structure S
Implements IEnumerable, IAppend
Private Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
Return Nothing
End Function
Private Sub Append(o As Object) Implements IAppend.Append
End Sub
End Structure
Module Program
<Extension>
Sub Add(x As IAppend, y As Object)
x.Append(y)
Console.Write(y)
End Sub
Function F(Of T As {IEnumerable, IAppend, New})() As T
Return New T() From { 1, 2 }
End Function
Sub Main()
Dim s = F(Of S)()
End Sub
End Module"
Dim comp = CreateCompilation(source, options:=TestOptions.ReleaseExe)
CompileAndVerify(comp, expectedOutput:="12")
End Sub
End Class
End Namespace
......@@ -1197,6 +1197,59 @@ IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitial
VerifyOperationTreeAndDiagnosticsForTest(Of ObjectCollectionInitializerSyntax)(source, expectedOperationTree, expectedDiagnostics)
End Sub
<CompilerTrait(CompilerFeature.IOperation)>
<Fact()>
Public Sub ObjectCreationCollectionInitializerBoxingConversion()
Dim source = <![CDATA[
Imports System.Collections
Imports System.Runtime.CompilerServices
Structure S
Implements IEnumerable
Private Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
Return Nothing
End Function
End Structure
Module Program
<Extension>
Sub Add(x As Object, y As Integer)
End Sub
Sub Main()
Dim s = New S() From { 1, 2 }'BIND:"New S() From { 1, 2 }"
End Sub
End Module]]>.Value
Dim expectedDiagnostics = String.Empty
Dim expectedOperationTree = <![CDATA[
IObjectCreationOperation (Constructor: Sub S..ctor()) (OperationKind.ObjectCreation, Type: S) (Syntax: 'New S() From { 1, 2 }')
Arguments(0)
Initializer:
IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: S) (Syntax: 'From { 1, 2 }')
Initializers(2):
IInvocationOperation ( Sub System.Object.Add(y As System.Int32)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '1')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'New S() From { 1, 2 }')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: S, IsImplicit) (Syntax: 'New S() From { 1, 2 }')
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: y) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '1')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
IInvocationOperation ( Sub System.Object.Add(y As System.Int32)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '2')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'New S() From { 1, 2 }')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: S, IsImplicit) (Syntax: 'New S() From { 1, 2 }')
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: y) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '2')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
]]>.Value
VerifyOperationTreeAndDiagnosticsForTest(Of ObjectCreationExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics)
End Sub
<CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)>
<Fact()>
Public Sub ObjectCreationFlow_01()
......
// 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.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.CodeAnalysis.Test.Utilities
{
......@@ -12,7 +8,7 @@ public static class TestExceptionUtilities
{
public static InvalidOperationException UnexpectedValue(object o)
{
string output = String.Format("Unexpected value '{0}' of type '{1}'", o, (o != null) ? o.GetType().FullName : "<unknown>");
string output = string.Format("Unexpected value '{0}' of type '{1}'", o, (o != null) ? o.GetType().FullName : "<unknown>");
System.Diagnostics.Debug.Fail(output);
return new InvalidOperationException(output);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册