private BoundExpression RewritePointerElementAccess(BoundPointerElementAccess node, BoundExpression rewrittenExpression, BoundExpression rewrittenIndex) { // Optimization: p[0] == *p if (rewrittenIndex.IsDefaultValue()) { return(new BoundPointerIndirectionOperator( node.Syntax, rewrittenExpression, node.Type)); } BinaryOperatorKind additionKind = BinaryOperatorKind.Addition; switch (rewrittenIndex.Type.SpecialType) { case SpecialType.System_Int32: additionKind |= BinaryOperatorKind.PointerAndInt32Addition; break; case SpecialType.System_UInt32: additionKind |= BinaryOperatorKind.PointerAndUInt32Addition; break; case SpecialType.System_Int64: additionKind |= BinaryOperatorKind.PointerAndInt64Addition; break; case SpecialType.System_UInt64: additionKind |= BinaryOperatorKind.PointerAndUInt64Addition; break; default: throw ExceptionUtilities.UnexpectedValue(rewrittenIndex.Type.SpecialType); } if (node.Checked) { additionKind |= BinaryOperatorKind.Checked; } return(new BoundPointerIndirectionOperator( node.Syntax, MakeBinaryOperator( node.Syntax, additionKind, rewrittenExpression, rewrittenIndex, rewrittenExpression.Type, method: null, isPointerElementAccess: true), //see RewriterPointerNumericOperator node.Type)); }
/// <summary> /// Variables local to current frame do not need temps when re-read multiple times /// as long as there is no code that may write to locals in between accesses and they /// are not captured. /// /// Example: /// l += goo(ref l); /// /// even though l is a local, we must access it via a temp since "goo(ref l)" may change it /// on between accesses. /// /// Note: In <c>this.x++</c>, <c>this</c> cannot change between reads. But in <c>(this, ...) == (..., this.Mutate())</c> it can. /// </summary> internal static bool CanChangeValueBetweenReads( BoundExpression expression, bool localsMayBeAssignedOrCaptured = true, bool structThisCanChangeValueBetweenReads = false) { if (expression.IsDefaultValue()) { return(false); } if (expression.ConstantValue != null) { var type = expression.Type; return(!ConstantValueIsTrivial(type)); } switch (expression.Kind) { case BoundKind.ThisReference: return(structThisCanChangeValueBetweenReads && ((BoundThisReference)expression).Type.IsStructType()); case BoundKind.BaseReference: return(false); case BoundKind.Literal: var type = expression.Type; return(!ConstantValueIsTrivial(type)); case BoundKind.Parameter: return(localsMayBeAssignedOrCaptured || ((BoundParameter)expression).ParameterSymbol.RefKind != RefKind.None); case BoundKind.Local: return(localsMayBeAssignedOrCaptured || ((BoundLocal)expression).LocalSymbol.RefKind != RefKind.None); default: return(true); } }
private BoundExpression MakeNullCoalescingOperator( SyntaxNode syntax, BoundExpression rewrittenLeft, BoundExpression rewrittenRight, Conversion leftConversion, BoundNullCoalescingOperatorResultKind resultKind, TypeSymbol rewrittenResultType) { Debug.Assert(rewrittenLeft != null); Debug.Assert(rewrittenRight != null); Debug.Assert(leftConversion.IsValid); Debug.Assert((object)rewrittenResultType != null); Debug.Assert(rewrittenRight.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); var isUnconstrainedTypeParameter = (object)rewrittenLeft.Type != null && !rewrittenLeft.Type.IsReferenceType && !rewrittenLeft.Type.IsValueType; // first we can make a small optimization: // If left is a constant then we already know whether it is null or not. If it is null then we // can simply generate "right". If it is not null then we can simply generate // MakeConversion(left). This does not hold when the left is an unconstrained type parameter: at runtime, // it can be either left or right depending on the runtime type of T if (!isUnconstrainedTypeParameter) { if (rewrittenLeft.IsDefaultValue()) { return(rewrittenRight); } if (rewrittenLeft.ConstantValue != null) { Debug.Assert(!rewrittenLeft.ConstantValue.IsNull); return(GetConvertedLeftForNullCoalescingOperator(rewrittenLeft, leftConversion, rewrittenResultType)); } } // string concatenation is never null. // interpolated string lowering may introduce redundant null coalescing, which we have to remove. if (IsStringConcat(rewrittenLeft)) { return(GetConvertedLeftForNullCoalescingOperator(rewrittenLeft, leftConversion, rewrittenResultType)); } // if left conversion is intrinsic implicit (always succeeds) and results in a reference type // we can apply conversion before doing the null check that allows for a more efficient IL emit. if (rewrittenLeft.Type.IsReferenceType && leftConversion.IsImplicit && !leftConversion.IsUserDefined) { if (!leftConversion.IsIdentity) { rewrittenLeft = MakeConversionNode(rewrittenLeft.Syntax, rewrittenLeft, leftConversion, rewrittenResultType, @checked: false); } return(new BoundNullCoalescingOperator(syntax, rewrittenLeft, rewrittenRight, Conversion.Identity, resultKind, rewrittenResultType)); } if (leftConversion.IsIdentity || leftConversion.Kind == ConversionKind.ExplicitNullable) { var conditionalAccess = rewrittenLeft as BoundLoweredConditionalAccess; if (conditionalAccess != null && (conditionalAccess.WhenNullOpt == null || NullableNeverHasValue(conditionalAccess.WhenNullOpt))) { var notNullAccess = NullableAlwaysHasValue(conditionalAccess.WhenNotNull); if (notNullAccess != null) { var whenNullOpt = rewrittenRight; if (whenNullOpt.Type.IsNullableType()) { notNullAccess = conditionalAccess.WhenNotNull; } if (whenNullOpt.IsDefaultValue()) { whenNullOpt = null; } return(conditionalAccess.Update( conditionalAccess.Receiver, conditionalAccess.HasValueMethodOpt, whenNotNull: notNullAccess, whenNullOpt: whenNullOpt, id: conditionalAccess.Id, type: rewrittenResultType )); } } } // Optimize left ?? right to left.GetValueOrDefault() when left is T? and right is the default value of T if (rewrittenLeft.Type.IsNullableType() && RemoveIdentityConversions(rewrittenRight).IsDefaultValue() && rewrittenRight.Type.Equals(rewrittenLeft.Type.GetNullableUnderlyingType(), TypeCompareKind.AllIgnoreOptions) && TryGetNullableMethod(rewrittenLeft.Syntax, rewrittenLeft.Type, SpecialMember.System_Nullable_T_GetValueOrDefault, out MethodSymbol getValueOrDefault)) { return(BoundCall.Synthesized(rewrittenLeft.Syntax, rewrittenLeft, getValueOrDefault)); } // We lower left ?? right to // // var temp = left; // (temp != null) ? MakeConversion(temp) : right // BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = _factory.StoreToTemp(rewrittenLeft, out tempAssignment); // temp != null BoundExpression nullCheck = MakeNullCheck(syntax, boundTemp, BinaryOperatorKind.NotEqual); // MakeConversion(temp, rewrittenResultType) BoundExpression convertedLeft = GetConvertedLeftForNullCoalescingOperator(boundTemp, leftConversion, rewrittenResultType); Debug.Assert(convertedLeft.HasErrors || convertedLeft.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); // (temp != null) ? MakeConversion(temp, LeftConversion) : RightOperand BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: nullCheck, rewrittenConsequence: convertedLeft, rewrittenAlternative: rewrittenRight, constantValueOpt: null, 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 | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); return(new BoundSequence( syntax: syntax, locals: ImmutableArray.Create(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment), value: conditionalExpression, type: rewrittenResultType)); }
// a simple check for common non-side-effecting expressions internal static bool ReadIsSideeffecting( BoundExpression expression) { if (expression.ConstantValue != null) { return(false); } if (expression.IsDefaultValue()) { return(false); } switch (expression.Kind) { case BoundKind.ThisReference: case BoundKind.BaseReference: case BoundKind.Literal: case BoundKind.Parameter: case BoundKind.Local: case BoundKind.Lambda: return(false); case BoundKind.Conversion: var conv = (BoundConversion)expression; return(conv.ConversionHasSideEffects() || ReadIsSideeffecting(conv.Operand)); case BoundKind.PassByCopy: return(ReadIsSideeffecting(((BoundPassByCopy)expression).Expression)); case BoundKind.ObjectCreationExpression: // common production of lowered conversions to nullable // new S?(arg) if (expression.Type.IsNullableType()) { var objCreation = (BoundObjectCreationExpression)expression; return(objCreation.Arguments.Length == 1 && ReadIsSideeffecting(objCreation.Arguments[0])); } return(true); case BoundKind.Call: var call = (BoundCall)expression; var method = call.Method; // common production of lowered lifted operators // GetValueOrDefault is known to be not sideeffecting. if (method.ContainingType?.IsNullableType() == true) { if (IsSpecialMember(method, SpecialMember.System_Nullable_T_GetValueOrDefault) || IsSpecialMember(method, SpecialMember.System_Nullable_T_get_HasValue)) { return(ReadIsSideeffecting(call.ReceiverOpt)); } } return(true); default: return(true); } }
private static bool IsNullOrEmptyStringConstant(BoundExpression operand) { return((operand.ConstantValue != null && string.IsNullOrEmpty(operand.ConstantValue.StringValue)) || operand.IsDefaultValue()); }