提交 f3f5cd30 编写于 作者: V Vladimir Sadov 提交者: GitHub

Merge pull request #17436 from VSadov/refTernaryRebased

Implemented  ref conditional operator (aka: ternary ref)
......@@ -3419,9 +3419,30 @@ private BoundExpression BindNullCoalescingOperator(BinaryExpressionSyntax node,
/// </remarks>
private BoundExpression BindConditionalOperator(ConditionalExpressionSyntax node, DiagnosticBag diagnostics)
{
var whenTrue = node.WhenTrue.SkipRef(out var whenTrueRefKind);
var whenFalse = node.WhenFalse.SkipRef(out var whenFalseRefKind);
var isRef = whenTrueRefKind == RefKind.Ref && whenFalseRefKind == RefKind.Ref;
if (!isRef)
{
if (whenFalseRefKind == RefKind.Ref)
{
diagnostics.Add(ErrorCode.ERR_RefConditionalNeedsTwoRefs, whenFalse.GetFirstToken().GetLocation());
}
if (whenTrueRefKind == RefKind.Ref)
{
diagnostics.Add(ErrorCode.ERR_RefConditionalNeedsTwoRefs, whenTrue.GetFirstToken().GetLocation());
}
}
BoundExpression condition = BindBooleanExpression(node.Condition, diagnostics);
BoundExpression trueExpr = BindValue(node.WhenTrue, diagnostics, BindValueKind.RValue);
BoundExpression falseExpr = BindValue(node.WhenFalse, diagnostics, BindValueKind.RValue);
var valKind = isRef ? BindValueKind.RefOrOut : BindValueKind.RValue;
BoundExpression trueExpr = BindValue(whenTrue, diagnostics, valKind);
BoundExpression falseExpr = BindValue(whenFalse, diagnostics, valKind);
TypeSymbol trueType = trueExpr.Type;
TypeSymbol falseType = falseExpr.Type;
......@@ -3488,6 +3509,21 @@ private BoundExpression BindConditionalOperator(ConditionalExpressionSyntax node
type = bestType;
hasErrors = true;
}
else if (isRef)
{
if (!Conversions.HasIdentityConversion(trueType, falseType))
{
diagnostics.Add(ErrorCode.ERR_RefConditionalDifferentTypes, falseExpr.Syntax.Location, trueType);
type = CreateErrorType();
hasErrors = true;
}
else
{
Debug.Assert(Conversions.HasIdentityConversion(trueType, bestType));
Debug.Assert(Conversions.HasIdentityConversion(falseType, bestType));
type = bestType;
}
}
else
{
trueExpr = GenerateConversionForAssignment(bestType, trueExpr, diagnostics);
......@@ -3515,7 +3551,7 @@ private BoundExpression BindConditionalOperator(ConditionalExpressionSyntax node
hasErrors = constantValue != null && constantValue.IsBad;
}
return new BoundConditionalOperator(node, condition, trueExpr, falseExpr, constantValue, type, hasErrors);
return new BoundConditionalOperator(node, isRef, condition, trueExpr, falseExpr, constantValue, type, hasErrors);
}
/// <summary>
......
......@@ -1521,6 +1521,28 @@ private bool CheckIsVariable(SyntaxNode node, BoundExpression expr, BindValueKin
return true;
}
var conditional = expr as BoundConditionalOperator;
if (conditional != null)
{
if (kind == BindValueKind.RefReturn)
{
var returnable = CheckIsVariable(conditional.Consequence.Syntax, conditional.Consequence, kind, checkingReceiver:false, diagnostics: diagnostics) &
CheckIsVariable(conditional.Alternative.Syntax, conditional.Alternative, kind, checkingReceiver:false, diagnostics: diagnostics);
return returnable;
}
else if (conditional.IsByRef)
{
return true;
}
else
{
Error(diagnostics, GetStandardLvalueError(kind), node);
}
return false;
}
// Local constants are never variables. Local variables are sometimes
// not to be treated as variables, if they are fixed, declared in a using,
// or declared in a foreach.
......
......@@ -426,7 +426,7 @@
<Node Name="BoundConditionalOperator" Base="BoundExpression">
<!-- Non-null type is required for this node kind -->
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
<Field Name="IsByRef" Type="bool"/>
<Field Name="Condition" Type="BoundExpression"/>
<Field Name="Consequence" Type="BoundExpression"/>
<Field Name="Alternative" Type="BoundExpression"/>
......
......@@ -2257,6 +2257,15 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to &apos;await&apos; cannot be used in an expression containing a ref conditional operator.
/// </summary>
internal static string ERR_RefConditionalAndAwait {
get {
return ResourceManager.GetString("ERR_RefConditionalAndAwait", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to A declaration of a by-reference variable must have an initializer.
/// </summary>
......@@ -7900,6 +7909,24 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to The expression must be of type &apos;{0}&apos; to match the alternative ref value.
/// </summary>
internal static string ERR_RefConditionalDifferentTypes {
get {
return ResourceManager.GetString("ERR_RefConditionalDifferentTypes", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Both conditional operator values must be ref values or neither may be a ref value.
/// </summary>
internal static string ERR_RefConditionalNeedsTwoRefs {
get {
return ResourceManager.GetString("ERR_RefConditionalNeedsTwoRefs", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The type &apos;{2}&apos; must be a reference type in order to use it as parameter &apos;{1}&apos; in the generic type or method &apos;{0}&apos;.
/// </summary>
......
......@@ -4834,6 +4834,15 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_RefReturningCallAndAwait" xml:space="preserve">
<value>'await' cannot be used in an expression containing a call to '{0}' because it returns by reference</value>
</data>
<data name="ERR_RefConditionalAndAwait" xml:space="preserve">
<value>'await' cannot be used in an expression containing a ref conditional operator</value>
</data>
<data name="ERR_RefConditionalNeedsTwoRefs" xml:space="preserve">
<value>Both conditional operator values must be ref values or neither may be a ref value</value>
</data>
<data name="ERR_RefConditionalDifferentTypes" xml:space="preserve">
<value>The expression must be of type '{0}' to match the alternative ref value</value>
</data>
<data name="ERR_ExpressionTreeContainsLocalFunction" xml:space="preserve">
<value>An expression tree may not contain a reference to a local function</value>
</data>
......
......@@ -103,6 +103,16 @@ private LocalDefinition EmitAddress(BoundExpression expression, AddressKind addr
EmitCallExpression(call, UseKind.UsedAsAddress);
break;
case BoundKind.ConditionalOperator:
var conditional = (BoundConditionalOperator)expression;
if (!conditional.IsByRef)
{
goto default;
}
EmitConditionalOperatorAddress(conditional);
break;
case BoundKind.AssignmentOperator:
var assignment = (BoundAssignmentOperator)expression;
if (assignment.RefKind == RefKind.None)
......@@ -120,6 +130,42 @@ private LocalDefinition EmitAddress(BoundExpression expression, AddressKind addr
return null;
}
/// <summary>
/// Emit code for a conditional (aka ternary) operator.
/// </summary>
/// <remarks>
/// (b ? x : y) becomes
/// push b
/// if pop then goto CONSEQUENCE
/// push y
/// goto DONE
/// CONSEQUENCE:
/// push x
/// DONE:
/// </remarks>
private void EmitConditionalOperatorAddress(BoundConditionalOperator expr)
{
Debug.Assert(expr.ConstantValue == null, "Constant value should have been emitted directly");
object consequenceLabel = new object();
object doneLabel = new object();
EmitCondBranch(expr.Condition, ref consequenceLabel, sense: true);
var temp = EmitAddress(expr.Alternative, AddressKind.Writeable);
Debug.Assert(temp == null);
_builder.EmitBranch(ILOpCode.Br, doneLabel);
// If we get to consequenceLabel, we should not have Alternative on stack, adjust for that.
_builder.AdjustStack(-1);
_builder.MarkLabel(consequenceLabel);
EmitAddress(expr.Consequence, AddressKind.Writeable);
_builder.MarkLabel(doneLabel);
}
private void EmitComplexConditionalReceiverAddress(BoundComplexConditionalReceiver expression)
{
Debug.Assert(!expression.Type.IsReferenceType);
......
......@@ -2162,6 +2162,18 @@ private bool EmitAssignmentPreamble(BoundAssignmentOperator assignmentOperator)
}
break;
case BoundKind.ConditionalOperator:
{
var left = (BoundConditionalOperator)assignmentTarget;
Debug.Assert(left.IsByRef);
var temp = EmitAddress(left, AddressKind.Writeable);
Debug.Assert(temp == null, "taking ref of this should not create a temp");
lhsUsesStack = true;
}
break;
case BoundKind.PointerIndirectionOperator:
{
var left = (BoundPointerIndirectionOperator)assignmentTarget;
......@@ -2337,6 +2349,11 @@ private void EmitStore(BoundAssignmentOperator assignment)
EmitIndirectStore(expression.Type, expression.Syntax);
break;
case BoundKind.ConditionalOperator:
Debug.Assert(((BoundConditionalOperator)expression).IsByRef);
EmitIndirectStore(expression.Type, expression.Syntax);
break;
case BoundKind.RefValueOperator:
case BoundKind.PointerIndirectionOperator:
case BoundKind.PseudoVariable:
......
......@@ -1001,6 +1001,10 @@ private static bool IsIndirectAssignment(BoundAssignmentOperator node)
Debug.Assert(((BoundCall)lhs).Method.RefKind == RefKind.Ref, "only ref returning methods are assignable");
return true;
case BoundKind.ConditionalOperator:
Debug.Assert(((BoundConditionalOperator)lhs).IsByRef, "only ref ternaries are assignable");
return true;
case BoundKind.AssignmentOperator:
Debug.Assert(((BoundAssignmentOperator)lhs).RefKind == RefKind.Ref, "only ref assignments are assignable");
return true;
......@@ -1239,21 +1243,23 @@ public override BoundNode VisitConditionalGoto(BoundConditionalGoto node)
public override BoundNode VisitConditionalOperator(BoundConditionalOperator node)
{
var origStack = StackDepth();
BoundExpression condition = (BoundExpression)this.Visit(node.Condition);
BoundExpression condition = this.VisitExpression(node.Condition, ExprContext.Value);
var cookie = GetStackStateCookie(); // implicit goto here
var context = node.IsByRef ? ExprContext.Address : ExprContext.Value;
SetStackDepth(origStack); // consequence is evaluated with original stack
BoundExpression consequence = (BoundExpression)this.Visit(node.Consequence);
BoundExpression consequence = this.VisitExpression(node.Consequence, context);
EnsureStackState(cookie); // implicit label here
SetStackDepth(origStack); // alternative is evaluated with original stack
BoundExpression alternative = (BoundExpression)this.Visit(node.Alternative);
BoundExpression alternative = this.VisitExpression(node.Alternative, context);
EnsureStackState(cookie); // implicit label here
return node.Update(condition, consequence, alternative, node.ConstantValueOpt, node.Type);
return node.Update(node.IsByRef, condition, consequence, alternative, node.ConstantValueOpt, node.Type);
}
public override BoundNode VisitBinaryOperator(BoundBinaryOperator node)
......
......@@ -1457,5 +1457,12 @@ internal enum ErrorCode
#endregion more stragglers for C# 7
ERR_BadParameterModifiers = 8205,
//PROTOTYPE(ReadonlyRefs): make err IDs contiguous before merging to master.
// For now it is more convenient to have a gap to avoid conflicts with other added errors
ERR_RefConditionalAndAwait = 8301,
ERR_RefConditionalNeedsTwoRefs = 8302,
ERR_RefConditionalDifferentTypes = 8332,
}
}
......@@ -2329,35 +2329,31 @@ public override BoundNode VisitContinueStatement(BoundContinueStatement node)
return null;
}
public override BoundNode VisitConditionalOperator(BoundConditionalOperator node)
public sealed override BoundNode VisitConditionalOperator(BoundConditionalOperator node)
{
var isByRef = node.IsByRef;
VisitCondition(node.Condition);
var consequenceState = this.StateWhenTrue;
var alternativeState = this.StateWhenFalse;
if (IsConstantTrue(node.Condition))
{
SetState(alternativeState);
Visit(node.Alternative);
SetState(consequenceState);
Visit(node.Consequence);
VisitConditionalOperand(alternativeState, node.Alternative, isByRef);
VisitConditionalOperand(consequenceState, node.Consequence, isByRef);
// it may be a boolean state at this point.
}
else if (IsConstantFalse(node.Condition))
{
SetState(consequenceState);
Visit(node.Consequence);
SetState(alternativeState);
Visit(node.Alternative);
VisitConditionalOperand(consequenceState, node.Consequence, isByRef);
VisitConditionalOperand(alternativeState, node.Alternative, isByRef);
// it may be a boolean state at this point.
}
else
{
SetState(consequenceState);
Visit(node.Consequence);
VisitConditionalOperand(consequenceState, node.Consequence, isByRef);
Unsplit();
consequenceState = this.State;
SetState(alternativeState);
Visit(node.Alternative);
VisitConditionalOperand(alternativeState, node.Alternative, isByRef);
Unsplit();
IntersectWith(ref this.State, ref consequenceState);
// it may not be a boolean state at this point (5.3.3.28)
......@@ -2366,6 +2362,21 @@ public override BoundNode VisitConditionalOperator(BoundConditionalOperator node
return null;
}
private void VisitConditionalOperand(LocalState state, BoundExpression operand, bool isByRef)
{
SetState(state);
if (isByRef)
{
VisitLvalue(operand);
// exposing ref is a potential write
WriteArgument(operand, RefKind.Ref, method: null);
}
else
{
Visit(operand);
}
}
public override BoundNode VisitBaseReference(BoundBaseReference node)
{
// TODO: in a struct constructor, "this" is not initially assigned.
......
......@@ -404,6 +404,16 @@ private BoundStatement UpdateStatement(BoundSpillSequenceBuilder builder, BoundS
}
goto default;
case BoundKind.ConditionalOperator:
var conditional = (BoundConditionalOperator)expression;
if (refKind != RefKind.None)
{
Debug.Assert(conditional.IsByRef);
_F.Diagnostics.Add(ErrorCode.ERR_RefConditionalAndAwait, _F.Syntax.Location);
refKind = RefKind.None; // Switch the RefKind to avoid asserting later in the pipeline
}
goto default;
case BoundKind.Literal:
case BoundKind.TypeExpression:
return expression;
......@@ -807,7 +817,7 @@ public override BoundNode VisitConditionalOperator(BoundConditionalOperator node
if (consequenceBuilder == null && alternativeBuilder == null)
{
return UpdateExpression(conditionBuilder, node.Update(condition, consequence, alternative, node.ConstantValueOpt, node.Type));
return UpdateExpression(conditionBuilder, node.Update(node.IsByRef, condition, consequence, alternative, node.ConstantValueOpt, node.Type));
}
if (conditionBuilder == null) conditionBuilder = new BoundSpillSequenceBuilder();
......
......@@ -97,7 +97,8 @@ public override BoundNode VisitUserDefinedConditionalLogicalOperator(BoundUserDe
rewrittenConsequence: boundTemp,
rewrittenAlternative: andOperatorCall,
constantValueOpt: null,
rewrittenType: type);
rewrittenType: type,
isRef: false);
// temp = x; T.false(temp) ? temp : T.&(temp, y)
return new BoundSequence(
......@@ -966,7 +967,8 @@ private BoundExpression MakeNullableHasValue(SyntaxNode syntax, BoundExpression
rewrittenConsequence: consequence,
rewrittenAlternative: alternative,
constantValueOpt: null,
rewrittenType: boolType);
rewrittenType: boolType,
isRef: false);
// tempx = x;
// tempy = y;
......@@ -1108,7 +1110,8 @@ private BoundExpression MakeNullableHasValue(SyntaxNode syntax, BoundExpression
rewrittenConsequence: unliftedOp,
rewrittenAlternative: MakeLiteral(syntax, ConstantValue.Create(operatorKind == BinaryOperatorKind.Equal), boolType),
constantValueOpt: null,
rewrittenType: boolType);
rewrittenType: boolType,
isRef: false);
}
else
{
......@@ -1125,7 +1128,8 @@ private BoundExpression MakeNullableHasValue(SyntaxNode syntax, BoundExpression
rewrittenConsequence: consequence,
rewrittenAlternative: alternative,
constantValueOpt: null,
rewrittenType: boolType);
rewrittenType: boolType,
isRef: false);
return new BoundSequence(
syntax: syntax,
......@@ -1313,7 +1317,8 @@ private BoundExpression MakeNullableHasValue(SyntaxNode syntax, BoundExpression
rewrittenConsequence: consequence,
rewrittenAlternative: alternative,
constantValueOpt: null,
rewrittenType: type);
rewrittenType: type,
isRef: false);
return new BoundSequence(
syntax: syntax,
......@@ -1462,7 +1467,8 @@ private BoundExpression CaptureNullableOperandInTempIfNeeded(BoundExpression ope
MakeBinaryOperator(syntax, kind, conditional.Consequence, right, type, method),
MakeBinaryOperator(syntax, kind, conditional.Alternative, right, type, method),
ConstantValue.NotAvailable,
type),
type,
isRef: false),
type);
}
}
......@@ -1532,7 +1538,8 @@ private BoundExpression MakeNewNullableBoolean(SyntaxNode syntax, bool? value)
rewrittenConsequence: kind == BinaryOperatorKind.LiftedBoolAnd ? nullBool : newNullBool,
rewrittenAlternative: kind == BinaryOperatorKind.LiftedBoolAnd ? newNullBool : nullBool,
constantValueOpt: null,
rewrittenType: alwaysNull.Type);
rewrittenType: alwaysNull.Type,
isRef: false);
}
// Now we optimize the case where one operand is null and the other is not. We generate
......@@ -1558,7 +1565,8 @@ private BoundExpression MakeNewNullableBoolean(SyntaxNode syntax, bool? value)
rewrittenConsequence: consequence,
rewrittenAlternative: alternative,
constantValueOpt: null,
rewrittenType: alwaysNull.Type);
rewrittenType: alwaysNull.Type,
isRef: false);
return new BoundSequence(
syntax: syntax,
locals: ImmutableArray.Create<LocalSymbol>(boundTemp.LocalSymbol),
......@@ -1618,7 +1626,8 @@ private BoundExpression MakeNewNullableBoolean(SyntaxNode syntax, bool? value)
rewrittenConsequence: consequence,
rewrittenAlternative: alternative,
constantValueOpt: null,
rewrittenType: newNullBool.Type);
rewrittenType: newNullBool.Type,
isRef: false);
return new BoundSequence(
syntax: syntax,
locals: ImmutableArray.Create<LocalSymbol>(boundTempX.LocalSymbol, boundTempY.LocalSymbol),
......@@ -1702,7 +1711,8 @@ private BoundExpression MakeNewNullableBoolean(SyntaxNode syntax, bool? value)
rewrittenConsequence: consequence,
rewrittenAlternative: alternative,
constantValueOpt: null,
rewrittenType: alternative.Type);
rewrittenType: alternative.Type,
isRef: false);
return new BoundSequence(
syntax: syntax,
......
......@@ -535,6 +535,10 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL
Debug.Assert(((BoundCall)originalLHS).Method.RefKind != RefKind.None);
break;
case BoundKind.ConditionalOperator:
Debug.Assert(((BoundConditionalOperator)originalLHS).IsByRef);
break;
case BoundKind.AssignmentOperator:
Debug.Assert(((BoundAssignmentOperator)originalLHS).RefKind != RefKind.None);
break;
......
......@@ -178,7 +178,8 @@ internal BoundExpression RewriteConditionalAccess(BoundConditionalAccess node, b
consequence,
_factory.Default(nodeType),
null,
nodeType);
nodeType,
isRef: false);
if (temp != null)
{
......
......@@ -24,7 +24,7 @@ public override BoundNode VisitConditionalOperator(BoundConditionalOperator node
if (rewrittenCondition.ConstantValue == null)
{
return node.Update(rewrittenCondition, rewrittenConsequence, rewrittenAlternative, node.ConstantValueOpt, node.Type);
return node.Update(node.IsByRef, rewrittenCondition, rewrittenConsequence, rewrittenAlternative, node.ConstantValueOpt, node.Type);
}
return RewriteConditionalOperator(
......@@ -33,7 +33,8 @@ public override BoundNode VisitConditionalOperator(BoundConditionalOperator node
rewrittenConsequence,
rewrittenAlternative,
node.ConstantValueOpt,
node.Type);
node.Type,
node.IsByRef);
}
private static BoundExpression RewriteConditionalOperator(
......@@ -42,7 +43,8 @@ public override BoundNode VisitConditionalOperator(BoundConditionalOperator node
BoundExpression rewrittenConsequence,
BoundExpression rewrittenAlternative,
ConstantValue constantValueOpt,
TypeSymbol rewrittenType)
TypeSymbol rewrittenType,
bool isRef)
{
// NOTE: This optimization assumes that a constant has no side effects. In the future we
// might wish to represent nodes that are known to the optimizer as having constant
......@@ -62,6 +64,7 @@ public override BoundNode VisitConditionalOperator(BoundConditionalOperator node
{
return new BoundConditionalOperator(
syntax,
isRef,
rewrittenCondition,
rewrittenConsequence,
rewrittenAlternative,
......
......@@ -841,7 +841,8 @@ private static BoundExpression NullableAlwaysHasValue(BoundExpression expression
rewrittenConsequence: consequence,
rewrittenAlternative: alternative,
constantValueOpt: null,
rewrittenType: type);
rewrittenType: type,
isRef: false);
return new BoundSequence(
syntax: syntax,
......@@ -967,7 +968,8 @@ private static BoundExpression NullableAlwaysHasValue(BoundExpression expression
MakeConversionNode(null, syntax, conditional.Consequence, conversion, @checked, explicitCastInCode: false, constantValueOpt: ConstantValue.NotAvailable, rewrittenType: type),
MakeConversionNode(null, syntax, conditional.Alternative, conversion, @checked, explicitCastInCode: false, constantValueOpt: ConstantValue.NotAvailable, rewrittenType: type),
ConstantValue.NotAvailable,
type),
type,
isRef: false),
type);
}
}
......@@ -1080,7 +1082,8 @@ private BoundExpression MakeLiftedUserDefinedConversionConsequence(BoundCall cal
rewrittenConsequence: consequence,
rewrittenAlternative: alternative,
constantValueOpt: null,
rewrittenType: rewrittenType);
rewrittenType: rewrittenType,
isRef: false);
// temp = operand
// temp.HasValue ? new R?(op_Whatever(temp.GetValueOrDefault())) : default(R?)
......
......@@ -348,7 +348,7 @@ public override BoundNode VisitFixedLocalCollectionInitializer(BoundFixedLocalCo
//(((temp = array) != null && temp.Length != 0) ? loc = &temp[0] : loc = null)
BoundStatement localInit = factory.ExpressionStatement(
new BoundConditionalOperator(factory.Syntax, condition, consequenceAssignment, alternativeAssignment, ConstantValue.NotAvailable, localType));
new BoundConditionalOperator(factory.Syntax,false, condition, consequenceAssignment, alternativeAssignment, ConstantValue.NotAvailable, localType));
return InstrumentLocalDeclarationIfNecessary(localDecl, localSymbol, localInit);
}
......
......@@ -125,7 +125,8 @@ public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperato
rewrittenConsequence: convertedLeft,
rewrittenAlternative: rewrittenRight,
constantValueOpt: null,
rewrittenType: rewrittenResultType);
rewrittenType: rewrittenResultType,
isRef: false);
Debug.Assert(conditionalExpression.ConstantValue == null); // we shouldn't have hit this else case otherwise
Debug.Assert(conditionalExpression.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames));
......
......@@ -210,7 +210,8 @@ public override BoundNode VisitUnaryOperator(BoundUnaryOperator node)
rewrittenConsequence: consequence,
rewrittenAlternative: alternative,
constantValueOpt: null,
rewrittenType: type);
rewrittenType: type,
isRef: false);
// temp = operand;
// temp.HasValue ?
......@@ -322,7 +323,8 @@ public override BoundNode VisitUnaryOperator(BoundUnaryOperator node)
MakeUnaryOperator(operatorKind, syntax, method, conditional.Consequence, type),
MakeUnaryOperator(operatorKind, syntax, method, conditional.Alternative, type),
ConstantValue.NotAvailable,
type),
type,
isRef: false),
type);
}
}
......@@ -647,7 +649,8 @@ private BoundExpression MakeUserDefinedIncrementOperator(BoundIncrementOperator
rewrittenConsequence: consequence,
rewrittenAlternative: alternative,
constantValueOpt: null,
rewrittenType: type);
rewrittenType: type,
isRef: false);
// temp = operand;
// temp.HasValue ?
......@@ -805,7 +808,7 @@ private BoundExpression MakeLiftedDecimalIncDecOperator(SyntaxNode syntax, Binar
BoundExpression alternative = new BoundDefaultOperator(syntax, null, operand.Type);
// x.HasValue ? new decimal?(op_Inc(x.GetValueOrDefault())) : default(decimal?)
return RewriteConditionalOperator(syntax, condition, consequence, alternative, ConstantValue.NotAvailable, operand.Type);
return RewriteConditionalOperator(syntax, condition, consequence, alternative, ConstantValue.NotAvailable, operand.Type, isRef: false);
}
/// <summary>
......
......@@ -552,6 +552,26 @@ private BoundExpression HoistRefInitialization(SynthesizedLocal local, BoundAssi
case BoundKind.DefaultOperator:
return expr;
case BoundKind.Call:
var call = (BoundCall)expr;
if (isRef)
{
Debug.Assert(call.Method.RefKind != RefKind.None);
F.Diagnostics.Add(ErrorCode.ERR_RefReturningCallAndAwait, F.Syntax.Location, call.Method);
isRef = false; // Switch to ByVal to avoid asserting later in the pipeline
}
goto default;
case BoundKind.ConditionalOperator:
var conditional = (BoundConditionalOperator)expr;
if (isRef)
{
Debug.Assert(conditional.IsByRef);
F.Diagnostics.Add(ErrorCode.ERR_RefConditionalAndAwait, F.Syntax.Location);
isRef = false; // Switch to ByVal to avoid asserting later in the pipeline
}
goto default;
default:
if (expr.ConstantValue != null)
{
......
......@@ -653,7 +653,7 @@ public BoundCall Call(BoundExpression receiver, MethodSymbol method, ImmutableAr
public BoundExpression Conditional(BoundExpression condition, BoundExpression consequence, BoundExpression alternative, TypeSymbol type)
{
return new BoundConditionalOperator(Syntax, condition, consequence, alternative, default(ConstantValue), type) { WasCompilerGenerated = true };
return new BoundConditionalOperator(Syntax, false, condition, consequence, alternative, default(ConstantValue), type) { WasCompilerGenerated = true };
}
public BoundExpression ComplexConditionalReceiver(BoundExpression valueTypeReceiver, BoundExpression referenceTypeReceiver)
......
......@@ -9507,9 +9507,9 @@ private ExpressionSyntax ParseSubExpressionCore(Precedence precedence)
if (tk == SyntaxKind.QuestionToken && precedence <= Precedence.Ternary)
{
var questionToken = this.EatToken();
var colonLeft = this.ParseExpressionCore();
var colonLeft = this.ParsePossibleRefExpression();
var colon = this.EatToken(SyntaxKind.ColonToken);
var colonRight = this.ParseExpressionCore();
var colonRight = this.ParsePossibleRefExpression();
leftOperand = _syntaxFactory.ConditionalExpression(leftOperand, questionToken, colonLeft, colon, colonRight);
}
......
......@@ -74,6 +74,7 @@
<Compile Include="CodeGen\CodeGenInParametersTests.cs" />
<Compile Include="CodeGen\CodeGenCapturing.cs" />
<Compile Include="CodeGen\CodeGenRefReadonlyReturnTests.cs" />
<Compile Include="CodeGen\CodeGenRefConditionalOperatorTests.cs" />
<Compile Include="Emit\BinaryCompatibility.cs" />
<Compile Include="Emit\DesktopStrongNameProviderTests.cs" />
<Compile Include="Attributes\InternalsVisibleToAndStrongNameTests.cs" />
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册