/// <summary> /// The flow analysis pass. This pass reports required diagnostics for unreachable /// statements and uninitialized variables (through the call to FlowAnalysisWalker.Analyze), /// and inserts a final return statement if the end of a void-returning method is reachable. /// </summary> /// <param name="method">the method to be analyzed</param> /// <param name="block">the method's body</param> /// <param name="diagnostics">the receiver of the reported diagnostics</param> /// <param name="hasTrailingExpression">indicates whether this Script had a trailing expression</param> /// <param name="originalBodyNested">the original method body is the last statement in the block</param> /// <returns>the rewritten block for the method (with a return statement possibly inserted)</returns> public static BoundBlock Rewrite( MethodSymbol method, BoundBlock block, DiagnosticBag diagnostics, bool hasTrailingExpression, bool originalBodyNested) { #if DEBUG // We should only see a trailingExpression if we're in a Script initializer. Debug.Assert(!hasTrailingExpression || method.IsScriptInitializer); var initialDiagnosticCount = diagnostics.ToReadOnly().Length; #endif var compilation = method.DeclaringCompilation; if (method.ReturnsVoid || method.IsIterator || (method.IsAsync && compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task) == method.ReturnType)) { // we don't analyze synthesized void methods. if ((method.IsImplicitlyDeclared && !method.IsScriptInitializer) || Analyze(compilation, method, block, diagnostics)) { block = AppendImplicitReturn(block, method, (CSharpSyntaxNode)(method as SourceMethodSymbol)?.BodySyntax, originalBodyNested); } } else if (Analyze(compilation, method, block, diagnostics)) { // If the method is a lambda expression being converted to a non-void delegate type // and the end point is reachable then suppress the error here; a special error // will be reported by the lambda binder. Debug.Assert(method.MethodKind != MethodKind.AnonymousFunction); // Add implicit "return default(T)" if this is a submission that does not have a trailing expression. var submissionResultType = (method as SynthesizedInteractiveInitializerMethod)?.ResultType; if (!hasTrailingExpression && ((object)submissionResultType != null)) { Debug.Assert(submissionResultType.SpecialType != SpecialType.System_Void); var trailingExpression = new BoundDefaultOperator(method.GetNonNullSyntaxNode(), submissionResultType); var newStatements = block.Statements.Add(new BoundReturnStatement(trailingExpression.Syntax, trailingExpression)); block = new BoundBlock(block.Syntax, ImmutableArray<LocalSymbol>.Empty, newStatements) { WasCompilerGenerated = true }; #if DEBUG // It should not be necessary to repeat analysis after adding this node, because adding a trailing // return in cases where one was missing should never produce different Diagnostics. var flowAnalysisDiagnostics = DiagnosticBag.GetInstance(); Debug.Assert(!Analyze(compilation, method, block, flowAnalysisDiagnostics)); Debug.Assert(flowAnalysisDiagnostics.ToReadOnly().SequenceEqual(diagnostics.ToReadOnly().Skip(initialDiagnosticCount))); flowAnalysisDiagnostics.Free(); #endif } // If there's more than one location, then the method is partial and we // have already reported a non-void partial method error. else if (method.Locations.Length == 1) { diagnostics.Add(ErrorCode.ERR_ReturnExpected, method.Locations[0], method); } } return block; }
internal static BoundTypeOrInstanceInitializers RewriteScriptInitializer(ImmutableArray<BoundInitializer> boundInitializers, SynthesizedInteractiveInitializerMethod method) { Debug.Assert(!boundInitializers.IsDefault); var boundStatements = ArrayBuilder<BoundStatement>.GetInstance(boundInitializers.Length); var submissionResultType = method.ResultType; BoundExpression submissionResult = null; foreach (var initializer in boundInitializers) { // The value of the last expression statement (if any) is returned from the submission initializer. if (((object)submissionResultType != null) && (initializer == boundInitializers.Last()) && (initializer.Kind == BoundKind.GlobalStatementInitializer)) { var statement = ((BoundGlobalStatementInitializer)initializer).Statement; if (statement.Kind == BoundKind.ExpressionStatement) { var expr = ((BoundExpressionStatement)statement).Expression; if ((object)expr.Type != null && expr.Type.SpecialType != SpecialType.System_Void) { submissionResult = expr; continue; } } } boundStatements.Add(RewriteInitializersAsStatements(initializer)); } var syntax = method.GetNonNullSyntaxNode(); if ((object)submissionResultType != null) { if (submissionResult == null) { // Return default(T) if submission does not have a trailing expression. submissionResult = new BoundDefaultOperator(syntax, submissionResultType); } Debug.Assert(submissionResult.Type.SpecialType != SpecialType.System_Void); // The expression is converted to the submission result type when the initializer is bound. boundStatements.Add(new BoundReturnStatement(submissionResult.Syntax, submissionResult)); } return new BoundTypeOrInstanceInitializers(syntax, boundStatements.ToImmutableAndFree()); }
private BoundExpression RewriteLiftedUserDefinedConversion( CSharpSyntaxNode syntax, BoundExpression rewrittenOperand, MethodSymbol method, TypeSymbol rewrittenType, ConversionKind conversionKind) { if (inExpressionLambda) { Conversion conv = MakeConversion(syntax, new Conversion(conversionKind, method, false), rewrittenOperand.Type, rewrittenType); return BoundConversion.Synthesized(syntax, rewrittenOperand, conv, false, true, default(ConstantValue), rewrittenType); } // DELIBERATE SPEC VIOLATION: // The native compiler allows for a "lifted" conversion even when the return type of the conversion // not a non-nullable value type. For example, if we have a conversion from struct S to string, // then a "lifted" conversion from S? to string is considered by the native compiler to exist, // with the semantics of "s.HasValue ? (string)s.Value : (string)null". The Roslyn compiler // perpetuates this error for the sake of backwards compatibility. Debug.Assert((object)rewrittenType != null); Debug.Assert(rewrittenOperand.Type.IsNullableType()); BoundExpression optimized = OptimizeLiftedUserDefinedConversion(syntax, rewrittenOperand, conversionKind, method, rewrittenType); if (optimized != null) { return optimized; } // We have no optimizations we can perform. If the return type of the // conversion method is a non-nullable value type R then we lower this as: // // temp = operand // temp.HasValue ? new R?(op_Whatever(temp.GetValueOrDefault())) : default(R?) // // Otherwise, if the return type of the conversion is a nullable value type, reference type // or pointer type P, then we lower this as: // // temp = operand // temp.HasValue ? op_Whatever(temp.GetValueOrDefault()) : default(P) BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = factory.StoreToTemp(rewrittenOperand, out tempAssignment); MethodSymbol get_HasValue = GetNullableMethod(syntax, boundTemp.Type, SpecialMember.System_Nullable_T_get_HasValue); MethodSymbol getValueOrDefault = GetNullableMethod(syntax, boundTemp.Type, SpecialMember.System_Nullable_T_GetValueOrDefault); // temp.HasValue BoundExpression condition = BoundCall.Synthesized(syntax, boundTemp, get_HasValue); // temp.GetValueOrDefault() BoundCall callGetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault); // op_Whatever(temp.GetValueOrDefault()) BoundCall userDefinedCall = BoundCall.Synthesized(syntax, null, method, callGetValueOrDefault); // new R?(op_Whatever(temp.GetValueOrDefault()) BoundExpression consequence = MakeLiftedUserDefinedConversionConsequence(userDefinedCall, rewrittenType); // default(R?) BoundExpression alternative = new BoundDefaultOperator(syntax, rewrittenType); // temp.HasValue ? new R?(op_Whatever(temp.GetValueOrDefault())) : default(R?) BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: rewrittenType); // temp = operand // temp.HasValue ? new R?(op_Whatever(temp.GetValueOrDefault())) : default(R?) return new BoundSequence( syntax: syntax, locals: ImmutableArray.Create(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create<BoundExpression>(tempAssignment), value: conditionalExpression, type: rewrittenType); }
private BoundExpression RewriteFullyLiftedBuiltInConversion( CSharpSyntaxNode syntax, BoundExpression operand, ConversionKind kind, bool @checked, TypeSymbol type) { // SPEC: If the nullable conversion is from S? to T?: // SPEC: * If the source HasValue property is false the result // SPEC: is a null value of type T?. // SPEC: * Otherwise the conversion is evaluated as an unwrapping // SPEC: from S? to S, followed by the underlying conversion from // SPEC: S to T, followed by a wrapping from T to T? BoundExpression optimized = OptimizeLiftedBuiltInConversion(syntax, operand, kind, @checked, type); if (optimized != null) { return optimized; } // We are unable to optimize the conversion. "(T?)s" is generated as: // S? temp = s; // temp.HasValue ? new T?((T)temp.GetValueOrDefault()) : default(T?) BoundAssignmentOperator tempAssignment; var boundTemp = factory.StoreToTemp(operand, out tempAssignment); MethodSymbol get_HasValue = GetNullableMethod(syntax, boundTemp.Type, SpecialMember.System_Nullable_T_get_HasValue); MethodSymbol getValueOrDefault = GetNullableMethod(syntax, boundTemp.Type, SpecialMember.System_Nullable_T_GetValueOrDefault); BoundExpression condition = BoundCall.Synthesized(syntax, boundTemp, get_HasValue); BoundExpression consequence = new BoundObjectCreationExpression( syntax, GetNullableMethod(syntax, type, SpecialMember.System_Nullable_T__ctor), MakeConversion( BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault), type.GetNullableUnderlyingType(), @checked)); BoundExpression alternative = new BoundDefaultOperator(syntax, null, type); BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: type); return new BoundSequence( syntax: syntax, locals: ImmutableArray.Create(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create<BoundExpression>(tempAssignment), value: conditionalExpression, type: type); }
/// <summary> /// Gets the default value for the <paramref name="parameter"/>. /// </summary> /// <param name="syntax"> /// A syntax node corresponding to the invocation. /// </param> /// <param name="parameter"> /// A parameter to get the default value for. /// </param> /// <param name="enableCallerInfo"> /// Indicates if caller info is to be enabled when processing this optional parameter. /// The value <see cref="ThreeState.Unknown"/> means the decision is to be made based on the shape of the <paramref name="syntax"/> node. /// </param> /// <remarks> /// DELIBERATE SPEC VIOLATION: When processing an implicit invocation of an <c>Add</c> method generated /// for an element-initializer in a collection-initializer, the parameter <paramref name="enableCallerInfo"/> /// is set to <see cref="ThreeState.True"/>. It means that if the optional parameter is annotated with <see cref="CallerLineNumberAttribute"/>, /// <see cref="CallerFilePathAttribute"/> or <see cref="CallerMemberNameAttribute"/>, and there is no explicit argument corresponding to it, /// we will provide caller information as a value of this parameter. /// This is done to match the native compiler behavior and user requests (see http://roslyn.codeplex.com/workitem/171). This behavior /// does not match the C# spec that currently requires to provide caller information only in explicit invocations and query expressions. /// </remarks> private BoundExpression GetDefaultParameterValue(SyntaxNode syntax, ParameterSymbol parameter, ThreeState enableCallerInfo) { // TODO: Ideally, the enableCallerInfo parameter would be of just bool type with only 'true' and 'false' values, and all callers // explicitly provided one of those values, so that we do not rely on shape of syntax nodes in the rewriter. There are not many immediate callers, // but often the immediate caller does not have the required information, so all possible call chains should be analyzed and possibly updated // to pass this information, and this might be a big task. We should consider doing this when the time permits. TypeSymbol parameterType = parameter.Type; Debug.Assert(parameter.IsOptional); ConstantValue defaultConstantValue = parameter.ExplicitDefaultConstantValue; BoundExpression defaultValue; SourceLocation callerSourceLocation; // For compatibility with the native compiler we treat all bad imported constant // values as default(T). if (defaultConstantValue != null && defaultConstantValue.IsBad) { defaultConstantValue = ConstantValue.Null; } if (parameter.IsCallerLineNumber && ((callerSourceLocation = GetCallerLocation(syntax, enableCallerInfo)) != null)) { int line = callerSourceLocation.SourceTree.GetDisplayLineNumber(callerSourceLocation.SourceSpan); BoundExpression lineLiteral = MakeLiteral(syntax, ConstantValue.Create(line), _compilation.GetSpecialType(SpecialType.System_Int32)); if (parameterType.IsNullableType()) { defaultValue = MakeConversionNode(lineLiteral, parameterType.GetNullableUnderlyingType(), false); // wrap it in a nullable ctor. defaultValue = new BoundObjectCreationExpression( syntax, GetNullableMethod(syntax, parameterType, SpecialMember.System_Nullable_T__ctor), defaultValue); } else { defaultValue = MakeConversionNode(lineLiteral, parameterType, false); } } else if (parameter.IsCallerFilePath && ((callerSourceLocation = GetCallerLocation(syntax, enableCallerInfo)) != null)) { string path = callerSourceLocation.SourceTree.GetDisplayPath(callerSourceLocation.SourceSpan, _compilation.Options.SourceReferenceResolver); BoundExpression memberNameLiteral = MakeLiteral(syntax, ConstantValue.Create(path), _compilation.GetSpecialType(SpecialType.System_String)); defaultValue = MakeConversionNode(memberNameLiteral, parameterType, false); } else if (parameter.IsCallerMemberName && ((callerSourceLocation = GetCallerLocation(syntax, enableCallerInfo)) != null)) { string memberName; switch (_factory.TopLevelMethod.MethodKind) { case MethodKind.Constructor: case MethodKind.StaticConstructor: // See if the code is actually part of a field, field-like event or property initializer and return the name of the corresponding member. var memberDecl = syntax.Ancestors().OfType<MemberDeclarationSyntax>().FirstOrDefault(); if (memberDecl != null) { BaseFieldDeclarationSyntax fieldDecl; if (memberDecl.Kind() == SyntaxKind.PropertyDeclaration) { var propDecl = (PropertyDeclarationSyntax)memberDecl; EqualsValueClauseSyntax initializer = propDecl.Initializer; if (initializer != null && initializer.Span.Contains(syntax.Span)) { memberName = propDecl.Identifier.ValueText; break; } } else if ((fieldDecl = memberDecl as BaseFieldDeclarationSyntax) != null) { memberName = null; foreach (VariableDeclaratorSyntax varDecl in fieldDecl.Declaration.Variables) { EqualsValueClauseSyntax initializer = varDecl.Initializer; if (initializer != null && initializer.Span.Contains(syntax.Span)) { memberName = varDecl.Identifier.ValueText; break; } } if (memberName != null) { break; } } } goto default; default: memberName = _factory.TopLevelMethod.GetMemberCallerName(); break; } BoundExpression memberNameLiteral = MakeLiteral(syntax, ConstantValue.Create(memberName), _compilation.GetSpecialType(SpecialType.System_String)); defaultValue = MakeConversionNode(memberNameLiteral, parameterType, false); } else if (defaultConstantValue == ConstantValue.NotAvailable) { // There is no constant value given for the parameter in source/metadata. if (parameterType.IsDynamic() || parameterType.SpecialType == SpecialType.System_Object) { // We have something like M([Optional] object x). We have special handling for such situations. defaultValue = GetDefaultParameterSpecial(syntax, parameter); } else { // The argument to M([Optional] int x) becomes default(int) defaultValue = new BoundDefaultOperator(syntax, parameterType); } } else if (defaultConstantValue.IsNull && parameterType.IsValueType) { // We have something like M(int? x = null) or M(S x = default(S)), // so replace the argument with default(int?). defaultValue = new BoundDefaultOperator(syntax, parameterType); } else if (parameterType.IsNullableType()) { // We have something like M(double? x = 1.23), so replace the argument // with new double?(1.23). TypeSymbol constantType = _compilation.GetSpecialType(defaultConstantValue.SpecialType); defaultValue = MakeLiteral(syntax, defaultConstantValue, constantType); // The parameter's underlying type might not match the constant type. For example, we might have // a default value of 5 (an integer) but a parameter type of decimal?. defaultValue = MakeConversionNode(defaultValue, parameterType.GetNullableUnderlyingType(), @checked: false, acceptFailingConversion: true); // Finally, wrap it in a nullable ctor. defaultValue = new BoundObjectCreationExpression( syntax, GetNullableMethod(syntax, parameterType, SpecialMember.System_Nullable_T__ctor), defaultValue); } else if (defaultConstantValue.IsNull || defaultConstantValue.IsBad) { defaultValue = MakeLiteral(syntax, defaultConstantValue, parameterType); } else { // We have something like M(double x = 1.23), so replace the argument with 1.23. TypeSymbol constantType = _compilation.GetSpecialType(defaultConstantValue.SpecialType); defaultValue = MakeLiteral(syntax, defaultConstantValue, constantType); // The parameter type might not match the constant type. defaultValue = MakeConversionNode(defaultValue, parameterType, @checked: false, acceptFailingConversion: true); } return defaultValue; }
private BoundExpression GetDefaultParameterSpecial(SyntaxNode syntax, ParameterSymbol parameter) { // We have a call to a method M([Optional] object x) which omits the argument. The value we generate // for the argument depends on the presence or absence of other attributes. The rules are: // // * If the parameter is marked as [MarshalAs(Interface)], [MarshalAs(IUnknown)] or [MarshalAs(IDispatch)] // then the argument is null. // * Otherwise, if the parameter is marked as [IUnknownConstant] then the argument is // new UnknownWrapper(null) // * Otherwise, if the parameter is marked as [IDispatchConstant] then the argument is // new DispatchWrapper(null) // * Otherwise, the argument is Type.Missing. BoundExpression defaultValue; if (parameter.IsMarshalAsObject) { // default(object) defaultValue = new BoundDefaultOperator(syntax, parameter.Type); } else if (parameter.IsIUnknownConstant) { // new UnknownWrapper(default(object)) var methodSymbol = (MethodSymbol)_compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_InteropServices_UnknownWrapper__ctor); var argument = new BoundDefaultOperator(syntax, parameter.Type); defaultValue = new BoundObjectCreationExpression(syntax, methodSymbol, argument); } else if (parameter.IsIDispatchConstant) { // new DispatchWrapper(default(object)) var methodSymbol = (MethodSymbol)_compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_InteropServices_DispatchWrapper__ctor); var argument = new BoundDefaultOperator(syntax, parameter.Type); defaultValue = new BoundObjectCreationExpression(syntax, methodSymbol, argument); } else { // Type.Missing var fieldSymbol = (FieldSymbol)_compilation.GetWellKnownTypeMember(WellKnownMember.System_Type__Missing); defaultValue = new BoundFieldAccess(syntax, null, fieldSymbol, ConstantValue.NotAvailable); } defaultValue = MakeConversionNode(defaultValue, parameter.Type, @checked: false); return defaultValue; }
private BoundExpression LowerLiftedBinaryArithmeticOperator( CSharpSyntaxNode syntax, BinaryOperatorKind kind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type, MethodSymbol method) { // We have a lifted * / % + - << >> ^ & | binary operator. We begin with trivial // optimizations; if both sides are null or neither side is null then we can // eliminate the lifting altogether. BoundExpression optimized = OptimizeLiftedBinaryArithmetic(syntax, kind, loweredLeft, loweredRight, type, method); if (optimized != null) { return optimized; } // We now know that neither side is null. However, we might have an operand that is known // to be non-null. If neither side is known to be non-null then we generate: // // S? tempX = left; // S? tempY = right; // R? r = tempX.HasValue & tempY.HasValue ? // new R?(tempX.GetValueOrDefault() OP tempY.GetValueOrDefault()) : // default(R?); // // If one of the operands, say the right, is non-null, then we generate: // // S? tempX = left; // S tempY = right; // not null // R? r = tempX.HasValue ? // new R?(tempX.GetValueOrDefault() OP tempY) : // default(R?); // var sideeffects = ArrayBuilder<BoundExpression>.GetInstance(); var locals = ArrayBuilder<LocalSymbol>.GetInstance(); BoundExpression leftNeverNull = NullableAlwaysHasValue(loweredLeft); BoundExpression rightNeverNull = NullableAlwaysHasValue(loweredRight); BoundExpression boundTempX = leftNeverNull ?? loweredLeft; boundTempX = CaptureNullableOperandInTempIfNeeded(boundTempX, sideeffects, locals); BoundExpression boundTempY = rightNeverNull ?? loweredRight; boundTempY = CaptureNullableOperandInTempIfNeeded(boundTempY, sideeffects, locals); BoundExpression callX_GetValueOrDefault = MakeOptimizedGetValueOrDefault(syntax, boundTempX); BoundExpression callY_GetValueOrDefault = MakeOptimizedGetValueOrDefault(syntax, boundTempY); BoundExpression callX_HasValue = MakeOptimizedHasValue(syntax, boundTempX); BoundExpression callY_HasValue = MakeOptimizedHasValue(syntax, boundTempY); // tempX.HasValue & tempY.HasValue TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression condition = MakeBinaryOperator(syntax, BinaryOperatorKind.BoolAnd, callX_HasValue, callY_HasValue, boolType, null); // new R?(tempX.GetValueOrDefault() OP tempY.GetValueOrDefault) BoundExpression consequence = MakeLiftedBinaryOperatorConsequence(syntax, kind, callX_GetValueOrDefault, callY_GetValueOrDefault, type, method); // default(R?) BoundExpression alternative = new BoundDefaultOperator(syntax, null, type); // tempX.HasValue & tempY.HasValue ? // new R?(tempX.GetValueOrDefault() OP tempY.GetValueOrDefault()) : // default(R?); BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: type); return new BoundSequence( syntax: syntax, locals: locals.ToImmutableAndFree(), sideEffects: sideeffects.ToImmutableAndFree(), value: conditionalExpression, type: type); }
private BoundExpression MakeUserDefinedIncrementOperator(BoundIncrementOperator node, BoundExpression rewrittenValueToIncrement) { Debug.Assert((object)node.MethodOpt != null); Debug.Assert(node.MethodOpt.ParameterCount == 1); bool isLifted = node.OperatorKind.IsLifted(); bool @checked = node.OperatorKind.IsChecked(); BoundExpression rewrittenArgument = rewrittenValueToIncrement; CSharpSyntaxNode syntax = node.Syntax; TypeSymbol type = node.MethodOpt.ParameterTypes[0]; if (isLifted) { type = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(type); Debug.Assert(node.MethodOpt.ParameterTypes[0] == node.MethodOpt.ReturnType); } if (!node.OperandConversion.IsIdentity) { rewrittenArgument = MakeConversionNode( syntax: syntax, rewrittenOperand: rewrittenValueToIncrement, conversion: node.OperandConversion, rewrittenType: type, @checked: @checked); } if (!isLifted) { return BoundCall.Synthesized(syntax, null, node.MethodOpt, rewrittenArgument); } // S? temp = operand; // S? r = temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : // default(S?); // Unlike the other unary operators, we do not attempt to optimize nullable user-defined // increment or decrement. The operand is a variable (or property), and so we do not know if // it is always null/never null. BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = _factory.StoreToTemp(rewrittenArgument, out tempAssignment); MethodSymbol getValueOrDefault = GetNullableMethod(syntax, type, SpecialMember.System_Nullable_T_GetValueOrDefault); MethodSymbol ctor = GetNullableMethod(syntax, type, SpecialMember.System_Nullable_T__ctor); // temp.HasValue BoundExpression condition = MakeNullableHasValue(node.Syntax, boundTemp); // temp.GetValueOrDefault() BoundExpression call_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault); // op_Increment(temp.GetValueOrDefault()) BoundExpression userDefinedCall = BoundCall.Synthesized(syntax, null, node.MethodOpt, call_GetValueOrDefault); // new S?(op_Increment(temp.GetValueOrDefault())) BoundExpression consequence = new BoundObjectCreationExpression(syntax, ctor, userDefinedCall); // default(S?) BoundExpression alternative = new BoundDefaultOperator(syntax, null, type); // temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : // default(S?); BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: type); // temp = operand; // temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : // default(S?); return new BoundSequence( syntax: syntax, locals: ImmutableArray.Create<LocalSymbol>(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create<BoundExpression>(tempAssignment), value: conditionalExpression, type: type); }
private BoundExpression MakeUserDefinedIncrementOperator(BoundIncrementOperator node, BoundExpression rewrittenValueToIncrement) { Debug.Assert((object)node.MethodOpt != null); Debug.Assert(node.MethodOpt.ParameterCount == 1); bool isLifted = node.OperatorKind.IsLifted(); bool @checked = node.OperatorKind.IsChecked(); BoundExpression rewrittenArgument = rewrittenValueToIncrement; CSharpSyntaxNode syntax = node.Syntax; TypeSymbol type = node.MethodOpt.ParameterTypes[0]; if (isLifted) { type = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(type); Debug.Assert(node.MethodOpt.ParameterTypes[0] == node.MethodOpt.ReturnType); } if (!node.OperandConversion.IsIdentity) { rewrittenArgument = MakeConversion( syntax: syntax, rewrittenOperand: rewrittenValueToIncrement, conversion: node.OperandConversion, rewrittenType: type, @checked: @checked); } if (!isLifted) { return(BoundCall.Synthesized(syntax, null, node.MethodOpt, rewrittenArgument)); } // S? temp = operand; // S? r = temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : // default(S?); // Unlike the other unary operators, we do not attempt to optimize nullable user-defined // increment or decrement. The operand is a variable (or property), and so we do not know if // it is always null/never null. BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = _factory.StoreToTemp(rewrittenArgument, out tempAssignment); MethodSymbol getValueOrDefault = GetNullableMethod(syntax, type, SpecialMember.System_Nullable_T_GetValueOrDefault); MethodSymbol ctor = GetNullableMethod(syntax, type, SpecialMember.System_Nullable_T__ctor); // temp.HasValue BoundExpression condition = MakeNullableHasValue(node.Syntax, boundTemp); // temp.GetValueOrDefault() BoundExpression call_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault); // op_Increment(temp.GetValueOrDefault()) BoundExpression userDefinedCall = BoundCall.Synthesized(syntax, null, node.MethodOpt, call_GetValueOrDefault); // new S?(op_Increment(temp.GetValueOrDefault())) BoundExpression consequence = new BoundObjectCreationExpression(syntax, ctor, userDefinedCall); // default(S?) BoundExpression alternative = new BoundDefaultOperator(syntax, null, type); // temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : // default(S?); BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: type); // temp = operand; // temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : // default(S?); return(new BoundSequence( syntax: syntax, locals: ImmutableArray.Create <LocalSymbol>(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment), value: conditionalExpression, type: type)); }
private BoundExpression LowerLiftedUnaryOperator( UnaryOperatorKind kind, CSharpSyntaxNode syntax, MethodSymbol method, BoundExpression loweredOperand, TypeSymbol type) { // First, an optimization. If we know that the operand is always null then // we can simply lower to the alternative. BoundExpression optimized = OptimizeLiftedUnaryOperator(kind, syntax, method, loweredOperand, type); if (optimized != null) { return(optimized); } // We do not know whether the operand is null or non-null, so we generate: // // S? temp = operand; // R? r = temp.HasValue ? // new R?(OP(temp.GetValueOrDefault())) : // default(R?); BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = _factory.StoreToTemp(loweredOperand, out tempAssignment); MethodSymbol getValueOrDefault = GetNullableMethod(syntax, boundTemp.Type, SpecialMember.System_Nullable_T_GetValueOrDefault); // temp.HasValue BoundExpression condition = MakeNullableHasValue(syntax, boundTemp); // temp.GetValueOrDefault() BoundExpression call_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault); // new R?(temp.GetValueOrDefault()) BoundExpression consequence = GetLiftedUnaryOperatorConsequence(kind, syntax, method, type, call_GetValueOrDefault); // default(R?) BoundExpression alternative = new BoundDefaultOperator(syntax, null, type); // temp.HasValue ? // new R?(OP(temp.GetValueOrDefault())) : // default(R?); BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: type); // temp = operand; // temp.HasValue ? // new R?(OP(temp.GetValueOrDefault())) : // default(R?); return(new BoundSequence( syntax: syntax, locals: ImmutableArray.Create <LocalSymbol>(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment), value: conditionalExpression, type: type)); }
/// <summary> /// The flow analysis pass. This pass reports required diagnostics for unreachable /// statements and uninitialized variables (through the call to FlowAnalysisWalker.Analyze), /// and inserts a final return statement if the end of a void-returning method is reachable. /// </summary> /// <param name="method">the method to be analyzed</param> /// <param name="block">the method's body</param> /// <param name="diagnostics">the receiver of the reported diagnostics</param> /// <param name="hasTrailingExpression">indicates whether this Script had a trailing expression</param> /// <param name="originalBodyNested">the original method body is the last statement in the block</param> /// <returns>the rewritten block for the method (with a return statement possibly inserted)</returns> public static BoundBlock Rewrite( MethodSymbol method, BoundBlock block, DiagnosticBag diagnostics, bool hasTrailingExpression, bool originalBodyNested) { #if DEBUG // We should only see a trailingExpression if we're in a Script initializer. Debug.Assert(!hasTrailingExpression || method.IsScriptInitializer); var initialDiagnosticCount = diagnostics.ToReadOnly().Length; #endif var compilation = method.DeclaringCompilation; if (method.ReturnsVoid || method.IsIterator || (method.IsAsync && compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task) == method.ReturnType)) { // we don't analyze synthesized void methods. if ((method.IsImplicitlyDeclared && !method.IsScriptInitializer) || Analyze(compilation, method, block, diagnostics)) { block = AppendImplicitReturn(block, method, (CSharpSyntaxNode)(method as SourceMethodSymbol)?.BodySyntax, originalBodyNested); } } else if (Analyze(compilation, method, block, diagnostics)) { // If the method is a lambda expression being converted to a non-void delegate type // and the end point is reachable then suppress the error here; a special error // will be reported by the lambda binder. Debug.Assert(method.MethodKind != MethodKind.AnonymousFunction); // Add implicit "return default(T)" if this is a submission that does not have a trailing expression. var submissionResultType = (method as SynthesizedInteractiveInitializerMethod)?.ResultType; if (!hasTrailingExpression && ((object)submissionResultType != null)) { Debug.Assert(submissionResultType.SpecialType != SpecialType.System_Void); var trailingExpression = new BoundDefaultOperator(method.GetNonNullSyntaxNode(), submissionResultType); var newStatements = block.Statements.Add(new BoundReturnStatement(trailingExpression.Syntax, trailingExpression)); block = new BoundBlock(block.Syntax, ImmutableArray <LocalSymbol> .Empty, newStatements) { WasCompilerGenerated = true }; #if DEBUG // It should not be necessary to repeat analysis after adding this node, because adding a trailing // return in cases where one was missing should never produce different Diagnostics. var flowAnalysisDiagnostics = DiagnosticBag.GetInstance(); Debug.Assert(!Analyze(compilation, method, block, flowAnalysisDiagnostics)); Debug.Assert(flowAnalysisDiagnostics.ToReadOnly().SequenceEqual(diagnostics.ToReadOnly().Skip(initialDiagnosticCount))); flowAnalysisDiagnostics.Free(); #endif } // If there's more than one location, then the method is partial and we // have already reported a non-void partial method error. else if (method.Locations.Length == 1) { diagnostics.Add(ErrorCode.ERR_ReturnExpected, method.Locations[0], method); } } return(block); }
public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node) { Debug.Assert(node != null); // Rewrite the arguments. // NOTE: We may need additional argument rewriting such as generating a params array, // re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. // NOTE: This is done later by MakeArguments, for now we just lower each argument. var rewrittenArguments = VisitList(node.Arguments); // We have already lowered each argument, but we may need some additional rewriting for the arguments, // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. ImmutableArray <LocalSymbol> temps; ImmutableArray <RefKind> argumentRefKindsOpt = node.ArgumentRefKindsOpt; rewrittenArguments = MakeArguments(node.Syntax, rewrittenArguments, node.Constructor, node.Constructor, node.Expanded, node.ArgsToParamsOpt, ref argumentRefKindsOpt, out temps); BoundExpression rewrittenObjectCreation; if (inExpressionLambda) { if (!temps.IsDefaultOrEmpty) { throw ExceptionUtilities.UnexpectedValue(temps.Length); } rewrittenObjectCreation = node.UpdateArgumentsAndInitializer(rewrittenArguments, MakeObjectCreationInitializerForExpressionTree(node.InitializerExpressionOpt), changeTypeOpt: node.Constructor.ContainingType); if (node.Type.IsInterfaceType()) { Debug.Assert(rewrittenObjectCreation.Type == ((NamedTypeSymbol)node.Type).ComImportCoClass); rewrittenObjectCreation = MakeConversion(rewrittenObjectCreation, node.Type, false, false); } return(rewrittenObjectCreation); } rewrittenObjectCreation = node.UpdateArgumentsAndInitializer(rewrittenArguments, newInitializerExpression: null, changeTypeOpt: node.Constructor.ContainingType); // replace "new S()" with a default struct ctor with "default(S)" if (node.Constructor.IsDefaultValueTypeConstructor()) { rewrittenObjectCreation = new BoundDefaultOperator(rewrittenObjectCreation.Syntax, rewrittenObjectCreation.Type); } if (!temps.IsDefaultOrEmpty) { rewrittenObjectCreation = new BoundSequence( node.Syntax, temps, ImmutableArray <BoundExpression> .Empty, rewrittenObjectCreation, node.Type); } if (node.Type.IsInterfaceType()) { Debug.Assert(rewrittenObjectCreation.Type == ((NamedTypeSymbol)node.Type).ComImportCoClass); rewrittenObjectCreation = MakeConversion(rewrittenObjectCreation, node.Type, false, false); } if (node.InitializerExpressionOpt == null || node.InitializerExpressionOpt.HasErrors) { return(rewrittenObjectCreation); } return(MakeObjectCreationWithInitializer(node.Syntax, rewrittenObjectCreation, node.InitializerExpressionOpt, node.Type)); }
private BoundExpression GetDefaultParameterValue(CSharpSyntaxNode syntax, ParameterSymbol parameter) { TypeSymbol parameterType = parameter.Type; ConstantValue defaultConstantValue = parameter.ExplicitDefaultConstantValue; BoundExpression defaultValue; SourceLocation callerSourceLocation; if (parameter.IsCallerLineNumber && ((callerSourceLocation = GetCallerLocation(syntax)) != null)) { int line = callerSourceLocation.SourceTree.GetDisplayLineNumber(callerSourceLocation.SourceSpan); BoundExpression lineLiteral = MakeLiteral(syntax, ConstantValue.Create(line), compilation.GetSpecialType(SpecialType.System_Int32)); if (parameterType.IsNullableType()) { defaultValue = MakeConversion(lineLiteral, parameterType.GetNullableUnderlyingType(), false); // wrap it in a nullable ctor. defaultValue = new BoundObjectCreationExpression( syntax, GetNullableMethod(syntax, parameterType, SpecialMember.System_Nullable_T__ctor), defaultValue); } else { defaultValue = MakeConversion(lineLiteral, parameterType, false); } } else if (parameter.IsCallerFilePath && ((callerSourceLocation = GetCallerLocation(syntax)) != null)) { string path = callerSourceLocation.SourceTree.GetDisplayPath(callerSourceLocation.SourceSpan, compilation.Options.SourceReferenceResolver); BoundExpression memberNameLiteral = MakeLiteral(syntax, ConstantValue.Create(path), compilation.GetSpecialType(SpecialType.System_String)); defaultValue = MakeConversion(memberNameLiteral, parameterType, false); } else if (parameter.IsCallerMemberName && ((callerSourceLocation = GetCallerLocation(syntax)) != null)) { string memberName = this.factory.TopLevelMethod.GetMemberCallerName(); BoundExpression memberNameLiteral = MakeLiteral(syntax, ConstantValue.Create(memberName), compilation.GetSpecialType(SpecialType.System_String)); defaultValue = MakeConversion(memberNameLiteral, parameterType, false); } else if (defaultConstantValue == ConstantValue.NotAvailable) { // There is no constant value given for the parameter in source/metadata. if (parameterType.IsDynamic() || parameterType.SpecialType == SpecialType.System_Object) { // We have something like M([Optional] object x). We have special handling for such situations. defaultValue = GetDefaultParameterSpecial(syntax, parameter); } else { // The argument to M([Optional] int x) becomes default(int) defaultValue = new BoundDefaultOperator(syntax, parameterType); } } else if (defaultConstantValue.IsNull && parameterType.IsValueType) { // We have something like M(int? x = null) or M(S x = default(S)), // so replace the argument with default(int?). defaultValue = new BoundDefaultOperator(syntax, parameterType); } else if (parameterType.IsNullableType()) { // We have something like M(double? x = 1.23), so replace the argument // with new double?(1.23). TypeSymbol constantType = compilation.GetSpecialType(defaultConstantValue.SpecialType); defaultValue = MakeLiteral(syntax, defaultConstantValue, constantType); // The parameter's underlying type might not match the constant type. For example, we might have // a default value of 5 (an integer) but a parameter type of decimal?. defaultValue = MakeConversion(defaultValue, parameterType.GetNullableUnderlyingType(), @checked: false, acceptFailingConversion: true); // Finally, wrap it in a nullable ctor. defaultValue = new BoundObjectCreationExpression( syntax, GetNullableMethod(syntax, parameterType, SpecialMember.System_Nullable_T__ctor), defaultValue); } else if (defaultConstantValue.IsNull || defaultConstantValue.IsBad) { defaultValue = MakeLiteral(syntax, defaultConstantValue, parameterType); } else { // We have something like M(double = 1.23), so replace the argument with 1.23. TypeSymbol constantType = compilation.GetSpecialType(defaultConstantValue.SpecialType); defaultValue = MakeLiteral(syntax, defaultConstantValue, constantType); // The parameter type might not match the constant type. defaultValue = MakeConversion(defaultValue, parameterType, @checked: false, acceptFailingConversion: true); } return(defaultValue); }
private BoundExpression MakeNewT(CSharpSyntaxNode syntax, TypeParameterSymbol typeParameter) { // How "new T()" is rewritten depends on whether T is known to be a value // type, a reference type, or neither (see OperatorRewriter::VisitNEWTYVAR). if (typeParameter.IsValueType) { // "new T()" rewritten as: "default(T)". return(new BoundDefaultOperator(syntax, type: typeParameter)); } // For types not known to be value types, "new T()" requires // Activator.CreateInstance<T>(). MethodSymbol method; if (!this.TryGetWellKnownTypeMember(syntax, WellKnownMember.System_Activator__CreateInstance_T, out method)) { return(new BoundDefaultOperator(syntax, null, type: typeParameter, hasErrors: true)); } Debug.Assert((object)method != null); method = method.Construct(ImmutableArray.Create <TypeSymbol>(typeParameter)); var createInstanceCall = new BoundCall( syntax, null, method, ImmutableArray <BoundExpression> .Empty, default(ImmutableArray <string>), default(ImmutableArray <RefKind>), isDelegateCall: false, expanded: false, invokedAsExtensionMethod: false, argsToParamsOpt: default(ImmutableArray <int>), resultKind: LookupResultKind.Viable, type: typeParameter); if (typeParameter.IsReferenceType) { // "new T()" is rewritten as: "Activator.CreateInstance<T>()". return(createInstanceCall); } else { // "new T()" is rewritten as: "(null == (object)default(T)) ? Activator.CreateInstance<T>() : default(T)". var defaultT = new BoundDefaultOperator(syntax, type: typeParameter); return(new BoundConditionalOperator( syntax, MakeNullCheck( syntax: syntax, rewrittenExpr: MakeConversion( syntax: syntax, rewrittenOperand: defaultT, conversionKind: ConversionKind.Boxing, rewrittenType: this.compilation.GetSpecialType(SpecialType.System_Object), @checked: false), operatorKind: BinaryOperatorKind.Equal), createInstanceCall, defaultT, constantValueOpt: null, type: typeParameter)); } }
private BoundReturnStatement BindReturn(ReturnStatementSyntax syntax, DiagnosticBag diagnostics) { var expressionSyntax = syntax.Expression; BoundExpression arg = null; if (expressionSyntax != null) { arg = BindValue(expressionSyntax, diagnostics, BindValueKind.RValue); } else { // If this is a void return statement in a script, return default(T). var interactiveInitializerMethod = this.ContainingMemberOrLambda as SynthesizedInteractiveInitializerMethod; if (interactiveInitializerMethod != null) { arg = new BoundDefaultOperator(interactiveInitializerMethod.GetNonNullSyntaxNode(), interactiveInitializerMethod.ResultType); } } bool hasErrors; if (IsDirectlyInIterator) { diagnostics.Add(ErrorCode.ERR_ReturnInIterator, syntax.ReturnKeyword.GetLocation()); hasErrors = true; } else if (arg != null) { hasErrors = arg.HasErrors || ((object)arg.Type != null && arg.Type.IsErrorType()); } else { hasErrors = false; } if (hasErrors) { return new BoundReturnStatement(syntax, arg, hasErrors: true); } TypeSymbol retType = GetCurrentReturnType(); // The return type could be null; we might be attempting to infer the return type either // because of method type inference, or because we are attempting to do error analysis // on a lambda expression of unknown return type. if ((object)retType != null) { if (retType.SpecialType == SpecialType.System_Void || IsTaskReturningAsyncMethod()) { if (arg != null) { var container = this.ContainingMemberOrLambda; var lambda = container as LambdaSymbol; if ((object)lambda != null) { // Error case: void-returning or async task-returning method or lambda with "return x;" var errorCode = retType.SpecialType == SpecialType.System_Void ? ErrorCode.ERR_RetNoObjectRequiredLambda : ErrorCode.ERR_TaskRetNoObjectRequiredLambda; // Anonymous function converted to a void returning delegate cannot return a value Error(diagnostics, errorCode, syntax.ReturnKeyword); // COMPATIBILITY: The native compiler also produced an error // COMPATIBILITY: "Cannot convert lambda expression to delegate type 'Action' because some of the // COMPATIBILITY: return types in the block are not implicitly convertible to the delegate return type" // COMPATIBILITY: This error doesn't make sense in the "void" case because the whole idea of // COMPATIBILITY: "conversion to void" is a bit unusual, and we've already given a good error. } else { // Error case: void-returning or async task-returning method or lambda with "return x;" var errorCode = retType.SpecialType == SpecialType.System_Void ? ErrorCode.ERR_RetNoObjectRequired : ErrorCode.ERR_TaskRetNoObjectRequired; Error(diagnostics, errorCode, syntax.ReturnKeyword, container); } } } else { if (arg == null) { // Error case: non-void-returning or Task<T>-returning method or lambda but just have "return;" var requiredType = IsGenericTaskReturningAsyncMethod() ? retType.GetMemberTypeArgumentsNoUseSiteDiagnostics().Single() : retType; Error(diagnostics, ErrorCode.ERR_RetObjectRequired, syntax.ReturnKeyword, requiredType); } else { arg = CreateReturnConversion(syntax, diagnostics, arg, retType); } } } else { // Check that the returned expression is not void. if ((object)arg?.Type != null && arg.Type.SpecialType == SpecialType.System_Void) { Error(diagnostics, ErrorCode.ERR_CantReturnVoid, expressionSyntax); } } return new BoundReturnStatement(syntax, arg); }
private BoundExpression LowerLiftedUnaryOperator( UnaryOperatorKind kind, CSharpSyntaxNode syntax, MethodSymbol method, BoundExpression loweredOperand, TypeSymbol type) { // First, an optimization. If we know that the operand is always null then // we can simply lower to the alternative. BoundExpression optimized = OptimizeLiftedUnaryOperator(kind, syntax, method, loweredOperand, type); if (optimized != null) { return optimized; } // We do not know whether the operand is null or non-null, so we generate: // // S? temp = operand; // R? r = temp.HasValue ? // new R?(OP(temp.GetValueOrDefault())) : // default(R?); BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = _factory.StoreToTemp(loweredOperand, out tempAssignment); MethodSymbol getValueOrDefault = GetNullableMethod(syntax, boundTemp.Type, SpecialMember.System_Nullable_T_GetValueOrDefault); // temp.HasValue BoundExpression condition = MakeNullableHasValue(syntax, boundTemp); // temp.GetValueOrDefault() BoundExpression call_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault); // new R?(temp.GetValueOrDefault()) BoundExpression consequence = GetLiftedUnaryOperatorConsequence(kind, syntax, method, type, call_GetValueOrDefault); // default(R?) BoundExpression alternative = new BoundDefaultOperator(syntax, null, type); // temp.HasValue ? // new R?(OP(temp.GetValueOrDefault())) : // default(R?); BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: type); // temp = operand; // temp.HasValue ? // new R?(OP(temp.GetValueOrDefault())) : // default(R?); return new BoundSequence( syntax: syntax, locals: ImmutableArray.Create<LocalSymbol>(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create<BoundExpression>(tempAssignment), value: conditionalExpression, type: type); }
private BoundExpression MakeNewT(CSharpSyntaxNode syntax, TypeParameterSymbol typeParameter) { // How "new T()" is rewritten depends on whether T is known to be a value // type, a reference type, or neither (see OperatorRewriter::VisitNEWTYVAR). if (typeParameter.IsValueType) { // "new T()" rewritten as: "default(T)". return new BoundDefaultOperator(syntax, type: typeParameter); } // For types not known to be value types, "new T()" requires // Activator.CreateInstance<T>(). MethodSymbol method; if (!this.TryGetWellKnownTypeMember(syntax, WellKnownMember.System_Activator__CreateInstance_T, out method)) { return new BoundDefaultOperator(syntax, null, type: typeParameter, hasErrors: true); } Debug.Assert((object)method != null); method = method.Construct(ImmutableArray.Create<TypeSymbol>(typeParameter)); var createInstanceCall = new BoundCall( syntax, null, method, ImmutableArray<BoundExpression>.Empty, default(ImmutableArray<string>), default(ImmutableArray<RefKind>), isDelegateCall: false, expanded: false, invokedAsExtensionMethod: false, argsToParamsOpt: default(ImmutableArray<int>), resultKind: LookupResultKind.Viable, type: typeParameter); if (typeParameter.IsReferenceType) { // "new T()" is rewritten as: "Activator.CreateInstance<T>()". return createInstanceCall; } else { // "new T()" is rewritten as: "(null == (object)default(T)) ? Activator.CreateInstance<T>() : default(T)". var defaultT = new BoundDefaultOperator(syntax, type: typeParameter); return new BoundConditionalOperator( syntax, MakeNullCheck( syntax: syntax, rewrittenExpr: MakeConversion( syntax: syntax, rewrittenOperand: defaultT, conversionKind: ConversionKind.Boxing, rewrittenType: this.compilation.GetSpecialType(SpecialType.System_Object), @checked: false), operatorKind: BinaryOperatorKind.Equal), createInstanceCall, defaultT, constantValueOpt: null, type: typeParameter); } }
private BoundExpression MakeLiftedDecimalIncDecOperator(CSharpSyntaxNode syntax, BinaryOperatorKind oper, BoundExpression operand) { Debug.Assert(operand.Type.IsNullableType() && operand.Type.GetNullableUnderlyingType().SpecialType == SpecialType.System_Decimal); // This method assumes that operand is already a temporary and so there is no need to copy it again. MethodSymbol method = GetDecimalIncDecOperator(oper); MethodSymbol getValueOrDefault = GetNullableMethod(syntax, operand.Type, SpecialMember.System_Nullable_T_GetValueOrDefault); MethodSymbol ctor = GetNullableMethod(syntax, operand.Type, SpecialMember.System_Nullable_T__ctor); // x.HasValue BoundExpression condition = MakeNullableHasValue(syntax, operand); // x.GetValueOrDefault() BoundExpression getValueCall = BoundCall.Synthesized(syntax, operand, getValueOrDefault); // op_Inc(x.GetValueOrDefault()) BoundExpression methodCall = BoundCall.Synthesized(syntax, null, method, getValueCall); // new decimal?(op_Inc(x.GetValueOrDefault())) BoundExpression consequence = new BoundObjectCreationExpression(syntax, ctor, methodCall); // default(decimal?) 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); }
public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node) { Debug.Assert(node != null); // Rewrite the arguments. // NOTE: We may need additional argument rewriting such as generating a params array, // re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. // NOTE: This is done later by MakeArguments, for now we just lower each argument. var rewrittenArguments = VisitList(node.Arguments); // We have already lowered each argument, but we may need some additional rewriting for the arguments, // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. ImmutableArray<LocalSymbol> temps; ImmutableArray<RefKind> argumentRefKindsOpt = node.ArgumentRefKindsOpt; rewrittenArguments = MakeArguments(node.Syntax, rewrittenArguments, node.Constructor, node.Constructor, node.Expanded, node.ArgsToParamsOpt, ref argumentRefKindsOpt, out temps); BoundExpression rewrittenObjectCreation; if (_inExpressionLambda) { if (!temps.IsDefaultOrEmpty) { throw ExceptionUtilities.UnexpectedValue(temps.Length); } rewrittenObjectCreation = node.UpdateArgumentsAndInitializer(rewrittenArguments, MakeObjectCreationInitializerForExpressionTree(node.InitializerExpressionOpt), changeTypeOpt: node.Constructor.ContainingType); if (node.Type.IsInterfaceType()) { Debug.Assert(rewrittenObjectCreation.Type == ((NamedTypeSymbol)node.Type).ComImportCoClass); rewrittenObjectCreation = MakeConversion(rewrittenObjectCreation, node.Type, false, false); } return rewrittenObjectCreation; } rewrittenObjectCreation = node.UpdateArgumentsAndInitializer(rewrittenArguments, newInitializerExpression: null, changeTypeOpt: node.Constructor.ContainingType); // replace "new S()" with a default struct ctor with "default(S)" if (node.Constructor.IsDefaultValueTypeConstructor()) { rewrittenObjectCreation = new BoundDefaultOperator(rewrittenObjectCreation.Syntax, rewrittenObjectCreation.Type); } if (!temps.IsDefaultOrEmpty) { rewrittenObjectCreation = new BoundSequence( node.Syntax, temps, ImmutableArray<BoundExpression>.Empty, rewrittenObjectCreation, node.Type); } if (node.Type.IsInterfaceType()) { Debug.Assert(rewrittenObjectCreation.Type == ((NamedTypeSymbol)node.Type).ComImportCoClass); rewrittenObjectCreation = MakeConversion(rewrittenObjectCreation, node.Type, false, false); } if (node.InitializerExpressionOpt == null || node.InitializerExpressionOpt.HasErrors) { return rewrittenObjectCreation; } return MakeObjectCreationWithInitializer(node.Syntax, rewrittenObjectCreation, node.InitializerExpressionOpt, node.Type); }
private BoundExpression OptimizeLiftedBooleanOperatorOneNull( CSharpSyntaxNode syntax, BinaryOperatorKind kind, BoundExpression left, BoundExpression right) { // Here we optimize the cases where one side is known to be null. bool leftAlwaysNull = NullableNeverHasValue(left); bool rightAlwaysNull = NullableNeverHasValue(right); Debug.Assert(!(leftAlwaysNull && rightAlwaysNull)); // We've already optimized this case. if (!(leftAlwaysNull || rightAlwaysNull)) { return null; } // First, if one operand is null and the other is definitely non null, then we can eliminate // all the temporaries: // // new bool?() & new bool?(B()) // new bool?() | new bool?(B()) // // can be generated as // // B() ? new bool?() : new bool?(false) // B() ? new bool?(true) : new bool?() // // respectively. BoundExpression alwaysNull = leftAlwaysNull ? left : right; BoundExpression notAlwaysNull = leftAlwaysNull ? right : left; BoundExpression neverNull = NullableAlwaysHasValue(notAlwaysNull); BoundExpression nullBool = new BoundDefaultOperator(syntax, null, alwaysNull.Type); if (neverNull != null) { BoundExpression newNullBool = MakeNewNullableBoolean(syntax, kind == BinaryOperatorKind.LiftedBoolOr); return RewriteConditionalOperator( syntax: syntax, rewrittenCondition: neverNull, rewrittenConsequence: kind == BinaryOperatorKind.LiftedBoolAnd ? nullBool : newNullBool, rewrittenAlternative: kind == BinaryOperatorKind.LiftedBoolAnd ? newNullBool : nullBool, constantValueOpt: null, rewrittenType: alwaysNull.Type); } // Now we optimize the case where one operand is null and the other is not. We generate // // new bool?() & M() // new bool?() | M() // // as // // bool? t = M(), t.GetValueOrDefault() ? new bool?() : t // bool? t = M(), t.GetValueOrDefault() ? t : new bool?() // // respectively. BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = _factory.StoreToTemp(notAlwaysNull, out tempAssignment); BoundExpression condition = MakeOptimizedGetValueOrDefault(syntax, boundTemp); BoundExpression consequence = kind == BinaryOperatorKind.LiftedBoolAnd ? nullBool : boundTemp; BoundExpression alternative = kind == BinaryOperatorKind.LiftedBoolAnd ? boundTemp : nullBool; BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: alwaysNull.Type); return new BoundSequence( syntax: syntax, locals: ImmutableArray.Create<LocalSymbol>(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create<BoundExpression>(tempAssignment), value: conditionalExpression, type: conditionalExpression.Type); }
private BoundExpression GetDefaultParameterValue(CSharpSyntaxNode syntax, ParameterSymbol parameter) { TypeSymbol parameterType = parameter.Type; ConstantValue defaultConstantValue = parameter.ExplicitDefaultConstantValue; BoundExpression defaultValue; SourceLocation callerSourceLocation; if (parameter.IsCallerLineNumber && ((callerSourceLocation = GetCallerLocation(syntax)) != null)) { int line = callerSourceLocation.SourceTree.GetDisplayLineNumber(callerSourceLocation.SourceSpan); BoundExpression lineLiteral = MakeLiteral(syntax, ConstantValue.Create(line), compilation.GetSpecialType(SpecialType.System_Int32)); if (parameterType.IsNullableType()) { defaultValue = MakeConversion(lineLiteral, parameterType.GetNullableUnderlyingType(), false); // wrap it in a nullable ctor. defaultValue = new BoundObjectCreationExpression( syntax, GetNullableMethod(syntax, parameterType, SpecialMember.System_Nullable_T__ctor), defaultValue); } else { defaultValue = MakeConversion(lineLiteral, parameterType, false); } } else if (parameter.IsCallerFilePath && ((callerSourceLocation = GetCallerLocation(syntax)) != null)) { string path = callerSourceLocation.SourceTree.GetDisplayPath(callerSourceLocation.SourceSpan, compilation.Options.SourceReferenceResolver); BoundExpression memberNameLiteral = MakeLiteral(syntax, ConstantValue.Create(path), compilation.GetSpecialType(SpecialType.System_String)); defaultValue = MakeConversion(memberNameLiteral, parameterType, false); } else if (parameter.IsCallerMemberName && ((callerSourceLocation = GetCallerLocation(syntax)) != null)) { string memberName = this.factory.TopLevelMethod.GetMemberCallerName(); BoundExpression memberNameLiteral = MakeLiteral(syntax, ConstantValue.Create(memberName), compilation.GetSpecialType(SpecialType.System_String)); defaultValue = MakeConversion(memberNameLiteral, parameterType, false); } else if (defaultConstantValue == ConstantValue.NotAvailable) { // There is no constant value given for the parameter in source/metadata. if (parameterType.IsDynamic() || parameterType.SpecialType == SpecialType.System_Object) { // We have something like M([Optional] object x). We have special handling for such situations. defaultValue = GetDefaultParameterSpecial(syntax, parameter); } else { // The argument to M([Optional] int x) becomes default(int) defaultValue = new BoundDefaultOperator(syntax, parameterType); } } else if (defaultConstantValue.IsNull && parameterType.IsValueType) { // We have something like M(int? x = null) or M(S x = default(S)), // so replace the argument with default(int?). defaultValue = new BoundDefaultOperator(syntax, parameterType); } else if (parameterType.IsNullableType()) { // We have something like M(double? x = 1.23), so replace the argument // with new double?(1.23). TypeSymbol constantType = compilation.GetSpecialType(defaultConstantValue.SpecialType); defaultValue = MakeLiteral(syntax, defaultConstantValue, constantType); // The parameter's underlying type might not match the constant type. For example, we might have // a default value of 5 (an integer) but a parameter type of decimal?. defaultValue = MakeConversion(defaultValue, parameterType.GetNullableUnderlyingType(), @checked: false, acceptFailingConversion: true); // Finally, wrap it in a nullable ctor. defaultValue = new BoundObjectCreationExpression( syntax, GetNullableMethod(syntax, parameterType, SpecialMember.System_Nullable_T__ctor), defaultValue); } else if (defaultConstantValue.IsNull || defaultConstantValue.IsBad) { defaultValue = MakeLiteral(syntax, defaultConstantValue, parameterType); } else { // We have something like M(double = 1.23), so replace the argument with 1.23. TypeSymbol constantType = compilation.GetSpecialType(defaultConstantValue.SpecialType); defaultValue = MakeLiteral(syntax, defaultConstantValue, constantType); // The parameter type might not match the constant type. defaultValue = MakeConversion(defaultValue, parameterType, @checked: false, acceptFailingConversion: true); } return defaultValue; }