提交 8ba62819 编写于 作者: M Manish Vasani

Merge branch 'UnifyLoops' of github.com:mavasani/roslyn into UnifyLoops

......@@ -139,7 +139,7 @@ private bool IsDebugPlus()
private bool EnablePEVerifyCompat()
{
return _module.Compilation.FeaturePEVerifyCompatEnabled;
return _module.Compilation.LanguageVersion < LanguageVersion.CSharp7_2 || _module.Compilation.FeaturePEVerifyCompatEnabled;
}
private LocalDefinition LazyReturnTemp
......
......@@ -24,8 +24,14 @@ private enum AddressKind
// reference itself will not be written to, nor it will be used to modify fields.
ReadOnly,
// same as ReadOnly, but we are not supposed to get a reference to a clone
// regardless of compat settings.
ReadOnlyStrict,
}
private static bool IsReadOnly(AddressKind addressKind) => addressKind >= AddressKind.ReadOnly;
/// <summary>
/// Emits address as in &amp;
///
......@@ -70,7 +76,7 @@ private LocalDefinition EmitAddress(BoundExpression expression, AddressKind addr
case BoundKind.ThisReference:
Debug.Assert(expression.Type.IsValueType, "only value types may need a ref to this");
Debug.Assert(HasHome(expression, addressKind == AddressKind.Writeable));
Debug.Assert(HasHome(expression, addressKind));
_builder.EmitOpCode(ILOpCode.Ldarg_0);
break;
......@@ -101,7 +107,7 @@ private LocalDefinition EmitAddress(BoundExpression expression, AddressKind addr
var methodRefKind = call.Method.RefKind;
if (methodRefKind == RefKind.Ref ||
(addressKind == AddressKind.ReadOnly && methodRefKind == RefKind.RefReadOnly))
(IsReadOnly(addressKind) && methodRefKind == RefKind.RefReadOnly))
{
EmitCallExpression(call, UseKind.UsedAsAddress);
break;
......@@ -121,7 +127,7 @@ private LocalDefinition EmitAddress(BoundExpression expression, AddressKind addr
case BoundKind.ConditionalOperator:
var conditional = (BoundConditionalOperator)expression;
if (!HasHome(conditional, addressKind != AddressKind.ReadOnly))
if (!HasHome(conditional, addressKind))
{
goto default;
}
......@@ -144,7 +150,7 @@ private LocalDefinition EmitAddress(BoundExpression expression, AddressKind addr
return null;
default:
Debug.Assert(!HasHome(expression, addressKind != AddressKind.ReadOnly));
Debug.Assert(!HasHome(expression, addressKind));
return EmitAddressOfTempClone(expression);
}
......@@ -218,7 +224,7 @@ private LocalDefinition EmitLocalAddress(BoundLocal localAccess, AddressKind add
{
var local = localAccess.LocalSymbol;
if (!HasHome(localAccess, needWriteable: addressKind != AddressKind.ReadOnly))
if (!HasHome(localAccess, addressKind))
{
return EmitAddressOfTempClone(localAccess);
}
......@@ -249,7 +255,7 @@ private LocalDefinition EmitLocalAddress(BoundLocal localAccess, AddressKind add
/// </summary>
private LocalDefinition EmitDupAddress(BoundDup dup, AddressKind addressKind)
{
if (!HasHome(dup, needWriteable: addressKind != AddressKind.ReadOnly))
if (!HasHome(dup, addressKind))
{
return EmitAddressOfTempClone(dup);
}
......@@ -339,7 +345,7 @@ private LocalSymbol DigForValueLocal(BoundSequence topSequence, BoundExpression
/// Checks if expression directly or indirectly represents a value with its own home. In
/// such cases it is possible to get a reference without loading into a temporary.
/// </summary>
private bool HasHome(BoundExpression expression, bool needWriteable)
private bool HasHome(BoundExpression expression, AddressKind addressKind)
{
switch (expression.Kind)
{
......@@ -352,10 +358,11 @@ private bool HasHome(BoundExpression expression, bool needWriteable)
case BoundKind.ThisReference:
Debug.Assert(expression.Type.IsValueType);
if (needWriteable && expression.Type.IsReadOnly)
if (!IsReadOnly(addressKind) && expression.Type.IsReadOnly)
{
return _method.MethodKind == MethodKind.Constructor;
}
return true;
case BoundKind.ThrowExpression:
......@@ -363,7 +370,7 @@ private bool HasHome(BoundExpression expression, bool needWriteable)
return true;
case BoundKind.Parameter:
return !needWriteable ||
return IsReadOnly(addressKind) ||
((BoundParameter)expression).ParameterSymbol.RefKind != RefKind.In;
case BoundKind.Local:
......@@ -371,31 +378,31 @@ private bool HasHome(BoundExpression expression, bool needWriteable)
// locals in a mutating call
var local = ((BoundLocal)expression).LocalSymbol;
return !((IsStackLocal(local) && local.RefKind == RefKind.None) ||
(needWriteable && local.RefKind == RefKind.RefReadOnly));
(!IsReadOnly(addressKind) && local.RefKind == RefKind.RefReadOnly));
case BoundKind.Call:
var methodRefKind = ((BoundCall)expression).Method.RefKind;
return methodRefKind == RefKind.Ref ||
(!needWriteable && methodRefKind == RefKind.RefReadOnly);
(IsReadOnly(addressKind) && methodRefKind == RefKind.RefReadOnly);
case BoundKind.Dup:
//NB: Dup represents locals that do not need IL slot
var dupRefKind = ((BoundDup)expression).RefKind;
return dupRefKind == RefKind.Ref ||
(!needWriteable && dupRefKind == RefKind.RefReadOnly);
(IsReadOnly(addressKind) && dupRefKind == RefKind.RefReadOnly);
case BoundKind.FieldAccess:
return HasHome((BoundFieldAccess)expression, needWriteable);
return HasHome((BoundFieldAccess)expression, addressKind);
case BoundKind.Sequence:
return HasHome(((BoundSequence)expression).Value, needWriteable);
return HasHome(((BoundSequence)expression).Value, addressKind);
case BoundKind.AssignmentOperator:
return ((BoundAssignmentOperator)expression).RefKind != RefKind.None;
case BoundKind.ComplexConditionalReceiver:
Debug.Assert(HasHome(((BoundComplexConditionalReceiver)expression).ValueTypeReceiver, needWriteable));
Debug.Assert(HasHome(((BoundComplexConditionalReceiver)expression).ReferenceTypeReceiver, needWriteable));
Debug.Assert(HasHome(((BoundComplexConditionalReceiver)expression).ValueTypeReceiver, addressKind));
Debug.Assert(HasHome(((BoundComplexConditionalReceiver)expression).ReferenceTypeReceiver, addressKind));
goto case BoundKind.ConditionalReceiver;
case BoundKind.ConditionalReceiver:
......@@ -415,7 +422,7 @@ private bool HasHome(BoundExpression expression, bool needWriteable)
// branch that has no home will need a temporary
// if both have no home, just say whole expression has no home
// so we could just use one temp for the whole thing
return HasHome(ternary.Consequence, needWriteable) && HasHome(ternary.Alternative, needWriteable);
return HasHome(ternary.Consequence, addressKind) && HasHome(ternary.Alternative, addressKind);
default:
return false;
......@@ -427,7 +434,7 @@ private bool HasHome(BoundExpression expression, bool needWriteable)
/// Fields have readable homes when they are not constants.
/// Fields have writeable homes unless they are readonly and used outside of the constructor.
/// </summary>
private bool HasHome(BoundFieldAccess fieldAccess, bool needWriteable)
private bool HasHome(BoundFieldAccess fieldAccess, AddressKind addressKind)
{
FieldSymbol field = fieldAccess.FieldSymbol;
......@@ -437,7 +444,14 @@ private bool HasHome(BoundFieldAccess fieldAccess, bool needWriteable)
return false;
}
if (!needWriteable && !EnablePEVerifyCompat())
// in readonly situations where ref to a copy is not allowed, consider fields as addressable
if (addressKind == AddressKind.ReadOnlyStrict)
{
return true;
}
// ReadOnly references can always be taken unless we are in peverify compat mode
if (addressKind == AddressKind.ReadOnly && !EnablePEVerifyCompat())
{
return true;
}
......@@ -456,7 +470,7 @@ private bool HasHome(BoundFieldAccess fieldAccess, bool needWriteable)
// we would not be able to dig for the inner field using references and the outer struct will have to be copied to a temp anyways.
if (!EnablePEVerifyCompat())
{
Debug.Assert(needWriteable == true);
Debug.Assert(!IsReadOnly(addressKind));
var receiver = fieldAccess.ReceiverOpt;
if (receiver?.Type.IsValueType == true)
......@@ -466,8 +480,8 @@ private bool HasHome(BoundFieldAccess fieldAccess, bool needWriteable)
// has readable home -> return false - we need to copy the field
// otherwise -> return true - the copy will be made at higher level so the leaf field can have writeable home
return HasHome(receiver, needWriteable: true) ||
!HasHome(receiver, needWriteable: false);
return HasHome(receiver, addressKind) ||
!HasHome(receiver, AddressKind.ReadOnly);
}
}
......@@ -534,7 +548,7 @@ private LocalDefinition EmitFieldAddress(BoundFieldAccess fieldAccess, AddressKi
{
FieldSymbol field = fieldAccess.FieldSymbol;
if (!HasHome(fieldAccess, addressKind != AddressKind.ReadOnly))
if (!HasHome(fieldAccess, addressKind))
{
// accessing a field that is not writable (const or readonly)
return EmitAddressOfTempClone(fieldAccess);
......@@ -546,11 +560,7 @@ private LocalDefinition EmitFieldAddress(BoundFieldAccess fieldAccess, AddressKi
}
else
{
//NOTE: we are not propagating AddressKind here.
// the reason is that while Constrained permits calls, it does not permit
// taking field addresses, so we have to turn Constrained into writeable.
// It is less error prone to just pass a bool "isReadonly"
return EmitInstanceFieldAddress(fieldAccess, isReadonly: addressKind == AddressKind.ReadOnly);
return EmitInstanceFieldAddress(fieldAccess, addressKind);
}
}
......@@ -564,7 +574,7 @@ private LocalDefinition EmitParameterAddress(BoundParameter parameter, AddressKi
{
ParameterSymbol parameterSymbol = parameter.ParameterSymbol;
if (!HasHome(parameter, needWriteable: addressKind != AddressKind.ReadOnly))
if (!HasHome(parameter, addressKind))
{
// accessing a parameter that is not writable
return EmitAddressOfTempClone(parameter);
......@@ -634,11 +644,14 @@ private LocalDefinition EmitReceiverRef(BoundExpression receiver, AddressKind ad
/// <summary>
/// May introduce a temp which it will return. (otherwise returns null)
/// </summary>
private LocalDefinition EmitInstanceFieldAddress(BoundFieldAccess fieldAccess, bool isReadonly)
private LocalDefinition EmitInstanceFieldAddress(BoundFieldAccess fieldAccess, AddressKind addressKind)
{
var field = fieldAccess.FieldSymbol;
var tempOpt = EmitReceiverRef(fieldAccess.ReceiverOpt, isReadonly? AddressKind.ReadOnly: AddressKind.Writeable);
//NOTE: we are not propagating AddressKind.Constrained here.
// the reason is that while Constrained permits calls, it does not permit
// taking field addresses, so we have to turn Constrained into writeable.
var tempOpt = EmitReceiverRef(fieldAccess.ReceiverOpt, addressKind == AddressKind.Constrained ? AddressKind.Writeable : addressKind);
_builder.EmitOpCode(ILOpCode.Ldflda);
EmitSymbolToken(field, fieldAccess.Syntax);
......
......@@ -426,10 +426,7 @@ private void EmitLoweredConditionalAccessExpression(BoundLoweredConditionalAcces
{
// this does not need to be writeable
// we may call "HasValue" on this, but it is not mutating
// (however verification does not know that and considers all calls mutating)
var addressKind = EnablePEVerifyCompat()?
AddressKind.Writeable:
AddressKind.ReadOnly;
var addressKind = AddressKind.ReadOnly;
receiverTemp = EmitReceiverRef(receiver, addressKind);
_builder.EmitOpCode(ILOpCode.Dup);
......@@ -605,7 +602,9 @@ private void EmitArgument(BoundExpression argument, RefKind refKind)
break;
default:
var unexpectedTemp = EmitAddress(argument, AddressKind.Writeable);
// NOTE: passing "ReadOnlyStrict" here.
// we should not get an address of a copy if at all possible
var unexpectedTemp = EmitAddress(argument, refKind == RefKindExtensions.StrictIn? AddressKind.ReadOnlyStrict: AddressKind.Writeable);
if (unexpectedTemp != null)
{
// interestingly enough "ref dynamic" sometimes is passed via a clone
......@@ -619,8 +618,11 @@ private void EmitArgument(BoundExpression argument, RefKind refKind)
private void EmitAddressOfExpression(BoundAddressOfOperator expression, bool used)
{
var temp = EmitAddress(expression.Operand, AddressKind.Writeable);
// NOTE: passing "ReadOnlyStrict" here.
// we should not get an address of a copy if at all possible
var temp = EmitAddress(expression.Operand, AddressKind.ReadOnlyStrict);
Debug.Assert(temp == null, "If the operand is addressable, then a temp shouldn't be required.");
if (used)
{
// When computing an address to be used to initialize a fixed-statement variable, we have to be careful
......@@ -811,15 +813,32 @@ private void EmitSideEffects(BoundSequence sequence)
}
}
private void EmitArguments(ImmutableArray<BoundExpression> arguments, ImmutableArray<ParameterSymbol> parameters)
private void EmitArguments(ImmutableArray<BoundExpression> arguments, ImmutableArray<ParameterSymbol> parameters, ImmutableArray<RefKind> refKindsOpt)
{
// We might have an extra argument for the __arglist() of a varargs method.
Debug.Assert(arguments.Length == parameters.Length || arguments.Length == parameters.Length + 1, "argument count must match parameter count");
for (int i = 0; i < arguments.Length; i++)
{
BoundExpression argument = arguments[i];
RefKind refKind = (i == parameters.Length) ? RefKind.None : parameters[i].RefKind;
EmitArgument(argument, refKind);
RefKind refKind;
if (!refKindsOpt.IsDefault && i < refKindsOpt.Length)
{
// if we have an explicit refKind for the given argument, use that
refKind = refKindsOpt[i];
}
else if (i < parameters.Length)
{
// otherwise check the parameter
refKind = parameters[i].RefKind;
}
else
{
// vararg case
Debug.Assert(arguments[i].Kind == BoundKind.ArgListOperator);
refKind = RefKind.None;
}
EmitArgument(arguments[i], refKind);
}
}
......@@ -1068,7 +1087,7 @@ private bool FieldLoadPrefersRef(BoundExpression receiver)
}
// can we take address at all?
if (!HasHome(receiver, needWriteable: false))
if (!HasHome(receiver, AddressKind.ReadOnly))
{
return false;
}
......@@ -1491,13 +1510,8 @@ private void EmitCallExpression(BoundCall call, UseKind useKind)
if (method.IsMetadataVirtual())
{
// NB: all methods that a struct could inherit from bases are non-mutating
// we are passing here "Writeable" just to keep verifier happy
// we can pass here "ReadOnly" and avoid unnecessary copy
var addressKind = EnablePEVerifyCompat() ?
AddressKind.Writeable :
AddressKind.ReadOnly;
tempOpt = EmitReceiverRef(receiver, addressKind);
// treat receiver as ReadOnly
tempOpt = EmitReceiverRef(receiver, AddressKind.ReadOnly);
callKind = CallKind.ConstrainedCallVirt;
}
else
......@@ -1569,7 +1583,7 @@ private void EmitCallExpression(BoundCall call, UseKind useKind)
}
}
EmitArguments(arguments, method.Parameters);
EmitArguments(arguments, method.Parameters, call.ArgumentRefKindsOpt);
int stackBehavior = GetCallStackBehavior(call);
switch (callKind)
{
......@@ -1861,7 +1875,7 @@ private void EmitObjectCreationExpression(BoundObjectCreationExpression expressi
}
else
{
EmitArguments(expression.Arguments, constructor.Parameters);
EmitArguments(expression.Arguments, constructor.Parameters, expression.ArgumentRefKindsOpt);
var stackAdjustment = GetObjCreationStackBehavior(expression);
_builder.EmitOpCode(ILOpCode.Newobj, stackAdjustment);
......@@ -2033,7 +2047,7 @@ private bool TryEmitAssignmentInPlace(BoundAssignmentOperator assignmentOperator
private bool SafeToGetWriteableReference(BoundExpression left)
{
if (!HasHome(left, needWriteable: true))
if (!HasHome(left, AddressKind.Writeable))
{
return false;
}
......@@ -2079,7 +2093,7 @@ private void InPlaceCtorCall(BoundExpression target, BoundObjectCreationExpressi
Debug.Assert(temp == null, "in-place ctor target should not create temps");
var constructor = objCreation.Constructor;
EmitArguments(objCreation.Arguments, constructor.Parameters);
EmitArguments(objCreation.Arguments, constructor.Parameters, objCreation.ArgumentRefKindsOpt);
// -2 to adjust for consumed target address and not produced value.
var stackAdjustment = GetObjCreationStackBehavior(objCreation) - 2;
_builder.EmitOpCode(ILOpCode.Call, stackAdjustment);
......@@ -2335,10 +2349,11 @@ private void EmitAssignmentValue(BoundAssignmentOperator assignmentOperator)
else
{
int exprTempsBefore = _expressionTemps?.Count ?? 0;
var local = ((BoundLocal)assignmentOperator.Left).LocalSymbol;
// NOTE: passing "ReadOnly" here. Assuming we do not have compile errors,
// We should not get an address of a copy, even if the RHS is readonly
LocalDefinition temp = EmitAddress(assignmentOperator.Right, AddressKind.ReadOnly);
// NOTE: passing "ReadOnlyStrict" here.
// we should not get an address of a copy if at all possible
LocalDefinition temp = EmitAddress(assignmentOperator.Right, local.RefKind == RefKind.RefReadOnly ? AddressKind.ReadOnlyStrict : AddressKind.Writeable);
// Generally taking a ref for the purpose of ref assignment should not be done on homeless values
// however, there are very rare cases when we need to get a ref off a temp in synthetic code.
......@@ -2346,7 +2361,7 @@ private void EmitAssignmentValue(BoundAssignmentOperator assignmentOperator)
AddExpressionTemp(temp);
// are we, by the way, ref-assigning to something that lives longer than encompassing expression?
if (((BoundLocal)assignmentOperator.Left).LocalSymbol.SynthesizedKind.IsLongLived())
if (local.SynthesizedKind.IsLongLived())
{
var exprTempsAfter = _expressionTemps?.Count ?? 0;
......
......@@ -724,7 +724,10 @@ private void EmitReturnStatement(BoundReturnStatement boundReturnStatement)
}
else
{
this.EmitAddress(expressionOpt, this._method.RefKind == RefKind.RefReadOnly? AddressKind.ReadOnly: AddressKind.Writeable);
// NOTE: passing "ReadOnlyStrict" here.
// we should never return an address of a copy
var unexpectedTemp = this.EmitAddress(expressionOpt, this._method.RefKind == RefKind.RefReadOnly ? AddressKind.ReadOnlyStrict : AddressKind.Writeable);
Debug.Assert(unexpectedTemp == null, "ref-returning a temp?");
}
if (ShouldUseIndirectReturn())
......
......@@ -2152,7 +2152,7 @@ internal override SyntaxNode GetDeclaratorSyntax()
throw new NotImplementedException();
}
internal override RefKind RefKind
public override RefKind RefKind
{
get { return RefKind.None; }
}
......
......@@ -211,7 +211,7 @@ protected override ImmutableArray<Declaration> GetDeclarationChildren()
return StaticCast<Declaration>.From(this.Children);
}
public IEnumerable<string> MemberNames
public ICollection<string> MemberNames
{
get
{
......
......@@ -718,7 +718,6 @@ private static ImmutableArray<RefKind> GetRefKindsOrNull(ArrayBuilder<RefKind> r
if (IsSafeForReordering(argument, argRefKind))
{
arguments[p] = argument;
refKinds[p] = argRefKind;
}
else
{
......@@ -727,6 +726,7 @@ private static ImmutableArray<RefKind> GetRefKindsOrNull(ArrayBuilder<RefKind> r
storesToTemps.Add(assignment);
arguments[p] = temp;
}
refKinds[p] = argRefKind;
}
}
......
......@@ -254,8 +254,10 @@ public override BoundNode VisitFixedLocalCollectionInitializer(BoundFixedLocalCo
pinnedTemp = factory.SynthesizedLocal(
initializerType,
syntax: declarator,
isPinned: true,
refKind: RefKind.Ref, // different from the array and string cases
isPinned: true,
//NOTE: different from the array and string cases
// RefReadOnly to allow referring to readonly variables. (technically we only "read" through the temp anyways)
refKind: RefKind.RefReadOnly,
kind: SynthesizedLocalKind.FixedReference);
// NOTE: we pin the reference, not the pointer.
......
......@@ -1269,7 +1269,7 @@ internal static BoundExpression NullOrDefault(TypeSymbol typeSymbol, SyntaxNode
#endif
)
{
if (refKind == RefKind.Out || refKind == RefKind.In)
if (refKind == RefKind.Out)
{
refKind = RefKind.Ref;
}
......
......@@ -281,9 +281,9 @@ private ImmutableArray<IOperation> GetIOperationChildren(BoundNode boundNode)
return builder.ToImmutableAndFree();
}
private IVariableDeclarationOperation CreateVariableDeclaration(BoundLocalDeclaration boundNode)
private IVariableDeclaratorOperation CreateVariableDeclarator(BoundLocalDeclaration boundNode)
{
return (IVariableDeclarationOperation)_cache.GetOrAdd(boundNode, n => CreateVariableDeclarationInternal((BoundLocalDeclaration)n, n.Syntax));
return (IVariableDeclaratorOperation)_cache.GetOrAdd(boundNode, n => CreateVariableDeclaratorInternal((BoundLocalDeclaration)n, n.Syntax));
}
private IPlaceholderOperation CreateBoundDeconstructValuePlaceholderOperation(BoundDeconstructValuePlaceholder boundDeconstructValuePlaceholder)
......@@ -314,7 +314,7 @@ private IOperation CreateBoundCallOperation(BoundCall boundCall)
ITypeSymbol type = boundCall.Type;
Optional<object> constantValue = ConvertToOptional(boundCall.ConstantValue);
bool isImplicit = boundCall.WasCompilerGenerated;
if (!boundCall.OriginalMethodsOpt.IsDefault || boundCall.ResultKind == LookupResultKind.OverloadResolutionFailure || targetMethod == null || targetMethod.OriginalDefinition is ErrorMethodSymbol)
{
return CreateInvalidExpressionForHasArgumentsExpression(boundCall.ReceiverOpt, boundCall.Arguments, null, syntax, type, constantValue, isImplicit);
......@@ -404,7 +404,7 @@ private IOperation CreateBoundIndexerAccessOperation(BoundIndexerAccess boundInd
MethodSymbol accessor = boundIndexerAccess.UseSetterForDefaultArgumentGeneration
? property.GetOwnOrInheritedSetMethod()
: property.GetOwnOrInheritedGetMethod();
if (!boundIndexerAccess.OriginalIndexersOpt.IsDefault || boundIndexerAccess.ResultKind == LookupResultKind.OverloadResolutionFailure || accessor == null || accessor.OriginalDefinition is ErrorMethodSymbol)
{
return CreateInvalidExpressionForHasArgumentsExpression(boundIndexerAccess.ReceiverOpt, boundIndexerAccess.Arguments, null, syntax, type, constantValue, isImplicit);
......@@ -498,7 +498,7 @@ private IOperation CreateBoundObjectCreationExpressionOperation(BoundObjectCreat
ITypeSymbol type = boundObjectCreationExpression.Type;
Optional<object> constantValue = ConvertToOptional(boundObjectCreationExpression.ConstantValue);
bool isImplicit = boundObjectCreationExpression.WasCompilerGenerated;
if (boundObjectCreationExpression.ResultKind == LookupResultKind.OverloadResolutionFailure || constructor == null || constructor.OriginalDefinition is ErrorMethodSymbol)
{
return CreateInvalidExpressionForHasArgumentsExpression(null, boundObjectCreationExpression.Arguments, boundObjectCreationExpression.InitializerExpressionOpt, syntax, type, constantValue, isImplicit);
......@@ -844,7 +844,7 @@ private IMethodReferenceOperation CreateBoundMethodGroupSingleMethodOperation(Bo
bool isVirtual = (methodSymbol.IsAbstract || methodSymbol.IsOverride || methodSymbol.IsVirtual) && !suppressVirtualCalls;
BoundExpression receiverOpt = boundMethodGroup.InstanceOpt;
if (methodSymbol.IsStatic && receiverOpt != null &&
if (methodSymbol.IsStatic && receiverOpt != null &&
receiverOpt.WasCompilerGenerated && receiverOpt.Kind == BoundKind.ThisReference)
{
receiverOpt = null;
......@@ -897,7 +897,7 @@ private IArrayCreationOperation CreateBoundArrayCreationOperation(BoundArrayCrea
SyntaxNode syntax = boundArrayCreation.Syntax;
ITypeSymbol type = boundArrayCreation.Type;
Optional<object> constantValue = ConvertToOptional(boundArrayCreation.ConstantValue);
bool isImplicit = boundArrayCreation.WasCompilerGenerated ||
bool isImplicit = boundArrayCreation.WasCompilerGenerated ||
(boundArrayCreation.InitializerOpt?.Syntax == syntax && !boundArrayCreation.InitializerOpt.WasCompilerGenerated);
return new LazyArrayCreationExpression(dimensionSizes, initializer, _semanticModel, syntax, type, constantValue, isImplicit);
}
......@@ -954,19 +954,20 @@ private ISimpleAssignmentOperation CreateBoundAssignmentOperatorOperation(BoundA
Debug.Assert(!IsMemberInitializer(boundAssignmentOperator));
Lazy<IOperation> target = new Lazy<IOperation>(() => Create(boundAssignmentOperator.Left));
bool isRef = boundAssignmentOperator.RefKind != RefKind.None;
Lazy<IOperation> value = new Lazy<IOperation>(() => Create(boundAssignmentOperator.Right));
SyntaxNode syntax = boundAssignmentOperator.Syntax;
ITypeSymbol type = boundAssignmentOperator.Type;
Optional<object> constantValue = ConvertToOptional(boundAssignmentOperator.ConstantValue);
bool isImplicit = boundAssignmentOperator.WasCompilerGenerated;
return new LazySimpleAssignmentExpression(target, value, _semanticModel, syntax, type, constantValue, isImplicit);
return new LazySimpleAssignmentExpression(target, isRef, value, _semanticModel, syntax, type, constantValue, isImplicit);
}
private IMemberInitializerOperation CreateBoundMemberInitializerOperation(BoundAssignmentOperator boundAssignmentOperator)
{
Debug.Assert(IsMemberInitializer(boundAssignmentOperator));
Lazy<IMemberReferenceOperation> target = new Lazy<IMemberReferenceOperation>(() => (IMemberReferenceOperation)Create(boundAssignmentOperator.Left));
Lazy<IOperation> target = new Lazy<IOperation>(() => Create(boundAssignmentOperator.Left));
Lazy<IObjectOrCollectionInitializerOperation> value = new Lazy<IObjectOrCollectionInitializerOperation>(() => (IObjectOrCollectionInitializerOperation)Create(boundAssignmentOperator.Right));
SyntaxNode syntax = boundAssignmentOperator.Syntax;
ITypeSymbol type = boundAssignmentOperator.Type;
......@@ -1063,11 +1064,12 @@ private IConditionalOperation CreateBoundConditionalOperatorOperation(BoundCondi
Lazy<IOperation> condition = new Lazy<IOperation>(() => Create(boundConditionalOperator.Condition));
Lazy<IOperation> whenTrue = new Lazy<IOperation>(() => Create(boundConditionalOperator.Consequence));
Lazy<IOperation> whenFalse = new Lazy<IOperation>(() => Create(boundConditionalOperator.Alternative));
bool isRef = boundConditionalOperator.IsByRef;
SyntaxNode syntax = boundConditionalOperator.Syntax;
ITypeSymbol type = boundConditionalOperator.Type;
Optional<object> constantValue = ConvertToOptional(boundConditionalOperator.ConstantValue);
bool isImplicit = boundConditionalOperator.WasCompilerGenerated;
return new LazyConditionalOperation(condition, whenTrue, whenFalse, _semanticModel, syntax, type, constantValue, isImplicit);
return new LazyConditionalOperation(condition, whenTrue, whenFalse, isRef, _semanticModel, syntax, type, constantValue, isImplicit);
}
private ICoalesceOperation CreateBoundNullCoalescingOperatorOperation(BoundNullCoalescingOperator boundNullCoalescingOperator)
......@@ -1301,11 +1303,12 @@ private IConditionalOperation CreateBoundIfStatementOperation(BoundIfStatement b
Lazy<IOperation> condition = new Lazy<IOperation>(() => Create(boundIfStatement.Condition));
Lazy<IOperation> ifTrueStatement = new Lazy<IOperation>(() => Create(boundIfStatement.Consequence));
Lazy<IOperation> ifFalseStatement = new Lazy<IOperation>(() => Create(boundIfStatement.AlternativeOpt));
bool isRef = false;
SyntaxNode syntax = boundIfStatement.Syntax;
ITypeSymbol type = null;
Optional<object> constantValue = default(Optional<object>);
bool isImplicit = boundIfStatement.WasCompilerGenerated;
return new LazyConditionalOperation(condition, ifTrueStatement, ifFalseStatement, _semanticModel, syntax, type, constantValue, isImplicit);
return new LazyConditionalOperation(condition, ifTrueStatement, ifFalseStatement, isRef, _semanticModel, syntax, type, constantValue, isImplicit);
}
private IWhileLoopOperation CreateBoundWhileStatementOperation(BoundWhileStatement boundWhileStatement)
......@@ -1426,7 +1429,7 @@ private ITryOperation CreateBoundTryStatementOperation(BoundTryStatement boundTr
private ICatchClauseOperation CreateBoundCatchBlockOperation(BoundCatchBlock boundCatchBlock)
{
var exceptionSourceOpt = (BoundLocal)boundCatchBlock.ExceptionSourceOpt;
Lazy<IOperation> expressionDeclarationOrExpression = new Lazy<IOperation>(() => exceptionSourceOpt != null ? CreateVariableDeclaration(exceptionSourceOpt) : null);
Lazy<IOperation> expressionDeclarationOrExpression = new Lazy<IOperation>(() => exceptionSourceOpt != null ? CreateVariableDeclarator(exceptionSourceOpt) : null);
ITypeSymbol exceptionType = boundCatchBlock.ExceptionTypeOpt;
ImmutableArray<ILocalSymbol> locals = boundCatchBlock.Locals.As<ILocalSymbol>();
Lazy<IOperation> filter = new Lazy<IOperation>(() => Create(boundCatchBlock.ExceptionFilterOpt));
......@@ -1440,7 +1443,7 @@ private ICatchClauseOperation CreateBoundCatchBlockOperation(BoundCatchBlock bou
private IFixedOperation CreateBoundFixedStatementOperation(BoundFixedStatement boundFixedStatement)
{
Lazy<IVariableDeclarationsOperation> variables = new Lazy<IVariableDeclarationsOperation>(() => (IVariableDeclarationsOperation)Create(boundFixedStatement.Declarations));
Lazy<IVariableDeclarationGroupOperation> variables = new Lazy<IVariableDeclarationGroupOperation>(() => (IVariableDeclarationGroupOperation)Create(boundFixedStatement.Declarations));
Lazy<IOperation> body = new Lazy<IOperation>(() => Create(boundFixedStatement.Body));
SyntaxNode syntax = boundFixedStatement.Syntax;
ITypeSymbol type = null;
......@@ -1521,6 +1524,7 @@ private IOperation CreateBoundLocalDeclarationOperation(BoundLocalDeclaration bo
SyntaxNode varStatement;
SyntaxNode varDeclaration;
SyntaxNode varDeclarator;
switch (kind)
{
case SyntaxKind.LocalDeclarationStatement:
......@@ -1531,18 +1535,22 @@ private IOperation CreateBoundLocalDeclarationOperation(BoundLocalDeclaration bo
// var statement points to LocalDeclarationStatementSyntax
varStatement = statement;
// var declaration points to VariableDeclaratorSyntax
varDeclaration = statement.Declaration.Variables.First();
varDeclaration = statement.Declaration;
varDeclarator = statement.Declaration.Variables.First();
break;
}
case SyntaxKind.VariableDeclarator:
{
// this happen for 'for loop' initializer
// We generate a DeclarationGroup for this scenario to maintain tree shape consistency across IOperation.
// var statement points to VariableDeclarationSyntax
varStatement = node.Parent;
varDeclaration = node.Parent;
// var declaration points to VariableDeclaratorSyntax
varDeclaration = node;
varDeclarator = node;
break;
}
default:
......@@ -1550,27 +1558,47 @@ private IOperation CreateBoundLocalDeclarationOperation(BoundLocalDeclaration bo
Debug.Fail($"Unexpected syntax: {kind}");
// otherwise, they points to whatever bound nodes are pointing to.
varStatement = varDeclaration = node;
varStatement = varDeclaration = varDeclarator = node;
break;
}
}
Lazy<ImmutableArray<IVariableDeclarationOperation>> declarations = new Lazy<ImmutableArray<IVariableDeclarationOperation>>(() => ImmutableArray.Create(CreateVariableDeclarationInternal(boundLocalDeclaration, varDeclaration)));
Lazy<ImmutableArray<IVariableDeclaratorOperation>> declarations = new Lazy<ImmutableArray<IVariableDeclaratorOperation>>(() => ImmutableArray.Create(CreateVariableDeclaratorInternal(boundLocalDeclaration, varDeclarator)));
bool multiVariableImplicit = boundLocalDeclaration.WasCompilerGenerated;
// In C#, the MultiVariable initializer will always be null, but we can't pass null as the actual lazy. We assume that all lazy elements always exist
Lazy<IVariableInitializerOperation> initializer = OperationFactory.EmptyInitializer;
IVariableDeclarationOperation multiVariableDeclaration = new LazyVariableDeclaration(declarations, initializer, _semanticModel, varDeclaration, null, default, multiVariableImplicit);
ITypeSymbol type = null;
Optional<object> constantValue = default(Optional<object>);
bool isImplicit = boundLocalDeclaration.WasCompilerGenerated;
return new LazyVariableDeclarationStatement(declarations, _semanticModel, varStatement, type, constantValue, isImplicit);
// In the case of a for loop, varStatement and varDeclaration will be the same syntax node.
// We can only have one explicit operation, so make sure this node is implicit in that scenario.
bool isImplicit = (varStatement == varDeclaration) || boundLocalDeclaration.WasCompilerGenerated;
return new VariableDeclarationGroupOperation(ImmutableArray.Create(multiVariableDeclaration), _semanticModel, varStatement, type, constantValue, isImplicit);
}
private IVariableDeclarationsOperation CreateBoundMultipleLocalDeclarationsOperation(BoundMultipleLocalDeclarations boundMultipleLocalDeclarations)
private IVariableDeclarationGroupOperation CreateBoundMultipleLocalDeclarationsOperation(BoundMultipleLocalDeclarations boundMultipleLocalDeclarations)
{
Lazy<ImmutableArray<IVariableDeclarationOperation>> declarations = new Lazy<ImmutableArray<IVariableDeclarationOperation>>(() =>
boundMultipleLocalDeclarations.LocalDeclarations.SelectAsArray(declaration => (IVariableDeclarationOperation)CreateVariableDeclaration(declaration)));
SyntaxNode syntax = boundMultipleLocalDeclarations.Syntax;
Lazy<ImmutableArray<IVariableDeclaratorOperation>> declarators = new Lazy<ImmutableArray<IVariableDeclaratorOperation>>(() =>
boundMultipleLocalDeclarations.LocalDeclarations.SelectAsArray(declaration => CreateVariableDeclarator(declaration)));
// In C#, the MultiVariable initializer will always be null, but we can't pass null as the actual lazy. We assume that all lazy elements always exist
Lazy<IVariableInitializerOperation> initializer = OperationFactory.EmptyInitializer;
// The syntax for the boundMultipleLocalDeclarations can either be a LocalDeclarationStatement or a VariableDeclaration, depending on the context
// (using/fixed statements vs variable declaration)
// We generate a DeclarationGroup for these scenarios (using/fixed) to maintain tree shape consistency across IOperation.
SyntaxNode declarationGroupSyntax = boundMultipleLocalDeclarations.Syntax;
SyntaxNode declarationSyntax = declarationGroupSyntax.IsKind(SyntaxKind.LocalDeclarationStatement) ?
((LocalDeclarationStatementSyntax)declarationGroupSyntax).Declaration :
declarationGroupSyntax;
bool declarationIsImplicit = boundMultipleLocalDeclarations.WasCompilerGenerated;
IVariableDeclarationOperation multiVariableDeclaration = new LazyVariableDeclaration(declarators, initializer, _semanticModel, declarationSyntax, null, default, declarationIsImplicit);
ITypeSymbol type = null;
Optional<object> constantValue = default(Optional<object>);
bool isImplicit = boundMultipleLocalDeclarations.WasCompilerGenerated;
return new LazyVariableDeclarationStatement(declarations, _semanticModel, syntax, type, constantValue, isImplicit);
// If the syntax was the same, we're in a fixed statement or using statement. We make the Group operation implicit in this scenario, as the
// syntax itself is a VariableDeclaration
bool isImplicit = declarationGroupSyntax == declarationSyntax || boundMultipleLocalDeclarations.WasCompilerGenerated;
return new VariableDeclarationGroupOperation(ImmutableArray.Create(multiVariableDeclaration), _semanticModel, declarationGroupSyntax, type, constantValue, isImplicit);
}
private ILabeledOperation CreateBoundLabelStatementOperation(BoundLabelStatement boundLabelStatement)
......
......@@ -51,14 +51,14 @@ internal IArgumentOperation CreateArgumentOperation(ArgumentKind kind, IParamete
isImplicit: expression.WasCompilerGenerated || argument == null);
}
private IVariableDeclarationOperation CreateVariableDeclarationInternal(BoundLocalDeclaration boundLocalDeclaration, SyntaxNode syntax)
private IVariableDeclaratorOperation CreateVariableDeclaratorInternal(BoundLocalDeclaration boundLocalDeclaration, SyntaxNode syntax)
{
IVariableInitializerOperation initializer = null;
if (boundLocalDeclaration.InitializerOpt != null)
{
IOperation initializerValue = Create(boundLocalDeclaration.InitializerOpt);
SyntaxNode initializerSyntax = null;
bool isImplicit = false;
bool initializerIsImplicit = false;
if (syntax is VariableDeclaratorSyntax variableDeclarator)
{
initializerSyntax = variableDeclarator.Initializer;
......@@ -72,18 +72,24 @@ private IVariableDeclarationOperation CreateVariableDeclarationInternal(BoundLoc
{
// There is no explicit syntax for the initializer, so we use the initializerValue's syntax and mark the operation as implicit.
initializerSyntax = initializerValue.Syntax;
isImplicit = true;
initializerIsImplicit = true;
}
initializer = OperationFactory.CreateVariableInitializer(initializerSyntax, initializerValue, _semanticModel, isImplicit);
initializer = OperationFactory.CreateVariableInitializer(initializerSyntax, initializerValue, _semanticModel, initializerIsImplicit);
}
return OperationFactory.CreateVariableDeclaration(boundLocalDeclaration.LocalSymbol, initializer, _semanticModel, syntax);
ILocalSymbol symbol = boundLocalDeclaration.LocalSymbol;
SyntaxNode syntaxNode = boundLocalDeclaration.Syntax;
ITypeSymbol type = null;
Optional<object> constantValue = default;
bool isImplicit = false;
return new VariableDeclarator(symbol, initializer, _semanticModel, syntax, type, constantValue, isImplicit);
}
private IVariableDeclarationOperation CreateVariableDeclaration(BoundLocal boundLocal)
private IVariableDeclaratorOperation CreateVariableDeclarator(BoundLocal boundLocal)
{
return OperationFactory.CreateVariableDeclaration(boundLocal.LocalSymbol, initializer: null, semanticModel: _semanticModel, syntax: boundLocal.Syntax);
return new VariableDeclarator(boundLocal.LocalSymbol, initializer: null, semanticModel: _semanticModel, syntax: boundLocal.Syntax, type: null, constantValue: default, isImplicit: false);
}
private IOperation CreateBoundCallInstanceOperation(BoundCall boundCall)
......@@ -193,7 +199,8 @@ private ImmutableArray<IOperation> GetAnonymousObjectCreationInitializers(BoundA
SyntaxNode syntax = value.Syntax?.Parent ?? expression.Syntax;
ITypeSymbol type = target.Type;
Optional<object> constantValue = value.ConstantValue;
var assignment = new SimpleAssignmentExpression(target, value, _semanticModel, syntax, type, constantValue, isImplicit: value.IsImplicit);
bool isRef = false;
var assignment = new SimpleAssignmentExpression(target, isRef, value, _semanticModel, syntax, type, constantValue, isImplicit: expression.WasCompilerGenerated);
builder.Add(assignment);
}
......
......@@ -183,6 +183,12 @@ public override void VisitLocal(ILocalSymbol symbol)
{
AddKeyword(SyntaxKind.RefKeyword);
AddSpace();
if (symbol.RefKind == RefKind.RefReadOnly)
{
AddKeyword(SyntaxKind.ReadOnlyKeyword);
AddSpace();
}
}
if (format.LocalOptions.IncludesOption(SymbolDisplayLocalOptions.IncludeType))
......
......@@ -321,9 +321,9 @@ public object ConstantValue
internal abstract ImmutableArray<Diagnostic> GetConstantValueDiagnostics(BoundExpression boundInitValue);
public bool IsRef => RefKind == RefKind.Ref;
public bool IsRef => RefKind != RefKind.None;
internal abstract RefKind RefKind
public abstract RefKind RefKind
{
get;
}
......
......@@ -458,7 +458,7 @@ internal override ImmutableArray<Diagnostic> GetConstantValueDiagnostics(BoundEx
return ImmutableArray<Diagnostic>.Empty;
}
internal override RefKind RefKind
public override RefKind RefKind
{
get { return _refKind; }
}
......
......@@ -1219,7 +1219,7 @@ public sealed override ImmutableArray<Symbol> GetMembers(string name)
internal override ImmutableArray<Symbol> GetSimpleNonTypeMembers(string name)
{
if (_lazyMembersDictionary != null || MemberNames.Contains(name))
if (_lazyMembersDictionary != null || declaration.MemberNames.Contains(name))
{
return GetMembers(name);
}
......
......@@ -74,7 +74,7 @@ internal override LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocal
_refKind);
}
internal override RefKind RefKind
public override RefKind RefKind
{
get { return _refKind; }
}
......
......@@ -88,7 +88,7 @@ internal override bool IsPinned
get { return _originalVariable.IsPinned; }
}
internal override RefKind RefKind
public override RefKind RefKind
{
get { return _originalVariable.RefKind; }
}
......
......@@ -233,6 +233,64 @@ .locals init (int V_0)
}
[Fact]
[CompilerTrait(CompilerFeature.PEVerifyCompat)]
public void InParamPassRoField1()
{
var text = @"
class Program
{
public static readonly int F = 42;
public static void Main()
{
System.Console.WriteLine(M(in F));
}
static ref readonly int M(in int x)
{
return ref x;
}
}
";
var comp = CompileAndVerify(text, parseOptions: TestOptions.Regular, verify: false, expectedOutput: "42");
comp.VerifyIL("Program.Main()", @"
{
// Code size 17 (0x11)
.maxstack 1
IL_0000: ldsflda ""int Program.F""
IL_0005: call ""ref readonly int Program.M(in int)""
IL_000a: ldind.i4
IL_000b: call ""void System.Console.WriteLine(int)""
IL_0010: ret
}");
comp.VerifyIL("Program.M(in int)", @"
{
// Code size 2 (0x2)
.maxstack 1
IL_0000: ldarg.0
IL_0001: ret
}");
comp = CompileAndVerify(text, verify: false, expectedOutput: "42", parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature());
comp.VerifyIL("Program.Main()", @"
{
// Code size 17 (0x11)
.maxstack 1
IL_0000: ldsflda ""int Program.F""
IL_0005: call ""ref readonly int Program.M(in int)""
IL_000a: ldind.i4
IL_000b: call ""void System.Console.WriteLine(int)""
IL_0010: ret
}");
}
[Fact]
public void InParamPassRoParamReturn()
{
......@@ -798,6 +856,143 @@ public static void M1(in int arg1, in int arg2, in int arg3)
);
}
[Fact]
public void ReadonlyParamAsyncSpillInRoField()
{
var text = @"
using System.Threading.Tasks;
class Program
{
public static readonly int F = 5;
static void Main(string[] args)
{
Test().Wait();
}
public static async Task Test()
{
int local = 1;
M1(in F, await GetT(2), 3);
}
public static async Task<T> GetT<T>(T val)
{
await Task.Yield();
MutateReadonlyField();
return val;
}
private static unsafe void MutateReadonlyField()
{
fixed(int* ptr = &F)
{
*ptr = 42;
}
}
public static void M1(in int arg1, in int arg2, in int arg3)
{
System.Console.WriteLine(arg1 + arg2 + arg3);
}
}
";
var comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.UnsafeReleaseExe);
var result = CompileAndVerify(comp, verify: false, expectedOutput: @"47");
var expectedIL = @"
{
// Code size 162 (0xa2)
.maxstack 3
.locals init (int V_0,
int V_1,
System.Runtime.CompilerServices.TaskAwaiter<int> V_2,
int V_3,
System.Exception V_4)
IL_0000: ldarg.0
IL_0001: ldfld ""int Program.<Test>d__2.<>1__state""
IL_0006: stloc.0
.try
{
IL_0007: ldloc.0
IL_0008: brfalse.s IL_003f
IL_000a: ldc.i4.2
IL_000b: call ""System.Threading.Tasks.Task<int> Program.GetT<int>(int)""
IL_0010: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()""
IL_0015: stloc.2
IL_0016: ldloca.s V_2
IL_0018: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get""
IL_001d: brtrue.s IL_005b
IL_001f: ldarg.0
IL_0020: ldc.i4.0
IL_0021: dup
IL_0022: stloc.0
IL_0023: stfld ""int Program.<Test>d__2.<>1__state""
IL_0028: ldarg.0
IL_0029: ldloc.2
IL_002a: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<Test>d__2.<>u__1""
IL_002f: ldarg.0
IL_0030: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Test>d__2.<>t__builder""
IL_0035: ldloca.s V_2
IL_0037: ldarg.0
IL_0038: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Program.<Test>d__2>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Program.<Test>d__2)""
IL_003d: leave.s IL_00a1
IL_003f: ldarg.0
IL_0040: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<Test>d__2.<>u__1""
IL_0045: stloc.2
IL_0046: ldarg.0
IL_0047: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<Test>d__2.<>u__1""
IL_004c: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_0052: ldarg.0
IL_0053: ldc.i4.m1
IL_0054: dup
IL_0055: stloc.0
IL_0056: stfld ""int Program.<Test>d__2.<>1__state""
IL_005b: ldloca.s V_2
IL_005d: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()""
IL_0062: stloc.1
IL_0063: ldsflda ""int Program.F""
IL_0068: ldloca.s V_1
IL_006a: ldc.i4.3
IL_006b: stloc.3
IL_006c: ldloca.s V_3
IL_006e: call ""void Program.M1(in int, in int, in int)""
IL_0073: leave.s IL_008e
}
catch System.Exception
{
IL_0075: stloc.s V_4
IL_0077: ldarg.0
IL_0078: ldc.i4.s -2
IL_007a: stfld ""int Program.<Test>d__2.<>1__state""
IL_007f: ldarg.0
IL_0080: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Test>d__2.<>t__builder""
IL_0085: ldloc.s V_4
IL_0087: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
IL_008c: leave.s IL_00a1
}
IL_008e: ldarg.0
IL_008f: ldc.i4.s -2
IL_0091: stfld ""int Program.<Test>d__2.<>1__state""
IL_0096: ldarg.0
IL_0097: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Test>d__2.<>t__builder""
IL_009c: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
IL_00a1: ret
}
";
result.VerifyIL("Program.<Test>d__2.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", expectedIL);
comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.UnsafeReleaseExe, parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature());
result = CompileAndVerify(comp, verify: false, expectedOutput: @"47");
result.VerifyIL("Program.<Test>d__2.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", expectedIL);
}
[Fact]
public void InParamAsyncSpill2()
{
......
......@@ -54,35 +54,30 @@ class C
}
}";
var comp = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature());
// WithPEVerifyCompatFeature should not cause us to get a ref of a temp in ref assignments
var comp = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature(), verify: false);
comp.VerifyIL("C.M", @"
{
// Code size 65 (0x41)
// Code size 59 (0x3b)
.maxstack 2
.locals init (S V_0,
S V_1,
S2 V_2)
.locals init (S V_0)
IL_0000: ldsflda ""S C.s1""
IL_0005: dup
IL_0006: ldobj ""S""
IL_000b: stloc.0
IL_000c: ldloca.s V_0
IL_000e: call ""void S.AddOne()""
IL_0013: ldsfld ""S C.s2""
IL_0018: stloc.0
IL_0019: ldloca.s V_0
IL_001b: ldobj ""S""
IL_0020: stloc.1
IL_0021: ldloca.s V_1
IL_0023: call ""void S.AddOne()""
IL_0028: ldsflda ""S2 C.s3""
IL_002d: call ""void S2.AddOne()""
IL_0032: ldarg.0
IL_0033: ldfld ""S2 C.s4""
IL_0038: stloc.2
IL_0039: ldloca.s V_2
IL_003b: call ""void S2.AddOne()""
IL_0040: ret
IL_0013: ldsflda ""S C.s2""
IL_0018: ldobj ""S""
IL_001d: stloc.0
IL_001e: ldloca.s V_0
IL_0020: call ""void S.AddOne()""
IL_0025: ldsflda ""S2 C.s3""
IL_002a: call ""void S2.AddOne()""
IL_002f: ldarg.0
IL_0030: ldflda ""S2 C.s4""
IL_0035: call ""void S2.AddOne()""
IL_003a: ret
}");
comp = CompileAndVerify(source, verify: false);
......@@ -864,47 +859,36 @@ .maxstack 1
IL_0033: ldflda ""int System.ValueTuple<int, int>.Item1""
IL_0038: ret
}");
comp = CompileAndVerify(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature(), verify: false);
// WithPEVerifyCompatFeature should not cause us to get a ref of a temp in ref returns
comp = CompileAndVerify(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature(), verify: false);
comp.VerifyIL("Program.Test", @"
{
// Code size 70 (0x46)
// Code size 57 (0x39)
.maxstack 1
.locals init (bool V_0, //b
int V_1,
System.ValueTuple<int, int> V_2,
int V_3,
System.ValueTuple<int, int> V_4)
.locals init (bool V_0) //b
IL_0000: ldc.i4.1
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: brfalse.s IL_0020
IL_0003: brfalse.s IL_001a
IL_0005: ldloc.0
IL_0006: brfalse.s IL_0012
IL_0006: brfalse.s IL_000f
IL_0008: ldarg.0
IL_0009: ldfld ""int Program.F""
IL_000e: stloc.1
IL_000f: ldloca.s V_1
IL_0011: ret
IL_0012: ldsfld ""(int Alice, int Bob) Program.F1""
IL_0017: stloc.2
IL_0018: ldloca.s V_2
IL_001a: ldflda ""int System.ValueTuple<int, int>.Item1""
IL_001f: ret
IL_0020: ldloc.0
IL_0021: brfalse.s IL_0032
IL_0023: ldarg.0
IL_0024: ldfld ""Program.S Program.S1""
IL_0029: ldfld ""int Program.S.F""
IL_002e: stloc.3
IL_002f: ldloca.s V_3
IL_0031: ret
IL_0032: ldsfld ""Program.S Program.S2""
IL_0037: ldfld ""(int Alice, int Bob) Program.S.F1""
IL_003c: stloc.s V_4
IL_003e: ldloca.s V_4
IL_0040: ldflda ""int System.ValueTuple<int, int>.Item1""
IL_0045: ret
IL_0009: ldflda ""int Program.F""
IL_000e: ret
IL_000f: ldsflda ""(int Alice, int Bob) Program.F1""
IL_0014: ldflda ""int System.ValueTuple<int, int>.Item1""
IL_0019: ret
IL_001a: ldloc.0
IL_001b: brfalse.s IL_0029
IL_001d: ldarg.0
IL_001e: ldflda ""Program.S Program.S1""
IL_0023: ldflda ""int Program.S.F""
IL_0028: ret
IL_0029: ldsflda ""Program.S Program.S2""
IL_002e: ldflda ""(int Alice, int Bob) Program.S.F1""
IL_0033: ldflda ""int System.ValueTuple<int, int>.Item1""
IL_0038: ret
}");
}
......
......@@ -12025,7 +12025,7 @@ public MyManagedStruct(int x)
n.n.num = x;
}
}";
var comp = CompileAndVerify(source, expectedOutput: @"42", verify: false);
var comp = CompileAndVerify(source, expectedOutput: @"42", parseOptions: TestOptions.Regular7_2, verify: false);
comp.VerifyIL("Program.Main",
@"
......@@ -12088,6 +12088,38 @@ .locals init (MyManagedStruct V_0)
IL_0046: call ""void System.Console.WriteLine(int)""
IL_004b: ret
}
");
comp = CompileAndVerify(source, expectedOutput: @"42", verify: true, parseOptions: TestOptions.Regular7_1);
comp.VerifyIL("Program.Main",
@"
{
// Code size 76 (0x4c)
.maxstack 3
.locals init (MyManagedStruct V_0)
IL_0000: newobj ""cls1..ctor()""
IL_0005: dup
IL_0006: ldfld ""MyManagedStruct cls1.y""
IL_000b: stloc.0
IL_000c: ldloca.s V_0
IL_000e: ldc.i4.s 123
IL_0010: call ""void MyManagedStruct.mutate(int)""
IL_0015: dup
IL_0016: ldfld ""MyManagedStruct cls1.y""
IL_001b: stloc.0
IL_001c: ldloca.s V_0
IL_001e: ldflda ""MyManagedStruct.Nested MyManagedStruct.n""
IL_0023: ldflda ""MyManagedStruct.Nested.Nested1 MyManagedStruct.Nested.n""
IL_0028: ldc.i4 0x1c8
IL_002d: call ""void MyManagedStruct.Nested.Nested1.mutate(int)""
IL_0032: ldfld ""MyManagedStruct cls1.y""
IL_0037: ldfld ""MyManagedStruct.Nested MyManagedStruct.n""
IL_003c: ldfld ""MyManagedStruct.Nested.Nested1 MyManagedStruct.Nested.n""
IL_0041: ldfld ""int MyManagedStruct.Nested.Nested1.num""
IL_0046: call ""void System.Console.WriteLine(int)""
IL_004b: ret
}
");
}
......
......@@ -1995,7 +1995,7 @@ void MixMethod()
compilation.VerifyDiagnostics();
var syntaxKinds = ImmutableArray.Create(SyntaxKind.VariableDeclaration);
var operationKinds = ImmutableArray.Create(OperationKind.VariableDeclaration);
var operationKinds = ImmutableArray.Create(OperationKind.VariableDeclarator);
var analyzers = new DiagnosticAnalyzer[] { new GeneratedCodeSyntaxAndOperationAnalyzer(GeneratedCodeAnalysisFlags.None, syntaxKinds, operationKinds) };
compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true,
......
......@@ -8,6 +8,7 @@
using Roslyn.Test.Utilities;
using Xunit;
using Microsoft.CodeAnalysis.UnitTests.Diagnostics;
using static Microsoft.CodeAnalysis.CommonDiagnosticAnalyzers;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
......@@ -1949,5 +1950,33 @@ public void M()
.VerifyAnalyzerDiagnostics(new DiagnosticAnalyzer[] { new SemanticModelInternalAnalyzer() }, null, null, true,
Diagnostic(SemanticModelInternalAnalyzer.GetOperationInternalDescriptor.Id, "1").WithLocation(6, 17));
}
[Fact]
public void TestOperationBlockAnalyzer_EmptyMethodBody()
{
const string source = @"
class C
{
public void M()
{
}
public void M2(int i)
{
}
public void M3(int i = 0)
{
}
}
";
CreateCompilationWithMscorlib45(source, parseOptions: TestOptions.RegularWithIOperationFeature)
.VerifyDiagnostics()
.VerifyAnalyzerDiagnostics(new DiagnosticAnalyzer[] { new OperationBlockAnalyzer() }, null, null, false,
Diagnostic("ID", "M2").WithArguments("M2", "Block").WithLocation(8, 17),
Diagnostic("ID", "M").WithArguments("M", "Block").WithLocation(4, 17),
Diagnostic("ID", "M3").WithArguments("M3", "ParameterInitializer").WithLocation(12, 17),
Diagnostic("ID", "M3").WithArguments("M3", "Block").WithLocation(12, 17));
}
}
}
......@@ -126,6 +126,27 @@ public void TestClone()
VerifyClone(model);
}
[CompilerTrait(CompilerFeature.IOperation)]
[WorkItem(22964, "https://github.com/dotnet/roslyn/issues/22964")]
[Fact]
public void GlobalStatement_Parent()
{
var source =
@"
System.Console.WriteLine();
";
var compilation = CreateStandardCompilation(source, options: TestOptions.ReleaseExe.WithScriptClassName("Script"), parseOptions: TestOptions.Script);
compilation.VerifyDiagnostics();
var tree = compilation.SyntaxTrees.Single();
var statement = tree.GetRoot().DescendantNodes().OfType<StatementSyntax>().Single();
var model = compilation.GetSemanticModel(tree);
var operation = model.GetOperationInternal(statement);
Assert.Equal(OperationKind.ExpressionStatement, operation.Kind);
Assert.Null(operation.Parent);
}
[CompilerTrait(CompilerFeature.IOperation)]
[Fact]
public void TestParentOperations()
......
......@@ -303,19 +303,22 @@ public void F(int dimension)
}
";
string expectedOperationTree = @"
IVariableDeclarationsOperation (1 declarations) (OperationKind.VariableDeclarations, Type: null) (Syntax: 'int[] x = { 1, 2 };')
IVariableDeclarationOperation (1 variables) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'x = { 1, 2 }')
Variables: Local_1: System.Int32[] x
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= { 1, 2 }')
IArrayCreationOperation (OperationKind.ArrayCreation, Type: System.Int32[], IsImplicit) (Syntax: '{ 1, 2 }')
Dimension Sizes(1):
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsImplicit) (Syntax: '{ 1, 2 }')
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null) (Syntax: 'int[] x = { 1, 2 };')
IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'int[] x = { 1, 2 }')
Declarators:
IVariableDeclaratorOperation (Symbol: System.Int32[] x) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'x = { 1, 2 }')
Initializer:
IArrayInitializerOperation (2 elements) (OperationKind.ArrayInitializer, Type: null) (Syntax: '{ 1, 2 }')
Element Values(2):
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2')
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= { 1, 2 }')
IArrayCreationOperation (OperationKind.ArrayCreation, Type: System.Int32[], IsImplicit) (Syntax: '{ 1, 2 }')
Dimension Sizes(1):
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsImplicit) (Syntax: '{ 1, 2 }')
Initializer:
IArrayInitializerOperation (2 elements) (OperationKind.ArrayInitializer, Type: null) (Syntax: '{ 1, 2 }')
Element Values(2):
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2')
Initializer:
null
";
var expectedDiagnostics = DiagnosticDescription.None;
......
......@@ -200,9 +200,12 @@ static void M(Task<int> t)
}
";
string expectedOperationTree = @"
IVariableDeclarationsOperation (1 declarations) (OperationKind.VariableDeclarations, Type: null, IsInvalid) (Syntax: 'await t;')
IVariableDeclarationOperation (1 variables) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: 't')
Variables: Local_1: await t
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsInvalid) (Syntax: 'await t;')
IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: 'await t')
Declarators:
IVariableDeclaratorOperation (Symbol: await t) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: 't')
Initializer:
null
Initializer:
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.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
public partial class IOperationTests : SemanticModelTestBase
{
[CompilerTrait(CompilerFeature.IOperation)]
[Fact]
public void ConditionalExpression_01()
{
string source = @"
class P
{
private void M()
{
int i = 0;
int j = 2;
var z = (/*<bind>*/true ? i : j/*</bind>*/);
}
}
";
string expectedOperationTree = @"
IConditionalOperation (OperationKind.Conditional, Type: System.Int32) (Syntax: 'true ? i : j')
Condition:
ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true')
WhenTrue:
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
WhenFalse:
ILocalReferenceOperation: j (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'j')
";
var expectedDiagnostics = DiagnosticDescription.None;
VerifyOperationTreeAndDiagnosticsForTest<ConditionalExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics);
}
[CompilerTrait(CompilerFeature.IOperation)]
[Fact]
public void ConditionalExpression_02()
{
string source = @"
class P
{
private void M()
{
int i = 0;
int j = 2;
(/*<bind>*/true ? ref i : ref j/*</bind>*/) = 4;
}
}
";
string expectedOperationTree = @"
IConditionalOperation (IsRef) (OperationKind.Conditional, Type: System.Int32) (Syntax: 'true ? ref i : ref j')
Condition:
ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true')
WhenTrue:
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
WhenFalse:
ILocalReferenceOperation: j (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'j')
";
var expectedDiagnostics = DiagnosticDescription.None;
VerifyOperationTreeAndDiagnosticsForTest<ConditionalExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics);
}
}
}
......@@ -35,18 +35,21 @@ void M1()
string expectedOperationTree = @"
IOperation: (OperationKind.None, Type: null) (Syntax: 'fixed(int * ... }')
Children(2):
IVariableDeclarationsOperation (1 declarations) (OperationKind.VariableDeclarations, Type: null) (Syntax: 'int *p = &i')
IVariableDeclarationOperation (1 variables) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'p = &i')
Variables: Local_1: System.Int32* p
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsImplicit) (Syntax: 'int *p = &i')
IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'int *p = &i')
Declarators:
IVariableDeclaratorOperation (Symbol: System.Int32* p) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'p = &i')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= &i')
IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: '&i')
Children(1):
IAddressOfOperation (OperationKind.AddressOf, Type: System.Int32*) (Syntax: '&i')
Reference:
IFieldReferenceOperation: System.Int32 C.i (OperationKind.FieldReference, Type: System.Int32) (Syntax: 'i')
Instance Receiver:
IInstanceReferenceOperation (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'i')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= &i')
IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: '&i')
Children(1):
IAddressOfOperation (OperationKind.AddressOf, Type: System.Int32*) (Syntax: '&i')
Reference:
IFieldReferenceOperation: System.Int32 C.i (OperationKind.FieldReference, Type: System.Int32) (Syntax: 'i')
Instance Receiver:
IInstanceReferenceOperation (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'i')
null
IBlockOperation (1 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'Console.Wri ... is {*p}"");')
Expression:
......@@ -106,29 +109,31 @@ void M1()
string expectedOperationTree = @"
IOperation: (OperationKind.None, Type: null) (Syntax: 'fixed (int* ... }')
Children(2):
IVariableDeclarationsOperation (2 declarations) (OperationKind.VariableDeclarations, Type: null) (Syntax: 'int* p1 = &i1, p2 = &i2')
IVariableDeclarationOperation (1 variables) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'p1 = &i1')
Variables: Local_1: System.Int32* p1
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= &i1')
IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: '&i1')
Children(1):
IAddressOfOperation (OperationKind.AddressOf, Type: System.Int32*) (Syntax: '&i1')
Reference:
IFieldReferenceOperation: System.Int32 C.i1 (OperationKind.FieldReference, Type: System.Int32) (Syntax: 'i1')
Instance Receiver:
IInstanceReferenceOperation (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'i1')
IVariableDeclarationOperation (1 variables) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'p2 = &i2')
Variables: Local_1: System.Int32* p2
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsImplicit) (Syntax: 'int* p1 = &i1, p2 = &i2')
IVariableDeclarationOperation (2 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'int* p1 = &i1, p2 = &i2')
Declarators:
IVariableDeclaratorOperation (Symbol: System.Int32* p1) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'p1 = &i1')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= &i1')
IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: '&i1')
Children(1):
IAddressOfOperation (OperationKind.AddressOf, Type: System.Int32*) (Syntax: '&i1')
Reference:
IFieldReferenceOperation: System.Int32 C.i1 (OperationKind.FieldReference, Type: System.Int32) (Syntax: 'i1')
Instance Receiver:
IInstanceReferenceOperation (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'i1')
IVariableDeclaratorOperation (Symbol: System.Int32* p2) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'p2 = &i2')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= &i2')
IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: '&i2')
Children(1):
IAddressOfOperation (OperationKind.AddressOf, Type: System.Int32*) (Syntax: '&i2')
Reference:
IFieldReferenceOperation: System.Int32 C.i2 (OperationKind.FieldReference, Type: System.Int32) (Syntax: 'i2')
Instance Receiver:
IInstanceReferenceOperation (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'i2')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= &i2')
IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: '&i2')
Children(1):
IAddressOfOperation (OperationKind.AddressOf, Type: System.Int32*) (Syntax: '&i2')
Reference:
IFieldReferenceOperation: System.Int32 C.i2 (OperationKind.FieldReference, Type: System.Int32) (Syntax: 'i2')
Instance Receiver:
IInstanceReferenceOperation (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'i2')
null
IBlockOperation (1 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'i3 = *p1 + *p2;')
Expression:
......@@ -183,32 +188,38 @@ void M1()
string expectedOperationTree = @"
IOperation: (OperationKind.None, Type: null) (Syntax: 'fixed (int* ... }')
Children(2):
IVariableDeclarationsOperation (1 declarations) (OperationKind.VariableDeclarations, Type: null) (Syntax: 'int* p1 = &i1')
IVariableDeclarationOperation (1 variables) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'p1 = &i1')
Variables: Local_1: System.Int32* p1
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= &i1')
IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: '&i1')
Children(1):
IAddressOfOperation (OperationKind.AddressOf, Type: System.Int32*) (Syntax: '&i1')
Reference:
IFieldReferenceOperation: System.Int32 C.i1 (OperationKind.FieldReference, Type: System.Int32) (Syntax: 'i1')
Instance Receiver:
IInstanceReferenceOperation (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'i1')
IOperation: (OperationKind.None, Type: null) (Syntax: 'fixed (int* ... }')
Children(2):
IVariableDeclarationsOperation (1 declarations) (OperationKind.VariableDeclarations, Type: null) (Syntax: 'int* p2 = &i2')
IVariableDeclarationOperation (1 variables) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'p2 = &i2')
Variables: Local_1: System.Int32* p2
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsImplicit) (Syntax: 'int* p1 = &i1')
IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'int* p1 = &i1')
Declarators:
IVariableDeclaratorOperation (Symbol: System.Int32* p1) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'p1 = &i1')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= &i2')
IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: '&i2')
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= &i1')
IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: '&i1')
Children(1):
IAddressOfOperation (OperationKind.AddressOf, Type: System.Int32*) (Syntax: '&i2')
IAddressOfOperation (OperationKind.AddressOf, Type: System.Int32*) (Syntax: '&i1')
Reference:
IFieldReferenceOperation: System.Int32 C.i2 (OperationKind.FieldReference, Type: System.Int32) (Syntax: 'i2')
IFieldReferenceOperation: System.Int32 C.i1 (OperationKind.FieldReference, Type: System.Int32) (Syntax: 'i1')
Instance Receiver:
IInstanceReferenceOperation (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'i2')
IInstanceReferenceOperation (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'i1')
Initializer:
null
IOperation: (OperationKind.None, Type: null) (Syntax: 'fixed (int* ... }')
Children(2):
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsImplicit) (Syntax: 'int* p2 = &i2')
IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'int* p2 = &i2')
Declarators:
IVariableDeclaratorOperation (Symbol: System.Int32* p2) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'p2 = &i2')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= &i2')
IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: '&i2')
Children(1):
IAddressOfOperation (OperationKind.AddressOf, Type: System.Int32*) (Syntax: '&i2')
Reference:
IFieldReferenceOperation: System.Int32 C.i2 (OperationKind.FieldReference, Type: System.Int32) (Syntax: 'i2')
Instance Receiver:
IInstanceReferenceOperation (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'i2')
Initializer:
null
IBlockOperation (1 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'i3 = *p1 + *p2;')
Expression:
......@@ -258,13 +269,16 @@ void M1()
string expectedOperationTree = @"
IOperation: (OperationKind.None, Type: null, IsInvalid) (Syntax: 'fixed (int* ... }')
Children(2):
IVariableDeclarationsOperation (1 declarations) (OperationKind.VariableDeclarations, Type: null, IsInvalid) (Syntax: 'int* p1 =')
IVariableDeclarationOperation (1 variables) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: 'p1 =')
Variables: Local_1: System.Int32* p1
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsInvalid, IsImplicit) (Syntax: 'int* p1 =')
IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: 'int* p1 =')
Declarators:
IVariableDeclaratorOperation (Symbol: System.Int32* p1) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: 'p1 =')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '=')
IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: '')
Children(0)
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '=')
IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: '')
Children(0)
null
IBlockOperation (1 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'i3 = *p1;')
Expression:
......@@ -313,18 +327,21 @@ void M1()
string expectedOperationTree = @"
IOperation: (OperationKind.None, Type: null, IsInvalid) (Syntax: 'fixed (int* ... }')
Children(2):
IVariableDeclarationsOperation (1 declarations) (OperationKind.VariableDeclarations, Type: null) (Syntax: 'int* p1 = &i1')
IVariableDeclarationOperation (1 variables) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'p1 = &i1')
Variables: Local_1: System.Int32* p1
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsImplicit) (Syntax: 'int* p1 = &i1')
IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'int* p1 = &i1')
Declarators:
IVariableDeclaratorOperation (Symbol: System.Int32* p1) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'p1 = &i1')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= &i1')
IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: '&i1')
Children(1):
IAddressOfOperation (OperationKind.AddressOf, Type: System.Int32*) (Syntax: '&i1')
Reference:
IFieldReferenceOperation: System.Int32 C.i1 (OperationKind.FieldReference, Type: System.Int32) (Syntax: 'i1')
Instance Receiver:
IInstanceReferenceOperation (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'i1')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= &i1')
IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: '&i1')
Children(1):
IAddressOfOperation (OperationKind.AddressOf, Type: System.Int32*) (Syntax: '&i1')
Reference:
IFieldReferenceOperation: System.Int32 C.i1 (OperationKind.FieldReference, Type: System.Int32) (Syntax: 'i1')
Instance Receiver:
IInstanceReferenceOperation (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'i1')
null
IBlockOperation (1 statements) (OperationKind.Block, Type: null, IsInvalid) (Syntax: '{ ... }')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: 'i3 = &p1;')
Expression:
......
......@@ -583,22 +583,25 @@ static void Main()
Body:
IBlockOperation (1 statements, 1 locals) (OperationKind.Block, Type: null) (Syntax: '{ ... }')
Locals: Local_1: System.Boolean b
IVariableDeclarationsOperation (1 declarations) (OperationKind.VariableDeclarations, Type: null) (Syntax: 'bool b = !x ... uals(null);')
IVariableDeclarationOperation (1 variables) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'b = !x.Equals(null)')
Variables: Local_1: System.Boolean b
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null) (Syntax: 'bool b = !x ... uals(null);')
IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'bool b = !x.Equals(null)')
Declarators:
IVariableDeclaratorOperation (Symbol: System.Boolean b) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'b = !x.Equals(null)')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= !x.Equals(null)')
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Boolean, IsImplicit) (Syntax: '!x.Equals(null)')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IUnaryOperation (UnaryOperatorKind.Not) (OperationKind.UnaryOperator, Type: System.Object) (Syntax: '!x.Equals(null)')
Operand:
IInvalidOperation (OperationKind.Invalid, Type: ?) (Syntax: 'x.Equals(null)')
Children(2):
IOperation: (OperationKind.None, Type: null) (Syntax: 'x.Equals')
Children(1):
ILocalReferenceOperation: x (OperationKind.LocalReference, Type: MissingType) (Syntax: 'x')
ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= !x.Equals(null)')
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Boolean, IsImplicit) (Syntax: '!x.Equals(null)')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IUnaryOperation (UnaryOperatorKind.Not) (OperationKind.UnaryOperator, Type: System.Object) (Syntax: '!x.Equals(null)')
Operand:
IInvalidOperation (OperationKind.Invalid, Type: ?) (Syntax: 'x.Equals(null)')
Children(2):
IOperation: (OperationKind.None, Type: null) (Syntax: 'x.Equals')
Children(1):
ILocalReferenceOperation: x (OperationKind.LocalReference, Type: MissingType) (Syntax: 'x')
ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
null
NextVariables(0)
";
VerifyOperationTreeForTest<ForEachStatementSyntax>(source, expectedOperationTree);
......
......@@ -476,8 +476,7 @@ public unsafe void M(int[] array)
}
";
string expectedOperationTree = @"
IVariableDeclarationOperation (1 variables) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'p = array')
Variables: Local_1: System.Int32* p
IVariableDeclaratorOperation (Symbol: System.Int32* p) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'p = array')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= array')
IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: 'array')
......@@ -576,8 +575,7 @@ public void M(dynamic d, int x)
}
";
string expectedOperationTree = @"
IVariableDeclarationOperation (1 variables) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'y = d[x]')
Variables: Local_1: dynamic y
IVariableDeclaratorOperation (Symbol: dynamic y) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'y = d[x]')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= d[x]')
IDynamicIndexerAccessOperation (OperationKind.DynamicIndexerAccess, Type: dynamic) (Syntax: 'd[x]')
......
......@@ -27,13 +27,15 @@ static void Main(string[] args)
}
";
string expectedOperationTree = @"
IVariableDeclarationsOperation (2 declarations) (OperationKind.VariableDeclarations, Type: null, IsInvalid) (Syntax: 'int x, ( 1 );')
IVariableDeclarationOperation (1 variables) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'x')
Variables: Local_1: System.Int32 x
Initializer:
null
IVariableDeclarationOperation (1 variables) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: '( 1 ')
Variables: Local_1: System.Int32
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsInvalid) (Syntax: 'int x, ( 1 );')
IVariableDeclarationOperation (2 declarators) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: 'int x, ( 1 ')
Declarators:
IVariableDeclaratorOperation (Symbol: System.Int32 x) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'x')
Initializer:
null
IVariableDeclaratorOperation (Symbol: System.Int32 ) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: '( 1 ')
Initializer:
null
Initializer:
null
";
......
......@@ -1828,8 +1828,7 @@ public void Deconstruct(out int x1, out int x2, out int x3, out int x4, out int
}
";
string expectedOperationTree = @"
IVariableDeclarationOperation (1 variables) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'y = (x, x, ... ) = new C()')
Variables: Local_1: (System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64) y
IVariableDeclaratorOperation (Symbol: (System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64) y) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'y = (x, x, ... ) = new C()')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= (x, x, x, ... ) = new C()')
IDeconstructionAssignmentOperation (OperationKind.DeconstructionAssignment, Type: (System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64, System.Int64)) (Syntax: '(x, x, x, x ... ) = new C()')
......@@ -3905,18 +3904,21 @@ static void Main()
}
";
string expectedOperationTree = @"
IVariableDeclarationsOperation (1 declarations) (OperationKind.VariableDeclarations, Type: null, IsInvalid) (Syntax: 'const (int ... ) = (1, 2);')
IVariableDeclarationOperation (1 variables) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: '= (1, 2)')
Variables: Local_1: (System.Int32 x, System.Int32 y)
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsInvalid) (Syntax: 'const (int ... ) = (1, 2);')
IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: '(int x, int y) = (1, 2)')
Declarators:
IVariableDeclaratorOperation (Symbol: (System.Int32 x, System.Int32 y) ) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: '= (1, 2)')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '= (1, 2)')
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Int32 x, System.Int32 y), IsImplicit) (Syntax: '(1, 2)')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(1, 2)')
Elements(2):
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '= (1, 2)')
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Int32 x, System.Int32 y), IsImplicit) (Syntax: '(1, 2)')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(1, 2)')
Elements(2):
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2')
null
";
var expectedDiagnostics = new DiagnosticDescription[] {
// CS1001: Identifier expected
......
......@@ -31,6 +31,11 @@ public interface IConditionalOperation : IOperation
/// Operation to be executed if the <see cref="Condition"/> is false.
/// </summary>
IOperation WhenFalse { get; }
/// <summary>
/// Is result a managed reference
/// </summary>
bool IsRef { get; }
}
}
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册