Esempio n. 1
0
        /// <summary>
        /// Most of the above optimizations are not applicable in expression trees as the operator
        /// must stay a binary operator. We cannot do much beyond constant folding which is done in binder.
        /// </summary>
        private BoundExpression RewriteStringConcatInExpressionLambda(SyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type)
        {
            SpecialMember member = (operatorKind == BinaryOperatorKind.StringConcatenation) ?
                                   SpecialMember.System_String__ConcatStringString :
                                   SpecialMember.System_String__ConcatObjectObject;

            var method = UnsafeGetSpecialTypeMethod(syntax, member);

            Debug.Assert((object)method != null);

            return(new BoundBinaryOperator(syntax, operatorKind, default(ConstantValue), method, default(LookupResultKind), loweredLeft, loweredRight, type));
        }
Esempio n. 2
0
 protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, string methodName, ImmutableArray <BoundExpression> args, DiagnosticBag diagnostics)
 {
     return(MakeQueryInvocation(node, receiver, methodName, default(SeparatedSyntaxList <TypeSyntax>), default(ImmutableArray <TypeSymbolWithAnnotations>), args, diagnostics));
 }
Esempio n. 3
0
        protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, string methodName, SeparatedSyntaxList <TypeSyntax> typeArgsSyntax, ImmutableArray <TypeSymbolWithAnnotations> typeArgs, ImmutableArray <BoundExpression> args, DiagnosticBag diagnostics)
        {
            // clean up the receiver
            var ultimateReceiver = receiver;

            while (ultimateReceiver.Kind == BoundKind.QueryClause)
            {
                ultimateReceiver = ((BoundQueryClause)ultimateReceiver).Value;
            }
            if ((object)ultimateReceiver.Type == null)
            {
                if (ultimateReceiver.HasAnyErrors || node.HasErrors)
                {
                    // report no additional errors
                }
                else if (ultimateReceiver.IsLiteralNull())
                {
                    diagnostics.Add(ErrorCode.ERR_NullNotValid, node.Location);
                }
                else if (ultimateReceiver.IsLiteralDefault())
                {
                    diagnostics.Add(ErrorCode.ERR_DefaultLiteralNotValid, node.Location);
                }
                else if (ultimateReceiver.Kind == BoundKind.NamespaceExpression)
                {
                    diagnostics.Add(ErrorCode.ERR_BadSKunknown, ultimateReceiver.Syntax.Location, ultimateReceiver.Syntax, MessageID.IDS_SK_NAMESPACE.Localize());
                }
                else if (ultimateReceiver.Kind == BoundKind.Lambda || ultimateReceiver.Kind == BoundKind.UnboundLambda)
                {
                    // Could not find an implementation of the query pattern for source type '{0}'.  '{1}' not found.
                    diagnostics.Add(ErrorCode.ERR_QueryNoProvider, node.Location, MessageID.IDS_AnonMethod.Localize(), methodName);
                }
                else if (ultimateReceiver.Kind == BoundKind.MethodGroup)
                {
                    var methodGroup = (BoundMethodGroup)ultimateReceiver;
                    HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                    var resolution = this.ResolveMethodGroup(methodGroup, analyzedArguments: null, isMethodGroupConversion: false, useSiteDiagnostics: ref useSiteDiagnostics);
                    diagnostics.Add(node, useSiteDiagnostics);
                    diagnostics.AddRange(resolution.Diagnostics);
                    if (resolution.HasAnyErrors)
                    {
                        receiver = this.BindMemberAccessBadResult(methodGroup);
                    }
                    else
                    {
                        Debug.Assert(!resolution.IsEmpty);
                        diagnostics.Add(ErrorCode.ERR_QueryNoProvider, node.Location, MessageID.IDS_SK_METHOD.Localize(), methodName);
                    }
                    resolution.Free();
                }

                receiver = new BoundBadExpression(receiver.Syntax, LookupResultKind.NotAValue, ImmutableArray <Symbol> .Empty, ImmutableArray.Create(receiver), CreateErrorType());
            }
            else if (receiver.Type.SpecialType == SpecialType.System_Void)
            {
                if (!receiver.HasAnyErrors && !node.HasErrors)
                {
                    diagnostics.Add(ErrorCode.ERR_QueryNoProvider, node.Location, "void", methodName);
                }

                receiver = new BoundBadExpression(receiver.Syntax, LookupResultKind.NotAValue, ImmutableArray <Symbol> .Empty, ImmutableArray.Create(receiver), CreateErrorType());
            }

            return((BoundCall)MakeInvocationExpression(
                       node,
                       receiver,
                       methodName,
                       args,
                       diagnostics,
                       typeArgsSyntax,
                       typeArgs,
                       queryClause: node,
                       // Queries are syntactical rewrites, so we allow fields and properties of delegate types to be invoked,
                       // although no well-known non-generic query method is used atm.
                       allowFieldsAndProperties: true));
        }
Esempio n. 4
0
        private void ReduceFrom(FromClauseSyntax from, QueryTranslationState state, DiagnosticBag diagnostics)
        {
            var x1 = state.rangeVariable;

            BoundExpression collectionSelectorLambda;

            if (from.Type == null)
            {
                collectionSelectorLambda = MakeQueryUnboundLambda(state.RangeVariableMap(), x1, from.Expression);
            }
            else
            {
                collectionSelectorLambda = MakeQueryUnboundLambdaWithCast(state.RangeVariableMap(), x1, from.Expression, from.Type, BindTypeArgument(from.Type, diagnostics));
            }

            var x2 = state.AddRangeVariable(this, from.Identifier, diagnostics);

            if (state.clauses.IsEmpty() && state.selectOrGroup.IsKind(SyntaxKind.SelectClause))
            {
                var select = (SelectClauseSyntax)state.selectOrGroup;

                // A query expression with a second from clause followed by a select clause
                //     from x1 in e1
                //     from x2 in e2
                //     select v
                // is translated into
                //     ( e1 ) . SelectMany( x1 => e2 , ( x1 , x2 ) => v )
                var resultSelectorLambda = MakeQueryUnboundLambda(state.RangeVariableMap(), ImmutableArray.Create(x1, x2), select.Expression);

                var invocation = MakeQueryInvocation(
                    from,
                    state.fromExpression,
                    "SelectMany",
                    ImmutableArray.Create(collectionSelectorLambda, resultSelectorLambda),
                    diagnostics);

                // Adjust the second-to-last parameter to be a query clause (if it was an extension method, an extra parameter was added)
                BoundExpression castInvocation = (from.Type != null) ? ExtractCastInvocation(invocation) : null;

                var arguments = invocation.Arguments;
                invocation = invocation.Update(
                    invocation.ReceiverOpt,
                    invocation.Method,
                    arguments.SetItem(arguments.Length - 2, MakeQueryClause(from, arguments[arguments.Length - 2], x2, invocation, castInvocation)));

                state.Clear();
                state.fromExpression = MakeQueryClause(from, invocation, definedSymbol: x2, queryInvocation: invocation);
                state.fromExpression = MakeQueryClause(select, state.fromExpression);
            }
            else
            {
                // A query expression with a second from clause followed by something other than a select clause:
                //     from x1 in e1
                //     from x2 in e2
                //     ...
                // is translated into
                //     from * in ( e1 ) . SelectMany( x1 => e2 , ( x1 , x2 ) => new { x1 , x2 } )
                //     ...

                // We use a slightly different translation strategy.  We produce
                //     from * in ( e ) . SelectMany ( x1 => e2, ( x1 , x2 ) => new Pair<X1,X2>(x1, x2) )
                // Where X1 is the type of x1, and X2 is the type of x2.
                // Subsequently, x1 (or members of x1, if it is a transparent identifier)
                // are accessed as TRID.Item1 (or members of that), and x2 is accessed
                // as TRID.Item2, where TRID is the compiler-generated identifier used
                // to represent the transparent identifier in the result.
                var resultSelectorLambda = MakePairLambda(from, state, x1, x2);

                var invocation = MakeQueryInvocation(
                    from,
                    state.fromExpression,
                    "SelectMany",
                    ImmutableArray.Create(collectionSelectorLambda, resultSelectorLambda),
                    diagnostics);

                BoundExpression castInvocation = (from.Type != null) ? ExtractCastInvocation(invocation) : null;
                state.fromExpression = MakeQueryClause(from, invocation, x2, invocation, castInvocation);
            }
        }
Esempio n. 5
0
        private BoundExpression MakePair(CSharpSyntaxNode node, string field1Name, BoundExpression field1Value, string field2Name, BoundExpression field2Value, QueryTranslationState state, DiagnosticBag diagnostics)
        {
            if (field1Name == field2Name)
            {
                // we will generate a diagnostic elsewhere
                field2Name  = state.TransparentRangeVariableName();
                field2Value = new BoundBadExpression(field2Value.Syntax, LookupResultKind.Empty, ImmutableArray <Symbol> .Empty, ImmutableArray.Create(field2Value), field2Value.Type, true);
            }

            AnonymousTypeDescriptor typeDescriptor = new AnonymousTypeDescriptor(
                ImmutableArray.Create <AnonymousTypeField>(
                    new AnonymousTypeField(field1Name, field1Value.Syntax.Location,
                                           TypeSymbolWithAnnotations.Create(TypeOrError(field1Value))),
                    new AnonymousTypeField(field2Name, field2Value.Syntax.Location,
                                           TypeSymbolWithAnnotations.Create(TypeOrError(field2Value)))
                    ),
                node.Location
                );

            AnonymousTypeManager manager       = this.Compilation.AnonymousTypeManager;
            NamedTypeSymbol      anonymousType = manager.ConstructAnonymousTypeSymbol(typeDescriptor);

            return(MakeConstruction(node, anonymousType, ImmutableArray.Create(field1Value, field2Value), diagnostics));
        }
Esempio n. 6
0
 public override BoundExpression InstrumentSwitchStatementExpression(BoundStatement original, BoundExpression rewrittenExpression, SyntheticBoundNodeFactory factory)
 {
     return(Previous.InstrumentSwitchStatementExpression(original, rewrittenExpression, factory));
 }
Esempio n. 7
0
        private BoundExpression FinalTranslation(QueryTranslationState state, DiagnosticBag diagnostics)
        {
            Debug.Assert(state.clauses.IsEmpty());
            switch (state.selectOrGroup.Kind())
            {
            case SyntaxKind.SelectClause:
            {
                // A query expression of the form
                //     from x in e select v
                // is translated into
                //     ( e ) . Select ( x => v )
                var selectClause = (SelectClauseSyntax)state.selectOrGroup;
                var x            = state.rangeVariable;
                var e            = state.fromExpression;
                var v            = selectClause.Expression;
                var lambda       = MakeQueryUnboundLambda(state.RangeVariableMap(), x, v);
                var result       = MakeQueryInvocation(state.selectOrGroup, e, "Select", lambda, diagnostics);
                return(MakeQueryClause(selectClause, result, queryInvocation: result));
            }

            case SyntaxKind.GroupClause:
            {
                // A query expression of the form
                //     from x in e group v by k
                // is translated into
                //     ( e ) . GroupBy ( x => k , x => v )
                // except when v is the identifier x, the translation is
                //     ( e ) . GroupBy ( x => k )
                var       groupClause = (GroupClauseSyntax)state.selectOrGroup;
                var       x           = state.rangeVariable;
                var       e           = state.fromExpression;
                var       v           = groupClause.GroupExpression;
                var       k           = groupClause.ByExpression;
                var       vId         = v as IdentifierNameSyntax;
                BoundCall result;
                var       lambdaLeft = MakeQueryUnboundLambda(state.RangeVariableMap(), x, k);

                // this is the unoptimized form (when v is not the identifier x)
                var             d           = DiagnosticBag.GetInstance();
                BoundExpression lambdaRight = MakeQueryUnboundLambda(state.RangeVariableMap(), x, v);
                result = MakeQueryInvocation(state.selectOrGroup, e, "GroupBy", ImmutableArray.Create(lambdaLeft, lambdaRight), d);
                // k and v appear reversed in the invocation, so we reorder their evaluation
                result = ReverseLastTwoParameterOrder(result);

                BoundExpression unoptimizedForm = null;
                if (vId != null && vId.Identifier.ValueText == x.Name)
                {
                    // The optimized form.  We store the unoptimized form for analysis
                    unoptimizedForm = result;
                    result          = MakeQueryInvocation(state.selectOrGroup, e, "GroupBy", lambdaLeft, diagnostics);
                    if (unoptimizedForm.HasAnyErrors && !result.HasAnyErrors)
                    {
                        unoptimizedForm = null;
                    }
                }
                else
                {
                    diagnostics.AddRange(d);
                }

                d.Free();
                return(MakeQueryClause(groupClause, result, queryInvocation: result, unoptimizedForm: unoptimizedForm));
            }

            default:
            {
                // there should have been a syntax error if we get here.
                return(new BoundBadExpression(
                           state.selectOrGroup, LookupResultKind.OverloadResolutionFailure, ImmutableArray <Symbol> .Empty,
                           ImmutableArray.Create(state.fromExpression), state.fromExpression.Type));
            }
            }
        }
Esempio n. 8
0
        private BoundDeconstructionAssignmentOperator BindDeconstructionAssignment(
            CSharpSyntaxNode node,
            ExpressionSyntax left,
            BoundExpression boundRHS,
            ArrayBuilder <DeconstructionVariable> checkedVariables,
            bool resultIsUsed,
            DiagnosticBag diagnostics)
        {
            uint rightEscape = GetValEscape(boundRHS, this.LocalScopeDepth);

            if ((object)boundRHS.Type == null || boundRHS.Type.IsErrorType())
            {
                // we could still not infer a type for the RHS
                FailRemainingInferencesAndSetValEscape(checkedVariables, diagnostics, rightEscape);
                var voidType = GetSpecialType(SpecialType.System_Void, diagnostics, node);

                var type = boundRHS.Type ?? voidType;
                return(new BoundDeconstructionAssignmentOperator(
                           node,
                           DeconstructionVariablesAsTuple(left, checkedVariables, diagnostics, ignoreDiagnosticsFromTuple: true),
                           new BoundConversion(boundRHS.Syntax, boundRHS, Conversion.Deconstruction, @checked: false, explicitCastInCode: false, conversionGroupOpt: null,
                                               constantValueOpt: null, type: type, hasErrors: true),
                           resultIsUsed,
                           voidType,
                           hasErrors: true));
            }

            Conversion conversion;
            bool       hasErrors = !MakeDeconstructionConversion(
                boundRHS.Type,
                node,
                boundRHS.Syntax,
                diagnostics,
                checkedVariables,
                out conversion);

            FailRemainingInferencesAndSetValEscape(checkedVariables, diagnostics, rightEscape);

            var        lhsTuple   = DeconstructionVariablesAsTuple(left, checkedVariables, diagnostics, ignoreDiagnosticsFromTuple: diagnostics.HasAnyErrors() || !resultIsUsed);
            TypeSymbol returnType = hasErrors ? CreateErrorType() : lhsTuple.Type;

            uint leftEscape = GetBroadestValEscape(lhsTuple, this.LocalScopeDepth);

            boundRHS = ValidateEscape(boundRHS, leftEscape, isByRef: false, diagnostics: diagnostics);

            var boundConversion = new BoundConversion(
                boundRHS.Syntax,
                boundRHS,
                conversion,
                @checked: false,
                explicitCastInCode: false,
                conversionGroupOpt: null,
                constantValueOpt: null,
                type: returnType,
                hasErrors: hasErrors)
            {
                WasCompilerGenerated = true
            };

            return(new BoundDeconstructionAssignmentOperator(node, lhsTuple, boundConversion, resultIsUsed, returnType));
        }
Esempio n. 9
0
        /// <summary>When boundRHS is a tuple literal, fix it up by inferring its types.</summary>
        private BoundExpression FixTupleLiteral(ArrayBuilder <DeconstructionVariable> checkedVariables, BoundExpression boundRHS, CSharpSyntaxNode syntax, DiagnosticBag diagnostics)
        {
            if (boundRHS.Kind == BoundKind.TupleLiteral)
            {
                // Let's fix the literal up by figuring out its type
                // For declarations, that means merging type information from the LHS and RHS
                // For assignments, only the LHS side matters since it is necessarily typed

                // If we already have diagnostics at this point, it is not worth collecting likely duplicate diagnostics from making the merged type
                bool       hadErrors       = diagnostics.HasAnyErrors();
                TypeSymbol mergedTupleType = MakeMergedTupleType(checkedVariables, (BoundTupleLiteral)boundRHS, syntax, Compilation, hadErrors ? null : diagnostics);
                if ((object)mergedTupleType != null)
                {
                    boundRHS = GenerateConversionForAssignment(mergedTupleType, boundRHS, diagnostics);
                }
            }
            else if ((object)boundRHS.Type == null)
            {
                Error(diagnostics, ErrorCode.ERR_DeconstructRequiresExpression, boundRHS.Syntax);
            }

            return(boundRHS);
        }
Esempio n. 10
0
        private BoundStatement RewriteForStatementWithoutInnerLocals(
            BoundLoopStatement original,
            ImmutableArray <LocalSymbol> outerLocals,
            BoundStatement rewrittenInitializer,
            BoundExpression rewrittenCondition,
            BoundStatement rewrittenIncrement,
            BoundStatement rewrittenBody,
            BoundStatement afterBreak,
            GeneratedLabelSymbol breakLabel,
            GeneratedLabelSymbol continueLabel,
            bool hasErrors)
        {
            Debug.Assert(original.Kind == BoundKind.ForStatement || original.Kind == BoundKind.ForEachStatement);
            Debug.Assert(rewrittenBody != null);

            // The sequence point behavior exhibited here is different from that of the native compiler.  In the native
            // compiler, if you have something like
            //
            // for([|int i = 0, j = 0|]; ; [|i++, j++|])
            //
            // then all the initializers are treated as a single sequence point, as are
            // all the loop incrementors.
            //
            // We now make each one individually a sequence point:
            //
            // for([|int i = 0|], [|j = 0|]; ; [|i++|], [|j++|])
            //
            // If we decide that we want to preserve the native compiler stepping behavior
            // then we'll need to be a bit fancy here. The initializer and increment statements
            // can contain lambdas whose bodies need to have sequence points inserted, so we
            // need to make sure we visit the children. But we'll also need to make sure that
            // we do not generate one sequence point for each statement in the initializers
            // and the incrementors.

            SyntaxNode syntax           = original.Syntax;
            var        statementBuilder = ArrayBuilder <BoundStatement> .GetInstance();

            if (rewrittenInitializer != null)
            {
                statementBuilder.Add(rewrittenInitializer);
            }

            var startLabel = new GeneratedLabelSymbol("start");

            // for (initializer; condition; increment)
            //   body;
            //
            // becomes the following (with block added for locals)
            //
            // {
            //   initializer;
            //   goto end;
            // start:
            //   body;
            // continue:
            //   increment;
            // end:
            //   GotoIfTrue condition start;
            // break:
            // }

            var endLabel = new GeneratedLabelSymbol("end");

            //  initializer;
            //  goto end;

            BoundStatement gotoEnd = new BoundGotoStatement(syntax, endLabel);

            if (this.Instrument)
            {
                // Mark the initial jump as hidden.
                // We do it to tell that this is not a part of previous statement.
                // This jump may be a target of another jump (for example if loops are nested) and that will make
                // impression of the previous statement being re-executed
                gotoEnd = new BoundSequencePoint(null, gotoEnd);
            }

            statementBuilder.Add(gotoEnd);

            // start:
            //   body;
            statementBuilder.Add(new BoundLabelStatement(syntax, startLabel));

            statementBuilder.Add(rewrittenBody);

            // continue:
            //   increment;
            statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel));
            if (rewrittenIncrement != null)
            {
                statementBuilder.Add(rewrittenIncrement);
            }

            // end:
            //   GotoIfTrue condition start;
            statementBuilder.Add(new BoundLabelStatement(syntax, endLabel));
            BoundStatement branchBack = null;

            if (rewrittenCondition != null)
            {
                branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel);
            }
            else
            {
                branchBack = new BoundGotoStatement(syntax, startLabel);
            }

            if (this.Instrument)
            {
                switch (original.Kind)
                {
                case BoundKind.ForEachStatement:
                    branchBack = _instrumenter.InstrumentForEachStatementConditionalGotoStart((BoundForEachStatement)original, branchBack);
                    break;

                default:
                    throw ExceptionUtilities.UnexpectedValue(original.Kind);
                }
            }

            statementBuilder.Add(branchBack);

            // break:
            statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel));

            if (afterBreak != null)
            {
                statementBuilder.Add(afterBreak);
            }

            var statements = statementBuilder.ToImmutableAndFree();

            return(new BoundBlock(syntax, outerLocals, statements, hasErrors));
        }
Esempio n. 11
0
        private BoundStatement RewriteForStatement(
            BoundForStatement node,
            BoundStatement rewrittenInitializer,
            BoundExpression rewrittenCondition,
            BoundStatement rewrittenIncrement,
            BoundStatement rewrittenBody)
        {
            if (node.InnerLocals.IsEmpty)
            {
                return(RewriteForStatementWithoutInnerLocals(
                           node,
                           node.OuterLocals,
                           rewrittenInitializer,
                           rewrittenCondition,
                           rewrittenIncrement,
                           rewrittenBody,
                           null,
                           node.BreakLabel,
                           node.ContinueLabel, node.HasErrors));
            }

            // We need to enter inner_scope-block from the top, that is where an instance of a display class will be created
            // if any local is captured within a lambda.

            // for (initializer; condition; increment)
            //   body;
            //
            // becomes the following (with block added for locals)
            //
            // {
            //   initializer;
            // start:
            //   {
            //     GotoIfFalse condition break;
            //     body;
            // continue:
            //     increment;
            //     goto start;
            //   }
            // break:
            // }

            Debug.Assert(rewrittenBody != null);

            SyntaxNode syntax           = node.Syntax;
            var        statementBuilder = ArrayBuilder <BoundStatement> .GetInstance();

            //  initializer;
            if (rewrittenInitializer != null)
            {
                statementBuilder.Add(rewrittenInitializer);
            }

            var startLabel = new GeneratedLabelSymbol("start");

            // start:
            BoundStatement startLabelStatement = new BoundLabelStatement(syntax, startLabel);

            if (Instrument)
            {
                startLabelStatement = new BoundSequencePoint(null, startLabelStatement);
            }

            statementBuilder.Add(startLabelStatement);

            var blockBuilder = ArrayBuilder <BoundStatement> .GetInstance();

            // GotoIfFalse condition break;
            if (rewrittenCondition != null)
            {
                BoundStatement ifNotConditionGotoBreak = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, node.BreakLabel);
                blockBuilder.Add(ifNotConditionGotoBreak);
            }

            // body;
            blockBuilder.Add(rewrittenBody);

            // continue:
            //   increment;
            blockBuilder.Add(new BoundLabelStatement(syntax, node.ContinueLabel));
            if (rewrittenIncrement != null)
            {
                blockBuilder.Add(rewrittenIncrement);
            }

            // goto start;
            blockBuilder.Add(new BoundGotoStatement(syntax, startLabel));

            statementBuilder.Add(new BoundBlock(syntax, node.InnerLocals, blockBuilder.ToImmutableAndFree()));

            // break:
            statementBuilder.Add(new BoundLabelStatement(syntax, node.BreakLabel));

            var statements = statementBuilder.ToImmutableAndFree();

            return(new BoundBlock(syntax, node.OuterLocals, statements, node.HasErrors));
        }
Esempio n. 12
0
 public ExpressionAndDiagnostics(BoundExpression expression, ImmutableArray <Diagnostic> diagnostics)
 {
     this.Expression  = expression;
     this.Diagnostics = diagnostics;
 }
Esempio n. 13
0
        /// <summary>
        /// Checks whether the expression represents a boxing conversion of a special value type.
        /// If it does, it tries to return a string-based representation instead in order
        /// to avoid allocations.  If it can't, the original expression is returned.
        /// </summary>
        private BoundExpression ConvertConcatExprToStringIfPossible(SyntaxNode syntax, BoundExpression expr)
        {
            if (expr.Kind == BoundKind.Conversion)
            {
                BoundConversion conv = (BoundConversion)expr;
                if (conv.ConversionKind == ConversionKind.Boxing)
                {
                    BoundExpression operand = conv.Operand;
                    if (operand != null)
                    {
                        // Is the expression a literal char?  If so, we can
                        // simply make it a literal string instead and avoid any
                        // allocations for converting the char to a string at run time.
                        if (operand.Kind == BoundKind.Literal)
                        {
                            ConstantValue cv = ((BoundLiteral)operand).ConstantValue;
                            if (cv != null && cv.SpecialType == SpecialType.System_Char)
                            {
                                return(_factory.StringLiteral(cv.CharValue.ToString()));
                            }
                        }

                        // Can the expression be optimized with a ToString call?
                        // If so, we can synthesize a ToString call to avoid boxing.
                        if (ConcatExprCanBeOptimizedWithToString(operand.Type))
                        {
                            var toString = UnsafeGetSpecialTypeMethod(syntax, SpecialMember.System_Object__ToString);

                            var type            = (NamedTypeSymbol)operand.Type;
                            var toStringMembers = type.GetMembers(toString.Name);
                            foreach (var member in toStringMembers)
                            {
                                var toStringMethod = member as MethodSymbol;
                                if (toStringMethod.GetLeastOverriddenMethod(type) == (object)toString)
                                {
                                    return(BoundCall.Synthesized(syntax, operand, toStringMethod));
                                }
                            }
                        }
                    }
                }
            }

            // Optimization not possible; just return the original expression.
            return(expr);
        }
Esempio n. 14
0
        /// <summary>
        /// The strategy of this rewrite is to do rewrite "locally".
        /// We analyze arguments of the concat in a shallow fashion assuming that
        /// lowering and optimizations (including this one) is already done for the arguments.
        /// Based on the arguments we select the most appropriate pattern for the current node.
        ///
        /// NOTE: it is not guaranteed that the node that we chose will be the most optimal since we have only
        ///       local information - i.e. we look at the arguments, but we do not know about siblings.
        ///       When we move to the parent, the node may be rewritten by this or some another optimization.
        ///
        /// Example:
        ///     result = ( "abc" + "def" + null ?? expr1 + "moo" + "baz" ) + expr2
        ///
        /// Will rewrite into:
        ///     result = Concat("abcdef", expr2)
        ///
        /// However there will be transient nodes like  Concat(expr1 + "moo")  that will not be present in the
        /// resulting tree.
        ///
        /// </summary>
        private BoundExpression RewriteStringConcatenation(SyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type)
        {
            Debug.Assert(
                operatorKind == BinaryOperatorKind.StringConcatenation ||
                operatorKind == BinaryOperatorKind.StringAndObjectConcatenation ||
                operatorKind == BinaryOperatorKind.ObjectAndStringConcatenation);

            if (_inExpressionLambda)
            {
                return(RewriteStringConcatInExpressionLambda(syntax, operatorKind, loweredLeft, loweredRight, type));
            }

            // avoid run time boxing and ToString operations if we can reasonably convert to a string at compile time
            loweredLeft  = ConvertConcatExprToStringIfPossible(syntax, loweredLeft);
            loweredRight = ConvertConcatExprToStringIfPossible(syntax, loweredRight);

            // try fold two args without flattening.
            var folded = TryFoldTwoConcatOperands(syntax, loweredLeft, loweredRight);

            if (folded != null)
            {
                return(folded);
            }

            // flatten and merge -  ( expr1 + "A" ) + ("B" + expr2) ===> (expr1 + "AB" + expr2)
            ArrayBuilder <BoundExpression> leftFlattened = ArrayBuilder <BoundExpression> .GetInstance();

            ArrayBuilder <BoundExpression> rightFlattened = ArrayBuilder <BoundExpression> .GetInstance();

            FlattenConcatArg(loweredLeft, leftFlattened);
            FlattenConcatArg(loweredRight, rightFlattened);

            if (leftFlattened.Any() && rightFlattened.Any())
            {
                folded = TryFoldTwoConcatOperands(syntax, leftFlattened.Last(), rightFlattened.First());
                if (folded != null)
                {
                    rightFlattened[0] = folded;
                    leftFlattened.RemoveLast();
                }
            }

            leftFlattened.AddRange(rightFlattened);
            rightFlattened.Free();

            BoundExpression result;

            switch (leftFlattened.Count)
            {
            case 0:
                result = _factory.StringLiteral(string.Empty);
                break;

            case 1:
                result = leftFlattened[0];
                break;

            case 2:
                var left  = leftFlattened[0];
                var right = leftFlattened[1];
                result = RewriteStringConcatenationTwoExprs(syntax, left, right);
                break;

            case 3:
                var first  = leftFlattened[0];
                var second = leftFlattened[1];
                var third  = leftFlattened[2];
                result = RewriteStringConcatenationThreeExprs(syntax, first, second, third);
                break;

            default:
                result = RewriteStringConcatenationManyExprs(syntax, leftFlattened.ToImmutable());
                break;
            }

            leftFlattened.Free();
            return(result);
        }
Esempio n. 15
0
 public override BoundStatement InstrumentSwitchWhenClauseConditionalGotoBody(BoundExpression original, BoundStatement ifConditionGotoBody)
 {
     return(Previous.InstrumentSwitchWhenClauseConditionalGotoBody(original, ifConditionGotoBody));
 }
Esempio n. 16
0
        /// <summary>
        /// Recursively builds a Conversion object with Kind=Deconstruction including information about any necessary
        /// Deconstruct method and any element-wise conversion.
        ///
        /// Note that the variables may either be plain or nested variables.
        /// The variables may be updated with inferred types if they didn't have types initially.
        /// Returns false if there was an error.
        /// </summary>
        private bool MakeDeconstructionConversion(
            TypeSymbol type,
            SyntaxNode syntax,
            SyntaxNode rightSyntax,
            DiagnosticBag diagnostics,
            ArrayBuilder <DeconstructionVariable> variables,
            out Conversion conversion)
        {
            Debug.Assert((object)type != null);
            ImmutableArray <TypeSymbol> tupleOrDeconstructedTypes;

            conversion = Conversion.Deconstruction;

            // Figure out the deconstruct method (if one is required) and determine the types we get from the RHS at this level
            var deconstructMethod = default(DeconstructMethodInfo);

            if (type.IsTupleType)
            {
                // tuple literal such as `(1, 2)`, `(null, null)`, `(x.P, y.M())`
                tupleOrDeconstructedTypes = type.TupleElementTypes.SelectAsArray(TypeMap.AsTypeSymbol);
                SetInferredTypes(variables, tupleOrDeconstructedTypes, diagnostics);

                if (variables.Count != tupleOrDeconstructedTypes.Length)
                {
                    Error(diagnostics, ErrorCode.ERR_DeconstructWrongCardinality, syntax, tupleOrDeconstructedTypes.Length, variables.Count);
                    return(false);
                }
            }
            else
            {
                if (variables.Count < 2)
                {
                    Error(diagnostics, ErrorCode.ERR_DeconstructTooFewElements, syntax);
                    return(false);
                }

                var             inputPlaceholder      = new BoundDeconstructValuePlaceholder(syntax, this.LocalScopeDepth, type);
                BoundExpression deconstructInvocation = MakeDeconstructInvocationExpression(variables.Count,
                                                                                            inputPlaceholder, rightSyntax, diagnostics, outPlaceholders: out ImmutableArray <BoundDeconstructValuePlaceholder> outPlaceholders, out _);

                if (deconstructInvocation.HasAnyErrors)
                {
                    return(false);
                }

                deconstructMethod = new DeconstructMethodInfo(deconstructInvocation, inputPlaceholder, outPlaceholders);

                tupleOrDeconstructedTypes = outPlaceholders.SelectAsArray(p => p.Type);
                SetInferredTypes(variables, tupleOrDeconstructedTypes, diagnostics);
            }

            // Figure out whether those types will need conversions, including further deconstructions
            bool hasErrors = false;

            int count             = variables.Count;
            var nestedConversions = ArrayBuilder <Conversion> .GetInstance(count);

            for (int i = 0; i < count; i++)
            {
                var variable = variables[i];

                Conversion nestedConversion;
                if (variable.HasNestedVariables)
                {
                    var elementSyntax = syntax.Kind() == SyntaxKind.TupleExpression ? ((TupleExpressionSyntax)syntax).Arguments[i] : syntax;

                    hasErrors |= !MakeDeconstructionConversion(tupleOrDeconstructedTypes[i], syntax, rightSyntax, diagnostics,
                                                               variable.NestedVariables, out nestedConversion);
                }
                else
                {
                    var single = variable.Single;
                    HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                    nestedConversion = this.Conversions.ClassifyConversionFromType(tupleOrDeconstructedTypes[i], single.Type, ref useSiteDiagnostics);
                    diagnostics.Add(single.Syntax, useSiteDiagnostics);

                    if (!nestedConversion.IsImplicit)
                    {
                        hasErrors = true;
                        GenerateImplicitConversionError(diagnostics, Compilation, single.Syntax, nestedConversion, tupleOrDeconstructedTypes[i], single.Type);
                    }
                }
                nestedConversions.Add(nestedConversion);
            }

            conversion = new Conversion(ConversionKind.Deconstruction, deconstructMethod, nestedConversions.ToImmutableAndFree());

            return(!hasErrors);
        }
Esempio n. 17
0
 public override BoundExpression InstrumentCatchClauseFilter(BoundCatchBlock original, BoundExpression rewrittenFilter, SyntheticBoundNodeFactory factory)
 {
     return(Previous.InstrumentCatchClauseFilter(original, rewrittenFilter, factory));
 }
Esempio n. 18
0
 internal DeconstructionVariable(BoundExpression variable, SyntaxNode syntax)
 {
     Single          = variable;
     NestedVariables = null;
     Syntax          = (CSharpSyntaxNode)syntax;
 }
Esempio n. 19
0
 public override BoundExpression InstrumentDoStatementCondition(BoundDoStatement original, BoundExpression rewrittenCondition, SyntheticBoundNodeFactory factory)
 {
     return(Previous.InstrumentDoStatementCondition(original, rewrittenCondition, factory));
 }
Esempio n. 20
0
 internal DeconstructionVariable(ArrayBuilder <DeconstructionVariable> variables, SyntaxNode syntax)
 {
     Single          = null;
     NestedVariables = variables;
     Syntax          = (CSharpSyntaxNode)syntax;
 }
Esempio n. 21
0
        private void ReduceJoin(JoinClauseSyntax join, QueryTranslationState state, DiagnosticBag diagnostics)
        {
            var inExpression = BindValue(join.InExpression, diagnostics, BindValueKind.RValue);

            BoundExpression castInvocation = null;

            if (join.Type != null)
            {
                // A join clause that explicitly specifies a range variable type
                //     join T x in e on k1 equals k2
                // is translated into
                //     join x in ( e ) . Cast < T > ( ) on k1 equals k2
                var castType = BindTypeArgument(join.Type, diagnostics);
                castInvocation = MakeQueryInvocation(join, inExpression, "Cast", join.Type, castType, diagnostics);
                inExpression   = castInvocation;
            }

            var outerKeySelectorLambda = MakeQueryUnboundLambda(state.RangeVariableMap(), state.rangeVariable, join.LeftExpression);

            var x1 = state.rangeVariable;
            var x2 = state.AddRangeVariable(this, join.Identifier, diagnostics);
            var innerKeySelectorLambda = MakeQueryUnboundLambda(QueryTranslationState.RangeVariableMap(x2), x2, join.RightExpression);

            if (state.clauses.IsEmpty() && state.selectOrGroup.Kind() == SyntaxKind.SelectClause)
            {
                var       select = (SelectClauseSyntax)state.selectOrGroup;
                BoundCall invocation;
                if (join.Into == null)
                {
                    // A query expression with a join clause without an into followed by a select clause
                    //     from x1 in e1
                    //     join x2 in e2 on k1 equals k2
                    //     select v
                    // is translated into
                    //     ( e1 ) . Join( e2 , x1 => k1 , x2 => k2 , ( x1 , x2 ) => v )
                    var resultSelectorLambda = MakeQueryUnboundLambda(state.RangeVariableMap(), ImmutableArray.Create(x1, x2), select.Expression);

                    invocation = MakeQueryInvocation(
                        join,
                        state.fromExpression,
                        "Join",
                        ImmutableArray.Create(inExpression, outerKeySelectorLambda, innerKeySelectorLambda, resultSelectorLambda),
                        diagnostics);
                }
                else
                {
                    // A query expression with a join clause with an into followed by a select clause
                    //     from x1 in e1
                    //     join x2 in e2 on k1 equals k2 into g
                    //     select v
                    // is translated into
                    //     ( e1 ) . GroupJoin( e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => v )
                    state.allRangeVariables[x2].Free();
                    state.allRangeVariables.Remove(x2);
                    var g = state.AddRangeVariable(this, join.Into.Identifier, diagnostics);

                    var resultSelectorLambda = MakeQueryUnboundLambda(state.RangeVariableMap(), ImmutableArray.Create(x1, g), select.Expression);

                    invocation = MakeQueryInvocation(
                        join,
                        state.fromExpression,
                        "GroupJoin",
                        ImmutableArray.Create(inExpression, outerKeySelectorLambda, innerKeySelectorLambda, resultSelectorLambda),
                        diagnostics);

                    // record the into clause in the bound tree
                    var arguments = invocation.Arguments;
                    arguments = arguments.SetItem(arguments.Length - 1, MakeQueryClause(join.Into, arguments[arguments.Length - 1], g));

                    invocation = invocation.Update(invocation.ReceiverOpt, invocation.Method, arguments);
                }

                state.Clear(); // this completes the whole query
                state.fromExpression = MakeQueryClause(join, invocation, x2, invocation, castInvocation);
                state.fromExpression = MakeQueryClause(select, state.fromExpression);
            }
            else
            {
                BoundCall invocation;
                if (join.Into == null)
                {
                    // A query expression with a join clause without an into followed by something other than a select clause
                    //     from x1 in e1
                    //     join x2 in e2 on k1 equals k2
                    //     ...
                    // is translated into
                    //     from * in ( e1 ) . Join(
                    //           e2 , x1 => k1 , x2 => k2 , ( x1 , x2 ) => new { x1 , x2 })
                    //     ...
                    var resultSelectorLambda = MakePairLambda(join, state, x1, x2);

                    invocation = MakeQueryInvocation(
                        join,
                        state.fromExpression,
                        "Join",
                        ImmutableArray.Create(inExpression, outerKeySelectorLambda, innerKeySelectorLambda, resultSelectorLambda),
                        diagnostics);
                }
                else
                {
                    // A query expression with a join clause with an into followed by something other than a select clause
                    //     from x1 in e1
                    //     join x2 in e2 on k1 equals k2 into g
                    //     ...
                    // is translated into
                    //     from * in ( e1 ) . GroupJoin(
                    //                 e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => new { x1 , g })
                    //     ...
                    state.allRangeVariables[x2].Free();
                    state.allRangeVariables.Remove(x2);

                    var g = state.AddRangeVariable(this, join.Into.Identifier, diagnostics);
                    var resultSelectorLambda = MakePairLambda(join, state, x1, g);

                    invocation = MakeQueryInvocation(
                        join,
                        state.fromExpression,
                        "GroupJoin",
                        ImmutableArray.Create(inExpression, outerKeySelectorLambda, innerKeySelectorLambda, resultSelectorLambda),
                        diagnostics);

                    var arguments = invocation.Arguments;
                    arguments = arguments.SetItem(arguments.Length - 1, MakeQueryClause(join.Into, arguments[arguments.Length - 1], g));

                    invocation = invocation.Update(invocation.ReceiverOpt, invocation.Method, arguments);
                }

                state.fromExpression = MakeQueryClause(join, invocation, x2, invocation, castInvocation);
            }
        }
Esempio n. 22
0
        /// <summary>
        /// For cases where the RHS of a deconstruction-declaration is a tuple literal, we merge type information from both the LHS and RHS.
        /// For cases where the RHS of a deconstruction-assignment is a tuple literal, the type information from the LHS determines the merged type, since all variables have a type.
        /// Returns null if a merged tuple type could not be fabricated.
        /// </summary>
        private static TypeSymbol MakeMergedTupleType(ArrayBuilder <DeconstructionVariable> lhsVariables, BoundTupleLiteral rhsLiteral, CSharpSyntaxNode syntax, CSharpCompilation compilation, DiagnosticBag diagnostics)
        {
            int leftLength  = lhsVariables.Count;
            int rightLength = rhsLiteral.Arguments.Length;

            var typesBuilder = ArrayBuilder <TypeSymbolWithAnnotations> .GetInstance(leftLength);

            var locationsBuilder = ArrayBuilder <Location> .GetInstance(leftLength);

            for (int i = 0; i < rightLength; i++)
            {
                BoundExpression element    = rhsLiteral.Arguments[i];
                TypeSymbol      mergedType = element.Type;

                if (i < leftLength)
                {
                    var variable = lhsVariables[i];
                    if (variable.HasNestedVariables)
                    {
                        if (element.Kind == BoundKind.TupleLiteral)
                        {
                            // (variables) on the left and (elements) on the right
                            mergedType = MakeMergedTupleType(variable.NestedVariables, (BoundTupleLiteral)element, syntax, compilation, diagnostics);
                        }
                        else if ((object)mergedType == null)
                        {
                            // (variables) on the left and null on the right
                            Error(diagnostics, ErrorCode.ERR_DeconstructRequiresExpression, element.Syntax);
                        }
                    }
                    else
                    {
                        if ((object)variable.Single.Type != null)
                        {
                            // typed-variable on the left
                            mergedType = variable.Single.Type;
                        }
                    }
                }
                else
                {
                    if ((object)mergedType == null)
                    {
                        // a typeless element on the right, matching no variable on the left
                        Error(diagnostics, ErrorCode.ERR_DeconstructRequiresExpression, element.Syntax);
                    }
                }

                typesBuilder.Add(TypeSymbolWithAnnotations.Create(mergedType));
                locationsBuilder.Add(element.Syntax.Location);
            }

            if (typesBuilder.Any(t => t.IsNull))
            {
                typesBuilder.Free();
                locationsBuilder.Free();
                return(null);
            }

            // The tuple created here is not identical to the one created by
            // DeconstructionVariablesAsTuple. It represents a smaller
            // tree of types used for figuring out natural types in tuple literal.
            return(TupleTypeSymbol.Create(
                       locationOpt: null,
                       elementTypes: typesBuilder.ToImmutableAndFree(),
                       elementLocations: locationsBuilder.ToImmutableAndFree(),
                       elementNames: default(ImmutableArray <string>),
                       compilation: compilation,
                       diagnostics: diagnostics,
                       shouldCheckConstraints: true,
                       errorPositions: default(ImmutableArray <bool>),
                       syntax: syntax));
        }
Esempio n. 23
0
        private BoundBlock CreateLambdaBlockForQueryClause(ExpressionSyntax expression, BoundExpression result, DiagnosticBag diagnostics)
        {
            var locals = this.GetDeclaredLocalsForScope(expression);

            if (locals.Any())
            {
                CheckFeatureAvailability(expression, MessageID.IDS_FeatureExpressionVariablesInQueriesAndInitializers, diagnostics, locals[0].Locations[0]);
            }

            return(this.CreateBlockFromExpression(expression, locals, RefKind.None, result, expression, diagnostics));
        }
        /// <summary>
        /// The left represents a tree of L-values. The structure of right can be missing parts of the tree on the left.
        /// The conversion holds nested conversions and deconstruction information, which matches the tree from the left,
        /// and it provides the information to fill in the missing parts of the tree from the right and convert it to
        /// the tree from the left.
        ///
        /// A bound sequence is returned which has different phases of side-effects:
        /// - the initialization phase includes side-effects from the left, followed by evaluations of the right
        /// - the deconstruction phase includes all the invocations of Deconstruct methods and tuple element accesses below a Deconstruct call
        /// - the conversion phase
        /// - the assignment phase
        /// </summary>
        private BoundExpression RewriteDeconstruction(BoundTupleExpression left, Conversion conversion, BoundExpression right, bool isUsed)
        {
            var lhsTemps = ArrayBuilder <LocalSymbol> .GetInstance();

            var lhsEffects = ArrayBuilder <BoundExpression> .GetInstance();

            ArrayBuilder <Binder.DeconstructionVariable> lhsTargets = GetAssignmentTargetsAndSideEffects(left, lhsTemps, lhsEffects);
            BoundExpression result = RewriteDeconstruction(lhsTargets, conversion, left.Type, right, isUsed);

            Binder.DeconstructionVariable.FreeDeconstructionVariables(lhsTargets);
            if (result is null)
            {
                lhsTemps.Free();
                lhsEffects.Free();
                return(null);
            }

            return(_factory.Sequence(lhsTemps.ToImmutableAndFree(), lhsEffects.ToImmutableAndFree(), result));
        }
Esempio n. 25
0
 private TypeSymbol TypeOrError(BoundExpression e)
 {
     return(e.Type ?? CreateErrorType());
 }
Esempio n. 26
0
        /// <summary>
        /// This method find the set of applicable user-defined and lifted conversion operators, u.
        /// The set consists of the user-defined and lifted implicit conversion operators declared by
        /// the classes and structs in d that convert from a type encompassing source to a type encompassed by target.
        /// However if allowAnyTarget is true, then it considers all operators that convert from a type encompassing source
        /// to any target. This flag must be set only if we are computing user defined conversions from a given source
        /// type to any target type.
        /// </summary>
        /// <remarks>
        /// Currently allowAnyTarget flag is only set to true by <see cref="AnalyzeImplicitUserDefinedConversionForV6SwitchGoverningType"/>,
        /// where we must consider user defined implicit conversions from the type of the switch expression to
        /// any of the possible switch governing types.
        /// </remarks>
        private void ComputeApplicableUserDefinedImplicitConversionSet(
            BoundExpression sourceExpression,
            TypeSymbol source,
            TypeSymbol target,
            ArrayBuilder <NamedTypeSymbol> d,
            ArrayBuilder <UserDefinedConversionAnalysis> u,
            ref HashSet <DiagnosticInfo> useSiteDiagnostics,
            bool allowAnyTarget = false)
        {
            Debug.Assert(sourceExpression != null || (object)source != null);
            Debug.Assert(((object)target != null) == !allowAnyTarget);
            Debug.Assert(d != null);
            Debug.Assert(u != null);

            // SPEC: Find the set of applicable user-defined and lifted conversion operators, U.
            // SPEC: The set consists of the user-defined and lifted implicit conversion operators
            // SPEC: declared by the classes and structs in D that convert from a type encompassing
            // SPEC: E to a type encompassed by T. If U is empty, the conversion is undefined and
            // SPEC: a compile-time error occurs.

            // SPEC: Give a user-defined conversion operator that converts from a non-nullable
            // SPEC: value type S to a non-nullable value type T, a lifted conversion operator
            // SPEC: exists that converts from S? to T?.

            // DELIBERATE SPEC VIOLATION:
            //
            // The spec here essentially says that we add an applicable "regular" conversion and
            // an applicable lifted conversion, if there is one, to the candidate set, and then
            // let them duke it out to determine which one is "best".
            //
            // This is not at all what the native compiler does, and attempting to implement
            // the specification, or slight variations on it, produces too many backwards-compatibility
            // breaking changes.
            //
            // The native compiler deviates from the specification in two major ways here.
            // First, it does not add *both* the regular and lifted forms to the candidate set.
            // Second, the way it characterizes a "lifted" form is very, very different from
            // how the specification characterizes a lifted form.
            //
            // An operation, in this case, X-->Y, is properly said to be "lifted" to X?-->Y? via
            // the rule that X?-->Y? matches the behavior of X-->Y for non-null X, and converts
            // null X to null Y otherwise.
            //
            // The native compiler, by contrast, takes the existing operator and "lifts" either
            // the operator's parameter type or the operator's return type to nullable. For
            // example, a conversion from X?-->Y would be "lifted" to X?-->Y? by making the
            // conversion from X? to Y, and then from Y to Y?.  No "lifting" semantics
            // are imposed; we do not check to see if the X? is null. This operator is not
            // actually "lifted" at all; rather, an implicit conversion is applied to the
            // output. **The native compiler considers the result type Y? of that standard implicit
            // conversion to be the result type of the "lifted" conversion**, rather than
            // properly considering Y to be the result type of the conversion for the purposes
            // of computing the best output type.
            //
            // MOREOVER: the native compiler actually *does* implement nullable lifting semantics
            // in the case where the input type of the user-defined conversion is a non-nullable
            // value type and the output type is a nullable value type **or pointer type, or
            // reference type**. This is an enormous departure from the specification; the
            // native compiler will take a user-defined conversion from X-->Y? or X-->C and "lift"
            // it to a conversion from X?-->Y? or X?-->C that has nullable semantics.
            //
            // This is quite confusing. In this code we will classify the conversion as either
            // "normal" or "lifted" on the basis of *whether or not special lifting semantics
            // are to be applied*. That is, whether or not a later rewriting pass is going to
            // need to insert a check to see if the source expression is null, and decide
            // whether or not to call the underlying unlifted conversion or produce a null
            // value without calling the unlifted conversion.

            // DELIBERATE SPEC VIOLATION (See bug 17021)
            // The specification defines a type U as "encompassing" a type V
            // if there is a standard implicit conversion from U to V, and
            // neither are interface types.
            //
            // The intention of this language is to ensure that we do not allow user-defined
            // conversions that involve interfaces. We have a reasonable expectation that a
            // conversion that involves an interface is one that preserves referential identity,
            // and user-defined conversions usually do not.
            //
            // Now, suppose we have a standard conversion from Alpha to Beta, a user-defined
            // conversion from Beta to Gamma, and a standard conversion from Gamma to Delta.
            // The specification allows the implicit conversion from Alpha to Delta only if
            // Beta encompasses Alpha and Delta encompasses Gamma.  And therefore, none of them
            // can be interface types, de jure.
            //
            // However, the dev10 compiler only checks Alpha and Delta to see if they are interfaces,
            // and allows Beta and Gamma to be interfaces.
            //
            // So what's the big deal there? It's not legal to define a user-defined conversion where
            // the input or output types are interfaces, right?
            //
            // It is not legal to define such a conversion, no, but it is legal to create one via generic
            // construction. If we have a conversion from T to C<T>, then C<I> has a conversion from I to C<I>.
            //
            // The dev10 compiler fails to check for this situation. This means that,
            // you can convert from int to C<IComparable> because int implements IComparable, but cannot
            // convert from IComparable to C<IComparable>!
            //
            // Unfortunately, we know of several real programs that rely upon this bug, so we are going
            // to reproduce it here.

            if ((object)source != null && source.IsInterfaceType() || (object)target != null && target.IsInterfaceType())
            {
                return;
            }

            foreach (NamedTypeSymbol declaringType in d)
            {
                foreach (MethodSymbol op in declaringType.GetOperators(WellKnownMemberNames.ImplicitConversionName))
                {
                    // We might have a bad operator and be in an error recovery situation. Ignore it.
                    if (op.ReturnsVoid || op.ParameterCount != 1)
                    {
                        continue;
                    }

                    TypeSymbol convertsFrom   = op.ParameterTypes[0].TypeSymbol;
                    TypeSymbol convertsTo     = op.ReturnType.TypeSymbol;
                    Conversion fromConversion = EncompassingImplicitConversion(sourceExpression, source, convertsFrom, ref useSiteDiagnostics);
                    Conversion toConversion   = allowAnyTarget ? Conversion.Identity :
                                                EncompassingImplicitConversion(null, convertsTo, target, ref useSiteDiagnostics);

                    if (fromConversion.Exists && toConversion.Exists)
                    {
                        // There is an additional spec violation in the native compiler. Suppose
                        // we have a conversion from X-->Y and are asked to do "Y? y = new X();"  Clearly
                        // the intention is to convert from X-->Y via the implicit conversion, and then
                        // stick a standard implicit conversion from Y-->Y? on the back end. **In this
                        // situation, the native compiler treats the conversion as though it were
                        // actually X-->Y? in source for the purposes of determining the best target
                        // type of an operator.
                        //
                        // We perpetuate this fiction here.

                        if ((object)target != null && target.IsNullableType() && convertsTo.IsNonNullableValueType())
                        {
                            convertsTo   = MakeNullableType(convertsTo);
                            toConversion = allowAnyTarget ? Conversion.Identity :
                                           EncompassingImplicitConversion(null, convertsTo, target, ref useSiteDiagnostics);
                        }

                        u.Add(UserDefinedConversionAnalysis.Normal(op, fromConversion, toConversion, convertsFrom, convertsTo));
                    }
                    else if ((object)source != null && source.IsNullableType() && convertsFrom.IsNonNullableValueType() &&
                             (allowAnyTarget || target.CanBeAssignedNull()))
                    {
                        // As mentioned above, here we diverge from the specification, in two ways.
                        // First, we only check for the lifted form if the normal form was inapplicable.
                        // Second, we are supposed to apply lifting semantics only if the conversion
                        // parameter and return types are *both* non-nullable value types.
                        //
                        // In fact the native compiler determines whether to check for a lifted form on
                        // the basis of:
                        //
                        // * Is the type we are ultimately converting from a nullable value type?
                        // * Is the parameter type of the conversion a non-nullable value type?
                        // * Is the type we are ultimately converting to a nullable value type,
                        //   pointer type, or reference type?
                        //
                        // If the answer to all those questions is "yes" then we lift to nullable
                        // and see if the resulting operator is applicable.
                        TypeSymbol nullableFrom         = MakeNullableType(convertsFrom);
                        TypeSymbol nullableTo           = convertsTo.IsNonNullableValueType() ? MakeNullableType(convertsTo) : convertsTo;
                        Conversion liftedFromConversion = EncompassingImplicitConversion(sourceExpression, source, nullableFrom, ref useSiteDiagnostics);
                        Conversion liftedToConversion   = !allowAnyTarget?
                                                          EncompassingImplicitConversion(null, nullableTo, target, ref useSiteDiagnostics) :
                                                              Conversion.Identity;

                        if (liftedFromConversion.Exists && liftedToConversion.Exists)
                        {
                            u.Add(UserDefinedConversionAnalysis.Lifted(op, liftedFromConversion, liftedToConversion, nullableFrom, nullableTo));
                        }
                    }
                }
            }
        }
Esempio n. 27
0
 protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, string methodName, TypeSyntax typeArgSyntax, TypeSymbolWithAnnotations typeArg, DiagnosticBag diagnostics)
 {
     return(MakeQueryInvocation(node, receiver, methodName, new SeparatedSyntaxList <TypeSyntax>(new SyntaxNodeOrTokenList(typeArgSyntax, 0)), ImmutableArray.Create(typeArg), ImmutableArray <BoundExpression> .Empty, diagnostics));
 }
Esempio n. 28
0
        /// <remarks>
        /// NOTE: Keep this method in sync with <see cref="AnalyzeImplicitUserDefinedConversionForV6SwitchGoverningType"/>.
        /// </remarks>
        private UserDefinedConversionResult AnalyzeImplicitUserDefinedConversions(
            BoundExpression sourceExpression,
            TypeSymbol source,
            TypeSymbol target,
            ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert(sourceExpression != null || (object)source != null);
            Debug.Assert((object)target != null);

            // User-defined conversions that involve generics can be quite strange. There
            // are two basic problems: first, that generic user-defined conversions can be
            // "shadowed" by built-in conversions, and second, that generic user-defined
            // conversions can make conversions that would never have been legal user-defined
            // conversions if declared non-generically. I call this latter kind of conversion
            // a "suspicious" conversion.
            //
            // The shadowed conversions are easily dealt with:
            //
            // SPEC: If a predefined implicit conversion exists from a type S to type T,
            // SPEC: all user-defined conversions, implicit or explicit, are ignored.
            // SPEC: If a predefined explicit conversion exists from a type S to type T,
            // SPEC: any user-defined explicit conversion from S to T are ignored.
            //
            // The rule above can come into play in cases like:
            //
            // sealed class C<T> { public static implicit operator T(C<T> c) { ... } }
            // C<object> c = whatever;
            // object o = c;
            //
            // The built-in implicit conversion from C<object> to object must shadow
            // the user-defined implicit conversion.
            //
            // The caller of this method checks for user-defined conversions *after*
            // predefined implicit conversions, so we already know that if we got here,
            // there was no predefined implicit conversion.
            //
            // Note that a user-defined *implicit* conversion may win over a built-in
            // *explicit* conversion by the rule given above. That is, if we created
            // an implicit conversion from T to C<T>, then the user-defined implicit
            // conversion from object to C<object> could be valid, even though that
            // would be "replacing" a built-in explicit conversion with a user-defined
            // implicit conversion. This is one of the "suspicious" conversions,
            // as it would not be legal to declare a user-defined conversion from
            // object in a non-generic type.
            //
            // The way the native compiler handles suspicious conversions involving
            // interfaces is neither sensible nor in line with the rules in the
            // specification. It is not clear at this time whether we should be exactly
            // matching the native compiler, the specification, or neither, in Roslyn.

            // Spec (6.4.4 User-defined implicit conversions)
            //   A user-defined implicit conversion from an expression E to type T is processed as follows:

            // SPEC: Find the set of types D from which user-defined conversion operators...
            var d = ArrayBuilder <NamedTypeSymbol> .GetInstance();

            ComputeUserDefinedImplicitConversionTypeSet(source, target, d, ref useSiteDiagnostics);

            // SPEC: Find the set of applicable user-defined and lifted conversion operators, U...
            var ubuild = ArrayBuilder <UserDefinedConversionAnalysis> .GetInstance();

            ComputeApplicableUserDefinedImplicitConversionSet(sourceExpression, source, target, d, ubuild, ref useSiteDiagnostics);
            d.Free();
            ImmutableArray <UserDefinedConversionAnalysis> u = ubuild.ToImmutableAndFree();

            // SPEC: If U is empty, the conversion is undefined and a compile-time error occurs.
            if (u.Length == 0)
            {
                return(UserDefinedConversionResult.NoApplicableOperators(u));
            }

            // SPEC: Find the most specific source type SX of the operators in U...
            TypeSymbol sx = MostSpecificSourceTypeForImplicitUserDefinedConversion(u, source, ref useSiteDiagnostics);

            if ((object)sx == null)
            {
                return(UserDefinedConversionResult.NoBestSourceType(u));
            }

            // SPEC: Find the most specific target type TX of the operators in U...
            TypeSymbol tx = MostSpecificTargetTypeForImplicitUserDefinedConversion(u, target, ref useSiteDiagnostics);

            if ((object)tx == null)
            {
                return(UserDefinedConversionResult.NoBestTargetType(u));
            }

            int?best = MostSpecificConversionOperator(sx, tx, u);

            if (best == null)
            {
                return(UserDefinedConversionResult.Ambiguous(u));
            }

            return(UserDefinedConversionResult.Valid(u, best.Value));
        }
Esempio n. 29
0
        private BoundExpression HoistExpression(
            BoundExpression expr,
            AwaitExpressionSyntax awaitSyntaxOpt,
            int syntaxOffset,
            RefKind refKind,
            ArrayBuilder <BoundExpression> sideEffects,
            ArrayBuilder <StateMachineFieldSymbol> hoistedFields,
            ref bool needsSacrificialEvaluation)
        {
            switch (expr.Kind)
            {
            case BoundKind.ArrayAccess:
            {
                var             array      = (BoundArrayAccess)expr;
                BoundExpression expression = HoistExpression(array.Expression, awaitSyntaxOpt, syntaxOffset, RefKind.None, sideEffects, hoistedFields, ref needsSacrificialEvaluation);
                var             indices    = ArrayBuilder <BoundExpression> .GetInstance();

                foreach (var index in array.Indices)
                {
                    indices.Add(HoistExpression(index, awaitSyntaxOpt, syntaxOffset, RefKind.None, sideEffects, hoistedFields, ref needsSacrificialEvaluation));
                }

                needsSacrificialEvaluation = true;         // need to force array index out of bounds exceptions
                return(array.Update(expression, indices.ToImmutableAndFree(), array.Type));
            }

            case BoundKind.FieldAccess:
            {
                var field = (BoundFieldAccess)expr;
                if (field.FieldSymbol.IsStatic)
                {
                    // the address of a static field, and the value of a readonly static field, is stable
                    if (refKind != RefKind.None || field.FieldSymbol.IsLet)
                    {
                        return(expr);
                    }
                    goto default;
                }

                if (refKind == RefKind.None)
                {
                    goto default;
                }

                var isFieldOfStruct = !field.FieldSymbol.ContainingType.IsReferenceType;

                var receiver = HoistExpression(field.ReceiverOpt, awaitSyntaxOpt, syntaxOffset,
                                               isFieldOfStruct ? refKind : RefKind.None, sideEffects, hoistedFields, ref needsSacrificialEvaluation);
                if (receiver.Kind != BoundKind.ThisReference && !isFieldOfStruct)
                {
                    needsSacrificialEvaluation = true;         // need the null check in field receiver
                }

                return(F.Field(receiver, field.FieldSymbol));
            }

            case BoundKind.ThisReference:
            case BoundKind.BaseReference:
            case BoundKind.DefaultExpression:
                return(expr);

            case BoundKind.Call:
                var call = (BoundCall)expr;
                // NOTE: There are two kinds of 'In' arguments that we may see at this point:
                //       - `RefKindExtensions.StrictIn`     (originally specified with 'In' modifier)
                //       - `RefKind.In`                     (specified with no modifiers and matched an 'In' parameter)
                //
                //       It is allowed to spill ordinary `In` arguments by value if reference-preserving spilling is not possible.
                //       The "strict" ones do not permit implicit copying, so the same situation should result in an error.
                if (refKind != RefKind.None && refKind != RefKind.In)
                {
                    Debug.Assert(call.Method.RefKind != RefKind.None);
                    F.Diagnostics.Add(ErrorCode.ERR_RefReturningCallAndAwait, F.Syntax.Location, call.Method);
                }
                // method call is not referentially transparent, we can only spill the result value.
                refKind = RefKind.None;
                goto default;

            case BoundKind.ConditionalOperator:
                var conditional = (BoundConditionalOperator)expr;
                // NOTE: There are two kinds of 'In' arguments that we may see at this point:
                //       - `RefKindExtensions.StrictIn`     (originally specified with 'In' modifier)
                //       - `RefKind.In`                     (specified with no modifiers and matched an 'In' parameter)
                //
                //       It is allowed to spill ordinary `In` arguments by value if reference-preserving spilling is not possible.
                //       The "strict" ones do not permit implicit copying, so the same situation should result in an error.
                if (refKind != RefKind.None && refKind != RefKind.RefReadOnly)
                {
                    Debug.Assert(conditional.IsRef);
                    F.Diagnostics.Add(ErrorCode.ERR_RefConditionalAndAwait, F.Syntax.Location);
                }
                // conditional expr is not referentially transparent, we can only spill the result value.
                refKind = RefKind.None;
                goto default;

            default:
                if (expr.ConstantValue != null)
                {
                    return(expr);
                }

                if (refKind != RefKind.None)
                {
                    throw ExceptionUtilities.UnexpectedValue(expr.Kind);
                }

                TypeSymbol fieldType = expr.Type;
                StateMachineFieldSymbol hoistedField;
                if (F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug)
                {
                    const SynthesizedLocalKind kind = SynthesizedLocalKind.AwaitByRefSpill;

                    Debug.Assert(awaitSyntaxOpt != null);

                    int ordinal = _synthesizedLocalOrdinals.AssignLocalOrdinal(kind, syntaxOffset);
                    var id      = new LocalDebugId(syntaxOffset, ordinal);

                    // Editing await expression is not allowed. Thus all spilled fields will be present in the previous state machine.
                    // However, it may happen that the type changes, in which case we need to allocate a new slot.
                    int slotIndex;
                    if (slotAllocatorOpt == null ||
                        !slotAllocatorOpt.TryGetPreviousHoistedLocalSlotIndex(
                            awaitSyntaxOpt,
                            F.ModuleBuilderOpt.Translate(fieldType, awaitSyntaxOpt, Diagnostics),
                            kind,
                            id,
                            Diagnostics,
                            out slotIndex))
                    {
                        slotIndex = _nextFreeHoistedLocalSlot++;
                    }

                    string fieldName = GeneratedNames.MakeHoistedLocalFieldName(kind, slotIndex);
                    hoistedField = F.StateMachineField(expr.Type, fieldName, new LocalSlotDebugInfo(kind, id), slotIndex);
                }
                else
                {
                    hoistedField = GetOrAllocateReusableHoistedField(fieldType, reused: out _);
                }

                hoistedFields.Add(hoistedField);

                var replacement = F.Field(F.This(), hoistedField);
                sideEffects.Add(F.AssignmentExpression(replacement, expr));
                return(replacement);
            }
        }
Esempio n. 30
0
        private BoundExpression RewriteStringConcatenationThreeExprs(SyntaxNode syntax, BoundExpression loweredFirst, BoundExpression loweredSecond, BoundExpression loweredThird)
        {
            SpecialMember member = (loweredFirst.Type.SpecialType == SpecialType.System_String &&
                                    loweredSecond.Type.SpecialType == SpecialType.System_String &&
                                    loweredThird.Type.SpecialType == SpecialType.System_String) ?
                                   SpecialMember.System_String__ConcatStringStringString :
                                   SpecialMember.System_String__ConcatObjectObjectObject;

            var method = UnsafeGetSpecialTypeMethod(syntax, member);

            Debug.Assert((object)method != null);

            return(BoundCall.Synthesized(syntax, null, method, ImmutableArray.Create(loweredFirst, loweredSecond, loweredThird)));
        }