/// <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;
        }
示例#2
0
        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);
        }
示例#5
0
        /// <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;
        }
示例#6
0
        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));
        }
示例#11
0
        /// <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);
        }
示例#12
0
        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));
        }
示例#13
0
        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));
            }
        }
示例#15
0
        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);
        }
示例#21
0
        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;
        }