Beispiel #1
0
        private BoundExpression MakeFieldAccess(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenReceiver,
            FieldSymbol fieldSymbol,
            ConstantValue constantValueOpt,
            LookupResultKind resultKind,
            TypeSymbol type,
            BoundFieldAccess oldNodeOpt = null)
        {

            if (fieldSymbol.IsTupleField)
            {
                return MakeTupleFieldAccess(syntax, fieldSymbol, rewrittenReceiver, constantValueOpt, resultKind);
            }
            
            BoundExpression result = oldNodeOpt != null ?
                oldNodeOpt.Update(rewrittenReceiver, fieldSymbol, constantValueOpt, resultKind, type) :
                new BoundFieldAccess(syntax, rewrittenReceiver, fieldSymbol, constantValueOpt, resultKind, type);

            if (fieldSymbol.IsFixed)
            {
                // a reference to a fixed buffer is translated into its address
                result = new BoundConversion(syntax,
                    new BoundAddressOfOperator(syntax, result, syntax != null && SyntaxFacts.IsFixedStatementExpression(syntax), type, false),
                    new Conversion(ConversionKind.PointerToPointer), false, false, default(ConstantValue), type, false);
            }

            return result;
        }
        public override BoundNode VisitConversion(BoundConversion node)
        {
            var rewrittenType = VisitType(node.Type);
            bool wasInExpressionLambda = inExpressionLambda;
            if (node.ConversionKind == ConversionKind.AnonymousFunction && !wasInExpressionLambda && rewrittenType.IsExpressionTree()) inExpressionLambda = true;
            var rewrittenOperand = VisitExpression(node.Operand);
            inExpressionLambda = wasInExpressionLambda;

            var result = MakeConversion(node, node.Syntax, rewrittenOperand, node.ConversionKind, node.SymbolOpt, node.Checked,
                node.ExplicitCastInCode, node.IsExtensionMethod, node.IsArrayIndex, node.ConstantValue, rewrittenType);

            var toType = node.Type;
            Debug.Assert(result.Type == toType);

            // 4.1.6 C# spec: To force a value of a floating point type to the exact precision of its type, an explicit cast can be used.
            // It means that explicit casts to (double) or (float) should be preserved on the node.
            // If original conversion has become something else with unknown precision, add an explicit identity cast.
            if (!inExpressionLambda &&
                node.ExplicitCastInCode &&
                IsFloatPointExpressionOfUnknownPrecision(result))
            {
                result = MakeConversion(
                    node.Syntax,
                    result,
                    Conversion.Identity,
                    toType,
                    @checked: false,
                    explicitCastInCode: true,
                    constantValueOpt: result.ConstantValue);
            }

            return result;
        }
            public override BoundNode VisitConversion(BoundConversion conversion)
            {
                if (conversion.ConversionKind == ConversionKind.MethodGroup &&
                    conversion.SymbolOpt?.MethodKind == MethodKind.LocalFunction)
                {
                    BoundExpression receiver;
                    MethodSymbol method;
                    var arguments = default(ImmutableArray<BoundExpression>);
                    _lambdaRewriter.RemapLocalFunction(
                        conversion.Syntax, conversion.SymbolOpt, out receiver, out method, ref arguments);

                    return new BoundDelegateCreationExpression(
                        conversion.Syntax, receiver, method, isExtensionMethod: false, type: conversion.Type);
                }
                return base.VisitConversion(conversion);
            }
Beispiel #4
0
        public BoundReturnStatement Return(BoundExpression expression = null)
        {
            if (expression != null)
            {
                // If necessary, add a conversion on the return expression.
                HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                var conversion = Compilation.Conversions.ClassifyConversion(expression.Type, CurrentMethod.ReturnType, ref useSiteDiagnostics);
                Debug.Assert(useSiteDiagnostics.IsNullOrEmpty());
                Debug.Assert(conversion.Kind != ConversionKind.NoConversion);
                if (conversion.Kind != ConversionKind.Identity)
                {
                    expression = BoundConversion.Synthesized(Syntax, expression, conversion, false, false, ConstantValue.NotAvailable, CurrentMethod.ReturnType);
                }
            }

            return(new BoundReturnStatement(Syntax, expression)
            {
                WasCompilerGenerated = true
            });
        }
 public override BoundNode VisitConversion(BoundConversion node)
 {
     if (node.ConversionKind == ConversionKind.MethodGroup)
     {
         if (node.SymbolOpt?.MethodKind == MethodKind.LocalFunction)
         {
             // Use OriginalDefinition to strip generic type parameters
             ReferenceVariable(node.Syntax, node.SymbolOpt.OriginalDefinition);
             MethodsConvertedToDelegates.Add(node.SymbolOpt.OriginalDefinition);
         }
         if (node.IsExtensionMethod || ((object)node.SymbolOpt != null && !node.SymbolOpt.IsStatic))
         {
             return(VisitSyntaxWithReceiver(node.Syntax, ((BoundMethodGroup)node.Operand).ReceiverOpt));
         }
         return(null);
     }
     else
     {
         return(base.VisitConversion(node));
     }
 }
            public override BoundNode VisitConversion(BoundConversion conversion)
            {
                if (conversion.ConversionKind == ConversionKind.MethodGroup &&
                    conversion.SymbolOpt?.MethodKind == MethodKind.LocalFunction)
                {
                    BoundExpression receiver;
                    MethodSymbol    method;
                    var             arguments = default(ImmutableArray <BoundExpression>);
                    _lambdaRewriter.RemapLocalFunction(
                        conversion.Syntax,
                        conversion.SymbolOpt,
                        out receiver,
                        out method,
                        ref arguments);
                    var newType = _lambdaRewriter.VisitType(conversion.Type);

                    return(new BoundDelegateCreationExpression(
                               conversion.Syntax, receiver, method, isExtensionMethod: false, type: newType));
                }
                return(base.VisitConversion(conversion));
            }
Beispiel #7
0
        private static bool IsInstanceFieldAccessWithNonThisReceiver(BoundFieldAccess fieldAccess)
        {
            BoundExpression receiver = fieldAccess.ReceiverOpt;

            if (receiver == null || fieldAccess.FieldSymbol.IsStatic)
            {
                return(false);
            }

            while (receiver.Kind == BoundKind.Conversion)
            {
                BoundConversion conversion = (BoundConversion)receiver;
                if (conversion.ExplicitCastInCode)
                {
                    break;
                }
                receiver = conversion.Operand;
            }

            return(receiver.Kind != BoundKind.ThisReference && receiver.Kind != BoundKind.BaseReference);
        }
        public override BoundNode VisitConversion(BoundConversion node)
        {
            CheckUnsafeType(node.Operand);
            CheckUnsafeType(node);
            bool wasInExpressionLambda = _inExpressionLambda;
            bool oldReportedUnsafe     = _reportedUnsafe;

            switch (node.ConversionKind)
            {
            case ConversionKind.MethodGroup:
                VisitMethodGroup((BoundMethodGroup)node.Operand, parentIsConversion: true);
                return(node);

            case ConversionKind.AnonymousFunction:
                if (!wasInExpressionLambda && node.Type.IsExpressionTree())
                {
                    _inExpressionLambda = true;
                    // we report "unsafe in expression tree" at most once for each expression tree
                    _reportedUnsafe = false;
                }
                break;

            case ConversionKind.ImplicitDynamic:
            case ConversionKind.ExplicitDynamic:
                if (_inExpressionLambda)
                {
                    Error(ErrorCode.ERR_ExpressionTreeContainsDynamicOperation, node);
                }
                break;

            default:
                break;
            }

            var result = base.VisitConversion(node);

            _inExpressionLambda = wasInExpressionLambda;
            _reportedUnsafe     = oldReportedUnsafe;
            return(result);
        }
Beispiel #9
0
        private static ConstantValue GetConstantValueForBitwiseOrCheck(BoundExpression operand)
        {
            // We might have a nullable conversion on top of an integer constant. But only dig out
            // one level.

            if (operand.Kind == BoundKind.Conversion)
            {
                BoundConversion conv = (BoundConversion)operand;
                if (conv.ConversionKind == ConversionKind.ImplicitNullable)
                {
                    operand = conv.Operand;
                }
            }

            ConstantValue constVal = operand.ConstantValue;

            if (constVal == null || !constVal.IsIntegral)
            {
                return(null);
            }

            return(constVal);
        }
Beispiel #10
0
        private static BoundExpression MakeFieldAccess(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenReceiver,
            FieldSymbol fieldSymbol,
            ConstantValue constantValueOpt,
            LookupResultKind resultKind,
            TypeSymbol type,
            BoundFieldAccess oldNodeOpt = null)
        {
            BoundExpression result = oldNodeOpt != null?
                                     oldNodeOpt.Update(rewrittenReceiver, fieldSymbol, constantValueOpt, resultKind, type) :
                                         new BoundFieldAccess(syntax, rewrittenReceiver, fieldSymbol, constantValueOpt, resultKind, type);

            if (fieldSymbol.IsFixed)
            {
                // a reference to a fixed buffer is translated into its address
                result = new BoundConversion(syntax,
                                             new BoundAddressOfOperator(syntax, result, syntax != null && SyntaxFacts.IsFixedStatementExpression(syntax), type, false),
                                             new Conversion(ConversionKind.PointerToPointer), false, false, default(ConstantValue), type, false);
            }

            return(result);
        }
        public override BoundNode VisitConversion(BoundConversion node)
        {
            var rewrittenType = VisitType(node.Type);
            if (node.ConversionKind == ConversionKind.InterpolatedString)
            {
                return RewriteInterpolatedStringConversion(node);
            }

            bool wasInExpressionLambda = _inExpressionLambda;
            _inExpressionLambda = _inExpressionLambda || (node.ConversionKind == ConversionKind.AnonymousFunction && !wasInExpressionLambda && rewrittenType.IsExpressionTree());
            var rewrittenOperand = VisitExpression(node.Operand);
            _inExpressionLambda = wasInExpressionLambda;

            var result = MakeConversionNode(node, node.Syntax, rewrittenOperand, node.Conversion, node.Checked, node.ExplicitCastInCode, node.ConstantValue, rewrittenType);

            var toType = node.Type;
            Debug.Assert(result.Type.Equals(toType, TypeCompareKind.IgnoreDynamicAndTupleNames));

            // 4.1.6 C# spec: To force a value of a floating point type to the exact precision of its type, an explicit cast can be used.
            // It means that explicit casts to (double) or (float) should be preserved on the node.
            // If original conversion has become something else with unknown precision, add an explicit identity cast.
            if (!_inExpressionLambda &&
                node.ExplicitCastInCode &&
                IsFloatPointExpressionOfUnknownPrecision(result))
            {
                result = MakeConversionNode(
                    node.Syntax,
                    result,
                    Conversion.Identity,
                    toType,
                    @checked: false,
                    explicitCastInCode: true,
                    constantValueOpt: result.ConstantValue);
            }

            return result;
        }
Beispiel #12
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(CSharpSyntaxNode 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 = GetSpecialTypeMethod(syntax, SpecialMember.System_Object__ToString);
                            return(BoundCall.Synthesized(syntax, operand, toString));
                        }
                    }
                }
            }

            // Optimization not possible; just return the original expression.
            return(expr);
        }
        private BoundExpression RewriteInterpolatedStringConversion(BoundConversion conversion)
        {
            Debug.Assert(conversion.ConversionKind == ConversionKind.InterpolatedString);
            BoundExpression format;
            ArrayBuilder<BoundExpression> expressions;
            MakeInterpolatedStringFormat((BoundInterpolatedString)conversion.Operand, out format, out expressions);
            expressions.Insert(0, format);
            var stringFactory = _factory.WellKnownType(WellKnownType.System_Runtime_CompilerServices_FormattableStringFactory);

            // The normal pattern for lowering is to lower subtrees before the enclosing tree. However we cannot lower
            // the arguments first in this situation because we do not know what conversions will be
            // produced for the arguments until after we've done overload resolution. So we produce the invocation
            // and then lower it along with its arguments.
            var result = _factory.StaticCall(stringFactory, "Create", expressions.ToImmutableAndFree(),
                allowUnexpandedForm: false // if an interpolation expression is the null literal, it should not match a params parameter.
                );
            if (!result.HasAnyErrors)
            {
                result = VisitExpression(result); // lower the arguments AND handle expanded form, argument conversions, etc.
                result = MakeImplicitConversion(result, conversion.Type);
            }

            return result;
        }
        public override BoundNode VisitConversion(BoundConversion node)
        {
            BoundExpression operand = (BoundExpression)this.Visit(node.Operand);
            TypeSymbol type = this.VisitType(node.Type);

            if (operand.Kind != BoundKind.SpillSequence)
            {
                return node.Update(operand, node.ConversionKind, node.SymbolOpt, node.Checked, node.ExplicitCastInCode, node.IsExtensionMethod, node.IsArrayIndex, node.ConstantValueOpt, node.ResultKind, type);
            }

            var spill = (BoundSpillSequence)operand;
            return RewriteSpillSequence(spill,
                node.Update(
                    spill.Value,
                    node.ConversionKind,
                    node.SymbolOpt,
                    node.Checked,
                    node.ExplicitCastInCode,
                    node.IsExtensionMethod,
                    node.IsArrayIndex,
                    node.ConstantValueOpt,
                    node.ResultKind,
                    type));
        }
                public override BoundNode VisitConversion(BoundConversion node)
                {
                    visitConversion(node.Conversion);
                    if (!_mightAssignSomething)
                    {
                        base.VisitConversion(node);
                    }
                    return(null);

                    void visitConversion(Conversion conversion)
                    {
                        switch (conversion.Kind)
                        {
                        case ConversionKind.MethodGroup:
                            if (conversion.Method.MethodKind == MethodKind.LocalFunction)
                            {
                                _mightAssignSomething = true;
                            }
                            break;

                        default:
                            if (!conversion.UnderlyingConversions.IsDefault)
                            {
                                foreach (var underlying in conversion.UnderlyingConversions)
                                {
                                    visitConversion(underlying);
                                    if (_mightAssignSomething)
                                    {
                                        return;
                                    }
                                }
                            }
                            break;
                        }
                    }
                }
Beispiel #16
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));
        }
 public override BoundNode VisitConversion(BoundConversion node)
 {
     if (node.ConversionKind == ConversionKind.MethodGroup)
     {
         if (node.SymbolOpt?.MethodKind == MethodKind.LocalFunction)
         {
             // Use OriginalDefinition to strip generic type parameters
             ReferenceVariable(node.Syntax, node.SymbolOpt.OriginalDefinition);
             MethodsConvertedToDelegates.Add(node.SymbolOpt.OriginalDefinition);
         }
         if (node.IsExtensionMethod || ((object)node.SymbolOpt != null && !node.SymbolOpt.IsStatic))
         {
             return VisitSyntaxWithReceiver(node.Syntax, ((BoundMethodGroup)node.Operand).ReceiverOpt);
         }
         return null;
     }
     else
     {
         return base.VisitConversion(node);
     }
 }
 private BoundExpression VisitConversion(BoundConversion node)
 {
     switch (node.ConversionKind)
     {
         case ConversionKind.MethodGroup:
             {
                 var mg = (BoundMethodGroup)node.Operand;
                 return DelegateCreation(mg.ReceiverOpt, node.SymbolOpt, node.Type, node.SymbolOpt.IsStatic && !node.IsExtensionMethod);
             }
         case ConversionKind.ExplicitUserDefined:
         case ConversionKind.ImplicitUserDefined:
         case ConversionKind.IntPtr:
             {
                 var method = node.SymbolOpt;
                 var operandType = node.Operand.Type;
                 var strippedOperandType = operandType.StrippedType();
                 var conversionInputType = method.Parameters[0].Type;
                 var isLifted = operandType != conversionInputType && strippedOperandType == conversionInputType;
                 bool requireAdditionalCast =
                     strippedOperandType != ((node.ConversionKind == ConversionKind.ExplicitUserDefined) ? conversionInputType : conversionInputType.StrippedType());
                 var resultType = (isLifted && method.ReturnType.IsNonNullableValueType() && node.Type.IsNullableType()) ? _nullableType.Construct(method.ReturnType) : method.ReturnType;
                 var e1 = requireAdditionalCast
                     ? Convert(Visit(node.Operand), node.Operand.Type, method.Parameters[0].Type, node.Checked, false)
                     : Visit(node.Operand);
                 var e2 = ExprFactory("Convert", e1, _bound.Typeof(resultType), _bound.MethodInfo(method));
                 return Convert(e2, resultType, node.Type, node.Checked, false);
             }
         case ConversionKind.ImplicitReference:
         case ConversionKind.Identity:
             {
                 var operand = Visit(node.Operand);
                 return node.ExplicitCastInCode ? Convert(operand, node.Type, false) : operand;
             }
         case ConversionKind.ImplicitNullable:
             if (node.Operand.Type.IsNullableType())
             {
                 return Convert(Visit(node.Operand), node.Operand.Type, node.Type, node.Checked, node.ExplicitCastInCode);
             }
             else
             {
                 // the native compiler performs this conversion in two steps, so we follow suit
                 var nullable = (NamedTypeSymbol)node.Type;
                 var intermediate = nullable.TypeArgumentsNoUseSiteDiagnostics[0];
                 var e1 = Convert(Visit(node.Operand), node.Operand.Type, intermediate, node.Checked, false);
                 return Convert(e1, intermediate, node.Type, node.Checked, false);
             }
         case ConversionKind.NullLiteral:
             return Convert(Constant(_bound.Null(_objectType)), _objectType, node.Type, false, node.ExplicitCastInCode);
         default:
             return Convert(Visit(node.Operand), node.Operand.Type, node.Type, node.Checked, node.ExplicitCastInCode);
     }
 }
Beispiel #19
0
        private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics, Binder originalBinder)
        {
            // Use the right binder to avoid seeing iteration variable
            BoundExpression collectionExpr = originalBinder.GetBinder(_syntax.Expression).BindValue(_syntax.Expression, diagnostics, BindValueKind.RValue); 

            ForEachEnumeratorInfo.Builder builder = new ForEachEnumeratorInfo.Builder();
            TypeSymbol inferredType;
            bool hasErrors = !GetEnumeratorInfoAndInferCollectionElementType(ref builder, ref collectionExpr, diagnostics, out inferredType);

            // These should only occur when special types are missing or malformed.
            hasErrors = hasErrors ||
                (object)builder.GetEnumeratorMethod == null ||
                (object)builder.MoveNextMethod == null ||
                (object)builder.CurrentPropertyGetter == null;

            // Check for local variable conflicts in the *enclosing* binder; obviously the *current*
            // binder has a local that matches!
            var hasNameConflicts = originalBinder.ValidateDeclarationNameConflictsInScope(IterationVariable, diagnostics);

            // If the type in syntax is "var", then the type should be set explicitly so that the
            // Type property doesn't fail.

            TypeSyntax typeSyntax = _syntax.Type;

            bool isVar;
            AliasSymbol alias;
            TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out alias);

            TypeSymbol iterationVariableType;
            if (isVar)
            {
                iterationVariableType = inferredType ?? CreateErrorType("var");
            }
            else
            {
                Debug.Assert((object)declType != null);
                iterationVariableType = declType;
            }


            BoundTypeExpression boundIterationVariableType = new BoundTypeExpression(typeSyntax, alias, iterationVariableType);
            this.IterationVariable.SetTypeSymbol(iterationVariableType);

            BoundStatement body = originalBinder.BindPossibleEmbeddedStatement(_syntax.Statement, diagnostics);

            hasErrors = hasErrors || iterationVariableType.IsErrorType();

            // Skip the conversion checks and array/enumerator differentiation if we know we have an error (except local name conflicts).
            if (hasErrors)
            {
                return new BoundForEachStatement(
                    _syntax,
                    null, // can't be sure that it's complete
                    default(Conversion),
                    boundIterationVariableType,
                    this.IterationVariable,
                    collectionExpr,
                    body,
                    CheckOverflowAtRuntime,
                    this.BreakLabel,
                    this.ContinueLabel,
                    hasErrors);
            }

            hasErrors |= hasNameConflicts;

            var foreachKeyword = _syntax.ForEachKeyword;
            ReportDiagnosticsIfObsolete(diagnostics, builder.GetEnumeratorMethod, foreachKeyword, hasBaseReceiver: false);
            ReportDiagnosticsIfObsolete(diagnostics, builder.MoveNextMethod, foreachKeyword, hasBaseReceiver: false);
            ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter, foreachKeyword, hasBaseReceiver: false);
            ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter.AssociatedSymbol, foreachKeyword, hasBaseReceiver: false);

            // We want to convert from inferredType in the array/string case and builder.ElementType in the enumerator case,
            // but it turns out that these are equivalent (when both are available).

            HashSet<DiagnosticInfo> useSiteDiagnostics = null;
            Conversion elementConversion = this.Conversions.ClassifyConversionForCast(inferredType, iterationVariableType, ref useSiteDiagnostics);

            if (!elementConversion.IsValid)
            {
                ImmutableArray<MethodSymbol> originalUserDefinedConversions = elementConversion.OriginalUserDefinedConversions;
                if (originalUserDefinedConversions.Length > 1)
                {
                    diagnostics.Add(ErrorCode.ERR_AmbigUDConv, _syntax.ForEachKeyword.GetLocation(), originalUserDefinedConversions[0], originalUserDefinedConversions[1], inferredType, iterationVariableType);
                }
                else
                {
                    SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, inferredType, iterationVariableType);
                    diagnostics.Add(ErrorCode.ERR_NoExplicitConv, _syntax.ForEachKeyword.GetLocation(), distinguisher.First, distinguisher.Second);
                }
                hasErrors = true;
            }
            else
            {
                ReportDiagnosticsIfObsolete(diagnostics, elementConversion, _syntax.ForEachKeyword, hasBaseReceiver: false);
            }

            // Spec (§8.8.4):
            // If the type X of expression is dynamic then there is an implicit conversion from >>expression<< (not the type of the expression) 
            // to the System.Collections.IEnumerable interface (§6.1.8). 
            builder.CollectionConversion = this.Conversions.ClassifyConversionFromExpression(collectionExpr, builder.CollectionType, ref useSiteDiagnostics);
            builder.CurrentConversion = this.Conversions.ClassifyConversion(builder.CurrentPropertyGetter.ReturnType, builder.ElementType, ref useSiteDiagnostics);

            builder.EnumeratorConversion = this.Conversions.ClassifyConversion(builder.GetEnumeratorMethod.ReturnType, GetSpecialType(SpecialType.System_Object, diagnostics, _syntax), ref useSiteDiagnostics);

            diagnostics.Add(_syntax.ForEachKeyword.GetLocation(), useSiteDiagnostics);

            // Due to the way we extracted the various types, these conversions should always be possible.
            // CAVEAT: if we're iterating over an array of pointers, the current conversion will fail since we
            // can't convert from object to a pointer type.  Similarly, if we're iterating over an array of
            // Nullable<Error>, the current conversion will fail because we don't know if an ErrorType is a
            // value type.  This doesn't matter in practice, since we won't actually use the enumerator pattern 
            // when we lower the loop.
            Debug.Assert(builder.CollectionConversion.IsValid);
            Debug.Assert(builder.CurrentConversion.IsValid ||
                (builder.ElementType.IsPointerType() && collectionExpr.Type.IsArray()) ||
                (builder.ElementType.IsNullableType() && builder.ElementType.GetMemberTypeArgumentsNoUseSiteDiagnostics().Single().IsErrorType() && collectionExpr.Type.IsArray()));
            Debug.Assert(builder.EnumeratorConversion.IsValid ||
                this.Compilation.GetSpecialType(SpecialType.System_Object).TypeKind == TypeKind.Error ||
                !useSiteDiagnostics.IsNullOrEmpty(),
                "Conversions to object succeed unless there's a problem with the object type or the source type");

            // If user-defined conversions could occur here, we would need to check for ObsoleteAttribute.
            Debug.Assert((object)builder.CollectionConversion.Method == null,
                "Conversion from collection expression to collection type should not be user-defined");
            Debug.Assert((object)builder.CurrentConversion.Method == null,
                "Conversion from Current property type to element type should not be user-defined");
            Debug.Assert((object)builder.EnumeratorConversion.Method == null,
                "Conversion from GetEnumerator return type to System.Object should not be user-defined");

            // We're wrapping the collection expression in a (non-synthesized) conversion so that its converted
            // type (i.e. builder.CollectionType) will be available in the binding API.
            BoundConversion convertedCollectionExpression = new BoundConversion(
                collectionExpr.Syntax,
                collectionExpr,
                builder.CollectionConversion,
                CheckOverflowAtRuntime,
                false,
                ConstantValue.NotAvailable,
                builder.CollectionType);

            return new BoundForEachStatement(
                _syntax,
                builder.Build(this.Flags),
                elementConversion,
                boundIterationVariableType,
                this.IterationVariable,
                convertedCollectionExpression,
                body,
                CheckOverflowAtRuntime,
                this.BreakLabel,
                this.ContinueLabel,
                hasErrors);
        }
Beispiel #20
0
        private static bool IsSafeForReordering(BoundExpression expression, RefKind kind)
        {
            // To be safe for reordering an expression must not cause any observable side effect *or
            // observe any side effect*. Accessing a local by value, for example, is possibly not
            // safe for reordering because reading a local can give a different result if reordered
            // with respect to a write elsewhere.

            var current = expression;

            while (true)
            {
                if (current.ConstantValue != null)
                {
                    return(true);
                }

                switch (current.Kind)
                {
                default:
                    return(false);

                case BoundKind.Parameter:
                case BoundKind.Local:
                    // A ref to a local variable or formal parameter is safe to reorder; it
                    // never has a side effect or consumes one.
                    return(kind != RefKind.None);

                case BoundKind.Conversion:
                {
                    BoundConversion conv = (BoundConversion)current;
                    switch (conv.ConversionKind)
                    {
                    case ConversionKind.AnonymousFunction:
                    case ConversionKind.ImplicitConstant:
                    case ConversionKind.MethodGroup:
                    case ConversionKind.NullLiteral:
                        return(true);

                    case ConversionKind.Boxing:
                    case ConversionKind.ImplicitDynamic:
                    case ConversionKind.ExplicitDynamic:
                    case ConversionKind.ExplicitEnumeration:
                    case ConversionKind.ExplicitNullable:
                    case ConversionKind.ExplicitNumeric:
                    case ConversionKind.ExplicitReference:
                    case ConversionKind.Identity:
                    case ConversionKind.ImplicitEnumeration:
                    case ConversionKind.ImplicitNullable:
                    case ConversionKind.ImplicitNumeric:
                    case ConversionKind.ImplicitReference:
                    case ConversionKind.Unboxing:
                    case ConversionKind.PointerToInteger:
                    case ConversionKind.PointerToPointer:
                    case ConversionKind.PointerToVoid:
                    case ConversionKind.NullToPointer:
                    case ConversionKind.IntegerToPointer:
                        current = conv.Operand;
                        break;

                    case ConversionKind.ExplicitUserDefined:
                    case ConversionKind.ImplicitUserDefined:
                        return(false);

                    default:
                        Debug.Assert(false, "Unhandled conversion kind in reordering logic");
                        return(false);
                    }
                    break;
                }
                }
            }
        }
Beispiel #21
0
        /// <summary>
        /// Generates a submission initialization part of a Script type constructor that represents an interactive submission.
        /// </summary>
        /// <remarks>
        /// The constructor takes a parameter of type Roslyn.Scripting.Session - the session reference.
        /// It adds the object being constructed into the session by calling Microsoft.CSharp.RuntimeHelpers.SessionHelpers.SetSubmission,
        /// and retrieves strongly typed references on all previous submission script classes whose members are referenced by this submission.
        /// The references are stored to fields of the submission (<paramref name="synthesizedFields"/>).
        /// </remarks>
        private static ImmutableArray <BoundStatement> MakeSubmissionInitialization(CSharpSyntaxNode syntax, MethodSymbol submissionConstructor, SynthesizedSubmissionFields synthesizedFields, CSharpCompilation compilation)
        {
            Debug.Assert(submissionConstructor.ParameterCount == 2);

            BoundStatement[] result = new BoundStatement[1 + synthesizedFields.Count];

            var sessionReference = new BoundParameter(syntax, submissionConstructor.Parameters[0])
            {
                WasCompilerGenerated = true
            };
            var submissionGetter = (MethodSymbol)compilation.GetWellKnownTypeMember(
                WellKnownMember.Microsoft_CSharp_RuntimeHelpers_SessionHelpers__GetSubmission
                );
            var submissionAdder = (MethodSymbol)compilation.GetWellKnownTypeMember(
                WellKnownMember.Microsoft_CSharp_RuntimeHelpers_SessionHelpers__SetSubmission
                );

            // TODO: report missing adder/getter
            Debug.Assert((object)submissionAdder != null && (object)submissionGetter != null);

            var intType       = compilation.GetSpecialType(SpecialType.System_Int32);
            var thisReference = new BoundThisReference(syntax, submissionConstructor.ContainingType)
            {
                WasCompilerGenerated = true
            };

            int i = 0;

            // hostObject = (THostObject)SessionHelpers.SetSubmission(<session>, <slot index>, this);
            var slotIndex = compilation.GetSubmissionSlotIndex();

            Debug.Assert(slotIndex >= 0);

            BoundExpression setSubmission = BoundCall.Synthesized(syntax,
                                                                  null,
                                                                  submissionAdder,
                                                                  sessionReference,
                                                                  new BoundLiteral(syntax, ConstantValue.Create(slotIndex), intType)
            {
                WasCompilerGenerated = true
            },
                                                                  thisReference
                                                                  );

            var hostObjectField = synthesizedFields.GetHostObjectField();

            if ((object)hostObjectField != null)
            {
                setSubmission = new BoundAssignmentOperator(syntax,
                                                            new BoundFieldAccess(syntax, thisReference, hostObjectField, ConstantValue.NotAvailable)
                {
                    WasCompilerGenerated = true
                },
                                                            BoundConversion.Synthesized(syntax,
                                                                                        setSubmission,
                                                                                        Conversion.ExplicitReference,
                                                                                        false,
                                                                                        true,
                                                                                        ConstantValue.NotAvailable,
                                                                                        hostObjectField.Type
                                                                                        ),
                                                            hostObjectField.Type
                                                            )
                {
                    WasCompilerGenerated = true
                };
            }

            result[i++] = new BoundExpressionStatement(syntax, setSubmission)
            {
                WasCompilerGenerated = true
            };

            foreach (var field in synthesizedFields.FieldSymbols)
            {
                var targetScriptClass  = (ImplicitNamedTypeSymbol)field.Type;
                var targetSubmissionId = targetScriptClass.DeclaringCompilation.GetSubmissionSlotIndex();
                Debug.Assert(targetSubmissionId >= 0);

                // this.<field> = (<FieldType>)SessionHelpers.GetSubmission(<session>, <i>);
                result[i++] =
                    new BoundExpressionStatement(syntax,
                                                 new BoundAssignmentOperator(syntax,
                                                                             new BoundFieldAccess(syntax, thisReference, field, ConstantValue.NotAvailable)
                {
                    WasCompilerGenerated = true
                },
                                                                             BoundConversion.Synthesized(syntax,
                                                                                                         BoundCall.Synthesized(syntax,
                                                                                                                               null,
                                                                                                                               submissionGetter,
                                                                                                                               sessionReference,
                                                                                                                               new BoundLiteral(syntax, ConstantValue.Create(targetSubmissionId), intType)
                {
                    WasCompilerGenerated = true
                }),
                                                                                                         Conversion.ExplicitReference,
                                                                                                         false,
                                                                                                         true,
                                                                                                         ConstantValue.NotAvailable,
                                                                                                         targetScriptClass
                                                                                                         ),
                                                                             targetScriptClass
                                                                             )
                {
                    WasCompilerGenerated = true
                })
                {
                    WasCompilerGenerated = true
                };
            }

            Debug.Assert(i == result.Length);

            return(result.AsImmutableOrNull());
        }
        private BoundExpression RewriteDecimalConversion(BoundConversion oldNode, CSharpSyntaxNode syntax, BoundExpression operand, TypeSymbol fromType, TypeSymbol toType)
        {
            Debug.Assert(fromType.SpecialType == SpecialType.System_Decimal || toType.SpecialType == SpecialType.System_Decimal);

            // call the method
            SpecialMember member = DecimalConversionMethod(fromType, toType);
            var method = (MethodSymbol)this.compilation.Assembly.GetSpecialTypeMember(member);
            Debug.Assert((object)method != null); // Should have been checked during Warnings pass

            if (inExpressionLambda && oldNode != null)
            {
                ConversionKind conversionKind = oldNode.ConversionKind.IsImplicitConversion() ? ConversionKind.ImplicitUserDefined : ConversionKind.ExplicitUserDefined;
                return oldNode.Update(
                    operand,
                    conversionKind,
                    oldNode.ResultKind,
                    isBaseConversion: oldNode.IsBaseConversion,
                    symbolOpt: method,
                    @checked: oldNode.Checked,
                    explicitCastInCode: oldNode.ExplicitCastInCode,
                    isExtensionMethod: oldNode.IsExtensionMethod,
                    isArrayIndex: oldNode.IsArrayIndex,
                    constantValueOpt: oldNode.ConstantValueOpt,
                    type: toType);
            }
            else
            {
                Debug.Assert(method.ReturnType == toType);
                return BoundCall.Synthesized(syntax, null, method, operand);
            }
        }
Beispiel #23
0
        private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics)
        {
            BoundExpression collectionExpr = this.Next.BindValue(syntax.Expression, diagnostics, BindValueKind.RValue); //bind with next to avoid seeing iteration variable

            ForEachEnumeratorInfo.Builder builder = new ForEachEnumeratorInfo.Builder();
            TypeSymbol inferredType;
            bool       hasErrors = !GetEnumeratorInfoAndInferCollectionElementType(ref builder, ref collectionExpr, diagnostics, out inferredType);

            // These should only occur when special types are missing or malformed.
            hasErrors = hasErrors ||
                        (object)builder.GetEnumeratorMethod == null ||
                        (object)builder.MoveNextMethod == null ||
                        (object)builder.CurrentPropertyGetter == null;

            // Check for local variable conflicts in the *enclosing* binder; obviously the *current*
            // binder has a local that matches!
            hasErrors |= this.ValidateDeclarationNameConflictsInScope(IterationVariable, diagnostics);

            // If the type in syntax is "var", then the type should be set explicitly so that the
            // Type property doesn't fail.

            TypeSyntax typeSyntax = this.syntax.Type;

            bool        isVar;
            AliasSymbol alias;
            TypeSymbol  declType = BindType(typeSyntax, diagnostics, out isVar, out alias);

            TypeSymbol iterationVariableType;

            if (isVar)
            {
                iterationVariableType = inferredType ?? CreateErrorType("var");
            }
            else
            {
                Debug.Assert((object)declType != null);
                iterationVariableType = declType;
            }


            BoundTypeExpression boundIterationVariableType = new BoundTypeExpression(typeSyntax, alias, iterationVariableType);

            this.IterationVariable.SetTypeSymbol(iterationVariableType);

            BoundStatement body = BindPossibleEmbeddedStatement(syntax.Statement, diagnostics);

            hasErrors = hasErrors || iterationVariableType.IsErrorType();

            // Skip the conversion checks and array/enumerator differentiation if we know we have an error.
            if (hasErrors)
            {
                return(new BoundForEachStatement(
                           syntax,
                           ImmutableArray <LocalSymbol> .Empty,
                           null, // can't be sure that it's complete
                           default(Conversion),
                           boundIterationVariableType,
                           this.IterationVariable,
                           collectionExpr,
                           body,
                           CheckOverflowAtRuntime,
                           this.BreakLabel,
                           this.ContinueLabel,
                           hasErrors));
            }

            var foreachKeyword = syntax.ForEachKeyword;

            ReportDiagnosticsIfObsolete(diagnostics, builder.GetEnumeratorMethod, foreachKeyword, hasBaseReceiver: false);
            ReportDiagnosticsIfObsolete(diagnostics, builder.MoveNextMethod, foreachKeyword, hasBaseReceiver: false);
            ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter, foreachKeyword, hasBaseReceiver: false);
            ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter.AssociatedSymbol, foreachKeyword, hasBaseReceiver: false);

            // We want to convert from inferredType in the array/string case and builder.ElementType in the enumerator case,
            // but it turns out that these are equivalent (when both are available).

            HashSet <DiagnosticInfo> useSiteDiagnostics = null;
            Conversion elementConversion = this.Conversions.ClassifyConversionForCast(inferredType, iterationVariableType, ref useSiteDiagnostics);

            if (!elementConversion.IsValid)
            {
                ImmutableArray <MethodSymbol> originalUserDefinedConversions = elementConversion.OriginalUserDefinedConversions;
                if (originalUserDefinedConversions.Length > 1)
                {
                    diagnostics.Add(ErrorCode.ERR_AmbigUDConv, syntax.ForEachKeyword.GetLocation(), originalUserDefinedConversions[0], originalUserDefinedConversions[1], inferredType, iterationVariableType);
                }
                else
                {
                    SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, inferredType, iterationVariableType);
                    diagnostics.Add(ErrorCode.ERR_NoExplicitConv, syntax.ForEachKeyword.GetLocation(), distinguisher.First, distinguisher.Second);
                }
                hasErrors = true;
            }
            else
            {
                ReportDiagnosticsIfObsolete(diagnostics, elementConversion, syntax.ForEachKeyword, hasBaseReceiver: false);
            }

            // Spec (§8.8.4):
            // If the type X of expression is dynamic then there is an implicit conversion from >>expression<< (not the type of the expression)
            // to the System.Collections.IEnumerable interface (§6.1.8).
            builder.CollectionConversion = this.Conversions.ClassifyConversionFromExpression(collectionExpr, builder.CollectionType, ref useSiteDiagnostics);
            builder.CurrentConversion    = this.Conversions.ClassifyConversion(builder.CurrentPropertyGetter.ReturnType, builder.ElementType, ref useSiteDiagnostics);

            builder.EnumeratorConversion = this.Conversions.ClassifyConversion(builder.GetEnumeratorMethod.ReturnType, GetSpecialType(SpecialType.System_Object, diagnostics, this.syntax), ref useSiteDiagnostics);

            diagnostics.Add(syntax.ForEachKeyword.GetLocation(), useSiteDiagnostics);

            // Due to the way we extracted the various types, these conversions should always be possible.
            // CAVEAT: if we're iterating over an array of pointers, the current conversion will fail since we
            // can't convert from object to a pointer type.  Similarly, if we're iterating over an array of
            // Nullable<Error>, the current conversion will fail because we don't know if an ErrorType is a
            // value type.  This doesn't matter in practice, since we won't actually use the enumerator pattern
            // when we lower the loop.
            Debug.Assert(builder.CollectionConversion.IsValid);
            Debug.Assert(builder.CurrentConversion.IsValid ||
                         (builder.ElementType.IsPointerType() && collectionExpr.Type.IsArray()) ||
                         (builder.ElementType.IsNullableType() && builder.ElementType.GetMemberTypeArgumentsNoUseSiteDiagnostics().Single().IsErrorType() && collectionExpr.Type.IsArray()));
            Debug.Assert(builder.EnumeratorConversion.IsValid ||
                         this.Compilation.GetSpecialType(SpecialType.System_Object).TypeKind == TypeKind.Error,
                         "Conversions to object succeed unless there's a problem with the object type");

            // If user-defined conversions could occur here, we would need to check for ObsoleteAttribute.
            Debug.Assert((object)builder.CollectionConversion.Method == null,
                         "Conversion from collection expression to collection type should not be user-defined");
            Debug.Assert((object)builder.CurrentConversion.Method == null,
                         "Conversion from Current property type to element type should not be user-defined");
            Debug.Assert((object)builder.EnumeratorConversion.Method == null,
                         "Conversion from GetEnumerator return type to System.Object should not be user-defined");

            // We're wrapping the collection expression in a (non-synthesized) conversion so that its converted
            // type (i.e. builder.CollectionType) will be available in the binding API.
            BoundConversion convertedCollectionExpression = new BoundConversion(
                collectionExpr.Syntax,
                collectionExpr,
                builder.CollectionConversion,
                CheckOverflowAtRuntime,
                false,
                ConstantValue.NotAvailable,
                builder.CollectionType);

            return(new BoundForEachStatement(
                       syntax,
                       ImmutableArray <LocalSymbol> .Empty,
                       builder.Build(this.Flags),
                       elementConversion,
                       boundIterationVariableType,
                       this.IterationVariable,
                       convertedCollectionExpression,
                       body,
                       CheckOverflowAtRuntime,
                       this.BreakLabel,
                       this.ContinueLabel,
                       hasErrors));
        }
Beispiel #24
0
        /// <summary>
        /// Generates a submission initialization part of a Script type constructor that represents an interactive submission.
        /// </summary>
        /// <remarks>
        /// The constructor takes a parameter of type Roslyn.Scripting.Session - the session reference.
        /// It adds the object being constructed into the session by calling Microsoft.CSharp.RuntimeHelpers.SessionHelpers.SetSubmission,
        /// and retrieves strongly typed references on all previous submission script classes whose members are referenced by this submission.
        /// The references are stored to fields of the submission (<paramref name="synthesizedFields"/>).
        /// </remarks>
        private static ImmutableArray <BoundStatement> MakeSubmissionInitialization(CSharpSyntaxNode syntax, MethodSymbol submissionConstructor, SynthesizedSubmissionFields synthesizedFields, CSharpCompilation compilation)
        {
            Debug.Assert(submissionConstructor.ParameterCount == 2);

            var statements = new List <BoundStatement>(2 + synthesizedFields.Count);

            var submissionArrayReference = new BoundParameter(syntax, submissionConstructor.Parameters[0])
            {
                WasCompilerGenerated = true
            };

            var intType       = compilation.GetSpecialType(SpecialType.System_Int32);
            var objectType    = compilation.GetSpecialType(SpecialType.System_Object);
            var thisReference = new BoundThisReference(syntax, submissionConstructor.ContainingType)
            {
                WasCompilerGenerated = true
            };

            var slotIndex = compilation.GetSubmissionSlotIndex();

            Debug.Assert(slotIndex >= 0);

            // <submission_array>[<slot_index] = this;
            statements.Add(new BoundExpressionStatement(syntax,
                                                        new BoundAssignmentOperator(syntax,
                                                                                    new BoundArrayAccess(syntax,
                                                                                                         submissionArrayReference,
                                                                                                         ImmutableArray.Create <BoundExpression>(new BoundLiteral(syntax, ConstantValue.Create(slotIndex), intType)
            {
                WasCompilerGenerated = true
            }),
                                                                                                         objectType)
            {
                WasCompilerGenerated = true
            },
                                                                                    thisReference,
                                                                                    RefKind.None,
                                                                                    thisReference.Type)
            {
                WasCompilerGenerated = true
            })
            {
                WasCompilerGenerated = true
            });

            var hostObjectField = synthesizedFields.GetHostObjectField();

            if ((object)hostObjectField != null)
            {
                // <host_object> = (<host_object_type>)<submission_array>[0]
                statements.Add(
                    new BoundExpressionStatement(syntax,
                                                 new BoundAssignmentOperator(syntax,
                                                                             new BoundFieldAccess(syntax, thisReference, hostObjectField, ConstantValue.NotAvailable)
                {
                    WasCompilerGenerated = true
                },
                                                                             BoundConversion.Synthesized(syntax,
                                                                                                         new BoundArrayAccess(syntax,
                                                                                                                              submissionArrayReference,
                                                                                                                              ImmutableArray.Create <BoundExpression>(new BoundLiteral(syntax, ConstantValue.Create(0), intType)
                {
                    WasCompilerGenerated = true
                }),
                                                                                                                              objectType),
                                                                                                         Conversion.ExplicitReference,
                                                                                                         false,
                                                                                                         true,
                                                                                                         ConstantValue.NotAvailable,
                                                                                                         hostObjectField.Type
                                                                                                         ),
                                                                             hostObjectField.Type)
                {
                    WasCompilerGenerated = true
                })
                {
                    WasCompilerGenerated = true
                });
            }

            foreach (var field in synthesizedFields.FieldSymbols)
            {
                var targetScriptType      = (ImplicitNamedTypeSymbol)field.Type;
                var targetSubmissionIndex = targetScriptType.DeclaringCompilation.GetSubmissionSlotIndex();
                Debug.Assert(targetSubmissionIndex >= 0);

                // this.<field> = (<target_script_type>)<submission_array>[<target_submission_index>];
                statements.Add(
                    new BoundExpressionStatement(syntax,
                                                 new BoundAssignmentOperator(syntax,
                                                                             new BoundFieldAccess(syntax, thisReference, field, ConstantValue.NotAvailable)
                {
                    WasCompilerGenerated = true
                },
                                                                             BoundConversion.Synthesized(syntax,
                                                                                                         new BoundArrayAccess(syntax,
                                                                                                                              submissionArrayReference,
                                                                                                                              ImmutableArray.Create <BoundExpression>(new BoundLiteral(syntax, ConstantValue.Create(targetSubmissionIndex), intType)
                {
                    WasCompilerGenerated = true
                }),
                                                                                                                              objectType)
                {
                    WasCompilerGenerated = true
                },
                                                                                                         Conversion.ExplicitReference,
                                                                                                         false,
                                                                                                         true,
                                                                                                         ConstantValue.NotAvailable,
                                                                                                         targetScriptType
                                                                                                         ),
                                                                             targetScriptType
                                                                             )
                {
                    WasCompilerGenerated = true
                })
                {
                    WasCompilerGenerated = true
                });
            }

            return(statements.AsImmutableOrNull());
        }
Beispiel #25
0
        private BoundExpression CreateTupleLiteralConversion(SyntaxNode syntax, BoundTupleLiteral sourceTuple, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics)
        {
            // We have a successful tuple conversion; rather than producing a separate conversion node 
            // which is a conversion on top of a tuple literal, tuple conversion is an element-wise conversion of arguments.
            Debug.Assert((conversion.Kind == ConversionKind.ImplicitNullable) == destination.IsNullableType());

            var destinationWithoutNullable = destination;
            var conversionWithoutNullable = conversion;

            if (conversion.Kind == ConversionKind.ImplicitNullable)
            {
                destinationWithoutNullable = destination.GetNullableUnderlyingType();
                conversionWithoutNullable = conversion.UnderlyingConversions[0];
            }

            Debug.Assert(conversionWithoutNullable.IsTupleLiteralConversion);

            NamedTypeSymbol targetType = (NamedTypeSymbol)destinationWithoutNullable;
            if (targetType.IsTupleType)
            {
                var destTupleType = (TupleTypeSymbol)targetType;
                // do not lose the original element names in the literal if different from names in the target

                TupleTypeSymbol.ReportNamesMismatchesIfAny(targetType, sourceTuple, diagnostics);

                // Come back to this, what about locations? (https://github.com/dotnet/roslyn/issues/11013)
                targetType = destTupleType.WithElementNames(sourceTuple.ArgumentNamesOpt);
            }

            var arguments = sourceTuple.Arguments;
            var convertedArguments = ArrayBuilder<BoundExpression>.GetInstance(arguments.Length);

            ImmutableArray<TypeSymbol> targetElementTypes = targetType.GetElementTypesOfTupleOrCompatible();
            Debug.Assert(targetElementTypes.Length == arguments.Length, "converting a tuple literal to incompatible type?");
            var underlyingConversions = conversionWithoutNullable.UnderlyingConversions;

            for (int i = 0; i < arguments.Length; i++)
            {
                var argument = arguments[i];
                var destType = targetElementTypes[i];
                var elementConversion = underlyingConversions[i];

                convertedArguments.Add(CreateConversion(argument.Syntax, argument, elementConversion, isCast, destType, diagnostics));
            }

            BoundExpression result = new BoundConvertedTupleLiteral(
                sourceTuple.Syntax,
                sourceTuple.Type,
                convertedArguments.ToImmutableAndFree(),
                targetType);

            if (sourceTuple.Type != destination)
            {
                // literal cast is applied to the literal 
                result = new BoundConversion(
                    sourceTuple.Syntax,
                    result,
                    conversion,
                    @checked: false,
                    explicitCastInCode: isCast,
                    constantValueOpt: ConstantValue.NotAvailable,
                    type: destination);
            }

            // If we had a cast in the code, keep conversion in the tree.
            // even though the literal is already converted to the target type.
            if (isCast)
            {
                result = new BoundConversion(
                    syntax,
                    result,
                    Conversion.Identity,
                    @checked: false,
                    explicitCastInCode: isCast,
                    constantValueOpt: ConstantValue.NotAvailable,
                    type: destination);
            }

            return result;
        }
Beispiel #26
0
        private BoundExpression RewriteIntPtrConversion(
            BoundConversion oldNode,
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenOperand,
            ConversionKind conversionKind,
            MethodSymbol symbolOpt,
            bool @checked,
            bool explicitCastInCode,
            bool isExtensionMethod,
            bool isArrayIndex,
            ConstantValue constantValueOpt,
            TypeSymbol rewrittenType)
        {
            Debug.Assert(rewrittenOperand != null);
            Debug.Assert((object)rewrittenType != null);

            TypeSymbol source = rewrittenOperand.Type;
            TypeSymbol target = rewrittenType;

            SpecialMember member = GetIntPtrConversionMethod(source: rewrittenOperand.Type, target: rewrittenType);
            MethodSymbol method = GetSpecialTypeMethod(syntax, member);
            Debug.Assert(!method.ReturnsVoid);
            Debug.Assert(method.ParameterCount == 1);

            if (source.IsNullableType() && target.IsNullableType())
            {
                Debug.Assert(target.IsNullableType());
                return RewriteLiftedUserDefinedConversion(syntax, rewrittenOperand, method, rewrittenType, ConversionKind.IntPtr);
            }
            else if (source.IsNullableType())
            {
                rewrittenOperand = MakeConversion(rewrittenOperand, source.StrippedType(), @checked);
            }

            rewrittenOperand = MakeConversion(rewrittenOperand, method.ParameterTypes[0], @checked);

            var returnType = method.ReturnType;
            Debug.Assert((object)returnType != null);

            if (_inExpressionLambda)
            {
                return BoundConversion.Synthesized(syntax, rewrittenOperand, new Conversion(conversionKind, method, false), @checked, explicitCastInCode, constantValueOpt, rewrittenType);
            }

            var rewrittenCall = MakeCall(
                    syntax: syntax,
                    rewrittenReceiver: null,
                    method: method,
                    rewrittenArguments: ImmutableArray.Create(rewrittenOperand),
                    type: returnType);

            return MakeConversion(rewrittenCall, rewrittenType, @checked);
        }
        private void CheckCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
        {
            BoundExpression left = node.Left;

            if (!node.Operator.Kind.IsDynamic() && !node.LeftConversion.IsIdentity && node.LeftConversion.Exists)
            {
                // Need to represent the implicit conversion as a node in order to be able to produce correct diagnostics.
                left = new BoundConversion(left.Syntax, left, node.LeftConversion, node.Operator.Kind.IsChecked(),
                                           explicitCastInCode: false, constantValueOpt: null, type: node.Operator.LeftType);
            }

            CheckForBitwiseOrSignExtend(node, node.Operator.Kind, left, node.Right);
            CheckLiftedCompoundAssignment(node);

            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsAssignment, node);
            }
        }
        private BoundExpression VisitConversion(BoundConversion node)
        {
            switch (node.ConversionKind)
            {
            case ConversionKind.MethodGroup:
            {
                var mg = (BoundMethodGroup)node.Operand;
                return(DelegateCreation(mg.ReceiverOpt, node.SymbolOpt, node.Type, node.SymbolOpt.IsStatic && !node.IsExtensionMethod));
            }

            case ConversionKind.ExplicitUserDefined:
            case ConversionKind.ImplicitUserDefined:
            case ConversionKind.IntPtr:
            {
                var  method                = node.SymbolOpt;
                var  operandType           = node.Operand.Type;
                var  strippedOperandType   = operandType.StrippedType();
                var  conversionInputType   = method.Parameters[0].Type;
                var  isLifted              = operandType != conversionInputType && strippedOperandType == conversionInputType;
                bool requireAdditionalCast =
                    strippedOperandType != ((node.ConversionKind == ConversionKind.ExplicitUserDefined) ? conversionInputType : conversionInputType.StrippedType());
                var resultType = (isLifted && method.ReturnType.IsNonNullableValueType() && node.Type.IsNullableType()) ? _nullableType.Construct(method.ReturnType) : method.ReturnType;
                var e1         = requireAdditionalCast
                            ? Convert(Visit(node.Operand), node.Operand.Type, method.Parameters[0].Type, node.Checked, false)
                            : Visit(node.Operand);
                var e2 = ExprFactory("Convert", e1, _bound.Typeof(resultType), _bound.MethodInfo(method));
                return(Convert(e2, resultType, node.Type, node.Checked, false));
            }

            case ConversionKind.ImplicitReference:
            case ConversionKind.Identity:
            {
                var operand = Visit(node.Operand);
                return(node.ExplicitCastInCode ? Convert(operand, node.Type, false) : operand);
            }

            case ConversionKind.IdentityValue:
            {
                return(Visit(node.Operand));
            }

            case ConversionKind.ImplicitNullable:
                if (node.Operand.Type.IsNullableType())
                {
                    return(Convert(Visit(node.Operand), node.Operand.Type, node.Type, node.Checked, node.ExplicitCastInCode));
                }
                else
                {
                    // the native compiler performs this conversion in two steps, so we follow suit
                    var nullable     = (NamedTypeSymbol)node.Type;
                    var intermediate = nullable.TypeArgumentsNoUseSiteDiagnostics[0];
                    var e1           = Convert(Visit(node.Operand), node.Operand.Type, intermediate, node.Checked, false);
                    return(Convert(e1, intermediate, node.Type, node.Checked, false));
                }

            case ConversionKind.NullLiteral:
                return(Convert(Constant(_bound.Null(_objectType)), _objectType, node.Type, false, node.ExplicitCastInCode));

            default:
                return(Convert(Visit(node.Operand), node.Operand.Type, node.Type, node.Checked, node.ExplicitCastInCode));
            }
        }
 public override BoundNode VisitConversion(BoundConversion node)
 {
     if (node.ConversionKind == ConversionKind.MethodGroup)
     {
         if (node.IsExtensionMethod || ((object)node.SymbolOpt != null && !node.SymbolOpt.IsStatic))
         {
             return VisitSyntaxWithReceiver(node.Syntax, ((BoundMethodGroup)node.Operand).ReceiverOpt);
         }
         return null;
     }
     else
     {
         return base.VisitConversion(node);
     }
 }
        /// <summary>
        /// Returns an expression which converts the given expression into a string (or null).
        /// If necessary, this invokes .ToString() on the expression, to avoid boxing value types.
        /// </summary>
        private BoundExpression ConvertConcatExprToString(SyntaxNode syntax, BoundExpression expr)
        {
            // If it's a value type, it'll have been boxed by the +(string, object) or +(object, string)
            // operator. Undo that.
            if (expr.Kind == BoundKind.Conversion)
            {
                BoundConversion conv = (BoundConversion)expr;
                if (conv.ConversionKind == ConversionKind.Boxing)
                {
                    expr = conv.Operand;
                }
            }

            // 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.
            // Similarly if it's a literal null, don't do anything special.
            if (expr.Kind == BoundKind.Literal)
            {
                ConstantValue cv = ((BoundLiteral)expr).ConstantValue;
                if (cv != null)
                {
                    if (cv.SpecialType == SpecialType.System_Char)
                    {
                        return(_factory.StringLiteral(cv.CharValue.ToString()));
                    }
                    else if (cv.IsNull)
                    {
                        return(expr);
                    }
                }
            }

            // If it's a string already, just return it
            if (expr.Type.IsStringType())
            {
                return(expr);
            }

            // Evaluate toString at the last possible moment, to avoid spurious diagnostics if it's missing.
            // All code paths below here use it.
            var objectToStringMethod = UnsafeGetSpecialTypeMethod(syntax, SpecialMember.System_Object__ToString);

            // If it's a struct which has overridden ToString, find that method. Note that we might fail to
            // find it, e.g. if object.ToString is missing
            MethodSymbol structToStringMethod = null;

            if (expr.Type.IsValueType && !expr.Type.IsTypeParameter())
            {
                var type = (NamedTypeSymbol)expr.Type;
                var typeToStringMembers = type.GetMembers(objectToStringMethod.Name);
                foreach (var member in typeToStringMembers)
                {
                    if (member is MethodSymbol toStringMethod &&
                        toStringMethod.GetLeastOverriddenMethod(type) == (object)objectToStringMethod)
                    {
                        structToStringMethod = toStringMethod;
                        break;
                    }
                }
            }

            // If it's a special value type (and not a field of a MarshalByRef object), it should have its own ToString method (but we might fail to find
            // it if object.ToString is missing). Assume that this won't be removed, and emit a direct call rather
            // than a constrained virtual call. This keeps in the spirit of #7079, but expands the range of
            // types to all special value types.
            if (structToStringMethod != null && (expr.Type.SpecialType != SpecialType.None && !isFieldOfMarshalByRef(expr, _compilation)))
            {
                return(BoundCall.Synthesized(expr.Syntax, expr, structToStringMethod));
            }

            // - It's a reference type (excluding unconstrained generics): no copy
            // - It's a constant: no copy
            // - The type definitely doesn't have its own ToString method (i.e. we're definitely calling
            //   object.ToString on a struct type, not type parameter): no copy (yes this is a versioning issue,
            //   but that doesn't matter)
            // - We're calling the type's own ToString method, and it's effectively readonly (the method or the whole
            //   type is readonly): no copy
            // - Otherwise: copy
            // This is to mimic the old behaviour, where value types would be boxed before ToString was called on them,
            // but with optimizations for readonly methods.
            bool callWithoutCopy = expr.Type.IsReferenceType ||
                                   expr.ConstantValue != null ||
                                   (structToStringMethod == null && !expr.Type.IsTypeParameter()) ||
                                   structToStringMethod?.IsEffectivelyReadOnly == true;

            // No need for a conditional access if it's a value type - we know it's not null.
            if (expr.Type.IsValueType)
            {
                if (!callWithoutCopy)
                {
                    expr = new BoundPassByCopy(expr.Syntax, expr, expr.Type);
                }
                return(BoundCall.Synthesized(expr.Syntax, expr, objectToStringMethod));
            }

            if (callWithoutCopy)
            {
                return(makeConditionalAccess(expr));
            }
            else
            {
                // If we do conditional access on a copy, we need a proper BoundLocal rather than a
                // BoundPassByCopy (as it's accessed multiple times). If we don't do this, and the
                // receiver is an unconstrained generic parameter, BoundLoweredConditionalAccess has
                // to generate a lot of code to ensure it only accesses the copy once (which is pointless).
                var temp = _factory.StoreToTemp(expr, out var store);
                return(_factory.Sequence(
                           ImmutableArray.Create(temp.LocalSymbol),
                           ImmutableArray.Create <BoundExpression>(store),
                           makeConditionalAccess(temp)));
            }

            BoundExpression makeConditionalAccess(BoundExpression receiver)
            {
                int currentConditionalAccessID = ++_currentConditionalAccessID;

                return(new BoundLoweredConditionalAccess(
                           syntax,
                           receiver,
                           hasValueMethodOpt: null,
                           whenNotNull: BoundCall.Synthesized(
                               syntax,
                               new BoundConditionalReceiver(syntax, currentConditionalAccessID, expr.Type),
                               objectToStringMethod),
                           whenNullOpt: null,
                           id: currentConditionalAccessID,
                           type: _compilation.GetSpecialType(SpecialType.System_String)));
            }
        protected BoundExpression CreateUserDefinedConversion(CSharpSyntaxNode syntax, BoundExpression source, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics)
        {
            if (!conversion.IsValid)
            {
                GenerateImplicitConversionError(diagnostics, syntax, conversion, source, destination);

                return new BoundConversion(
                    syntax,
                    source,
                    conversion,
                    CheckOverflowAtRuntime,
                    explicitCastInCode: isCast,
                    constantValueOpt: ConstantValue.NotAvailable,
                    type: destination,
                    hasErrors: true)
                { WasCompilerGenerated = source.WasCompilerGenerated };
            }

            // Due to an oddity in the way we create a non-lifted user-defined conversion from A to D? 
            // (required backwards compatibility with the native compiler) we can end up in a situation 
            // where we have:
            // a standard conversion from A to B?
            // then a standard conversion from B? to B
            // then a user-defined  conversion from B to C
            // then a standard conversion from C to C? 
            // then a standard conversion from C? to D?
            //
            // In that scenario, the "from type" of the conversion will be B? and the "from conversion" will be 
            // from A to B?. Similarly the "to type" of the conversion will be C? and the "to conversion"
            // of the conversion will be from C? to D?.
            //
            // Therefore, we might need to introduce an extra conversion on the source side, from B? to B.
            // Now, you might think we should also introduce an extra conversion on the destination side,
            // from C to C?. But that then gives us the following bad situation: If we in fact bind this as
            //
            // (D?)(C?)(C)(B)(B?)(A)x 
            //
            // then what we are in effect doing is saying "convert C? to D? by checking for null, unwrapping,
            // converting C to D, and then wrapping". But we know that the C? will never be null. In this case
            // we should actually generate
            //
            // (D?)(C)(B)(B?)(A)x
            //
            // And thereby skip the unnecessary nullable conversion.

            // Original expression --> conversion's "from" type
            BoundExpression convertedOperand = CreateConversion(
                syntax: source.Syntax,
                source: source,
                conversion: conversion.UserDefinedFromConversion,
                isCast: false,
                wasCompilerGenerated: true,
                destination: conversion.BestUserDefinedConversionAnalysis.FromType,
                diagnostics: diagnostics);

            TypeSymbol conversionParameterType = conversion.BestUserDefinedConversionAnalysis.Operator.ParameterTypes[0];
            HashSet<DiagnosticInfo> useSiteDiagnostics = null;

            if (conversion.BestUserDefinedConversionAnalysis.Kind == UserDefinedConversionAnalysisKind.ApplicableInNormalForm &&
                conversion.BestUserDefinedConversionAnalysis.FromType != conversionParameterType)
            {
                // Conversion's "from" type --> conversion method's parameter type.
                convertedOperand = CreateConversion(
                    syntax: syntax,
                    source: convertedOperand,
                    conversion: Conversions.ClassifyStandardConversion(null, convertedOperand.Type, conversionParameterType, ref useSiteDiagnostics),
                    isCast: false,
                    wasCompilerGenerated: true,
                    destination: conversionParameterType,
                    diagnostics: diagnostics);
            }

            BoundExpression userDefinedConversion;

            TypeSymbol conversionReturnType = conversion.BestUserDefinedConversionAnalysis.Operator.ReturnType;
            TypeSymbol conversionToType = conversion.BestUserDefinedConversionAnalysis.ToType;
            Conversion toConversion = conversion.UserDefinedToConversion;

            if (conversion.BestUserDefinedConversionAnalysis.Kind == UserDefinedConversionAnalysisKind.ApplicableInNormalForm &&
                conversionToType != conversionReturnType)
            {
                // Conversion method's parameter type --> conversion method's return type
                // NB: not calling CreateConversion here because this is the recursive base case.
                userDefinedConversion = new BoundConversion(
                    syntax,
                    convertedOperand,
                    conversion,
                    @checked: false, // There are no checked user-defined conversions, but the conversions on either side might be checked.
                    explicitCastInCode: isCast,
                    constantValueOpt: ConstantValue.NotAvailable,
                    type: conversionReturnType)
                { WasCompilerGenerated = true };

                if (conversionToType.IsNullableType() && conversionToType.GetNullableUnderlyingType() == conversionReturnType)
                {
                    // Skip introducing the conversion from C to C?.  The "to" conversion is now wrong though,
                    // because it will still assume converting C? to D?. 

                    toConversion = Conversions.ClassifyConversion(conversionReturnType, destination, ref useSiteDiagnostics);
                    Debug.Assert(toConversion.Exists);
                }
                else
                {
                    // Conversion method's return type --> conversion's "to" type
                    userDefinedConversion = CreateConversion(
                        syntax: syntax,
                        source: userDefinedConversion,
                        conversion: Conversions.ClassifyStandardConversion(null, conversionReturnType, conversion.BestUserDefinedConversionAnalysis.ToType, ref useSiteDiagnostics),
                        isCast: false,
                        wasCompilerGenerated: true,
                        destination: conversion.BestUserDefinedConversionAnalysis.ToType,
                        diagnostics: diagnostics);
                }
            }
            else
            {
                // Conversion method's parameter type --> conversion method's "to" type
                // NB: not calling CreateConversion here because this is the recursive base case.
                userDefinedConversion = new BoundConversion(
                    syntax,
                    convertedOperand,
                    conversion,
                    @checked: false,
                    explicitCastInCode: isCast,
                    constantValueOpt: ConstantValue.NotAvailable,
                    type: conversion.BestUserDefinedConversionAnalysis.ToType)
                { WasCompilerGenerated = true };
            }

            diagnostics.Add(syntax, useSiteDiagnostics);

            // Conversion's "to" type --> final type
            BoundExpression finalConversion = CreateConversion(
                syntax: syntax,
                source: userDefinedConversion,
                conversion: toConversion,
                isCast: false,
                wasCompilerGenerated: true, // NOTE: doesn't necessarily set flag on resulting bound expression.
                destination: destination,
                diagnostics: diagnostics);

            finalConversion.ResetCompilerGenerated(source.WasCompilerGenerated);

            return finalConversion;
        }
        private BoundExpression RewriteLiftedConversionInExpressionTree(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenOperand,
            ConversionKind conversionKind,
            bool @checked,
            bool explicitCastInCode,
            TypeSymbol rewrittenType)
        {
            Debug.Assert((object)rewrittenType != null);
            TypeSymbol rewrittenOperandType = rewrittenOperand.Type;
            Debug.Assert(rewrittenType.IsNullableType() || rewrittenOperandType.IsNullableType());

            TypeSymbol typeFrom = rewrittenOperandType.StrippedType();
            TypeSymbol typeTo = rewrittenType.StrippedType();
            if (typeFrom != typeTo && (typeFrom.SpecialType == SpecialType.System_Decimal || typeTo.SpecialType == SpecialType.System_Decimal))
            {
                // take special care if the underlying conversion is a decimal conversion
                TypeSymbol typeFromUnderlying = typeFrom;
                TypeSymbol typeToUnderlying = typeTo;

                // They can't both be enums, since one of them is decimal.
                if (typeFrom.IsEnumType())
                {
                    typeFromUnderlying = typeFrom.GetEnumUnderlyingType();

                    // NOTE: Dev10 converts enum? to underlying?, rather than directly to underlying.
                    rewrittenOperandType = rewrittenOperandType.IsNullableType() ? ((NamedTypeSymbol)rewrittenOperandType.OriginalDefinition).Construct(typeFromUnderlying) : typeFromUnderlying;
                    rewrittenOperand = BoundConversion.SynthesizedNonUserDefined(syntax, rewrittenOperand, ConversionKind.ImplicitEnumeration, rewrittenOperandType);
                }
                else if (typeTo.IsEnumType())
                {
                    typeToUnderlying = typeTo.GetEnumUnderlyingType();
                }

                var method = (MethodSymbol)this.compilation.Assembly.GetSpecialTypeMember(DecimalConversionMethod(typeFromUnderlying, typeToUnderlying));
                conversionKind = conversionKind.IsImplicitConversion() ? ConversionKind.ImplicitUserDefined : ConversionKind.ExplicitUserDefined;
                var result = new BoundConversion(syntax, rewrittenOperand, new Conversion(conversionKind, method, false), @checked, explicitCastInCode, default(ConstantValue), rewrittenType);
                return result;
            }
            else
            {
                return new BoundConversion(syntax, rewrittenOperand, new Conversion(conversionKind), @checked, explicitCastInCode, default(ConstantValue), rewrittenType);
            }
        }
Beispiel #33
0
 public override BoundNode VisitConversion(BoundConversion node)
 {
     BoundSpillSequence2 ss = null;
     var operand = VisitExpression(ref ss, node.Operand);
     return UpdateExpression(
         ss,
         node.Update(
             operand,
             node.ConversionKind,
             node.ResultKind,
             isBaseConversion: node.IsBaseConversion,
             symbolOpt: node.SymbolOpt,
             @checked: node.Checked,
             explicitCastInCode: node.ExplicitCastInCode,
             isExtensionMethod: node.IsExtensionMethod,
             isArrayIndex: node.IsArrayIndex,
             constantValueOpt: node.ConstantValueOpt,
             type: node.Type));
 }
        /// <summary>
        /// Helper method to generate a lowered conversion.
        /// </summary>
        private BoundExpression MakeConversion(
            BoundConversion oldNode,
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenOperand,
            ConversionKind conversionKind,
            MethodSymbol symbolOpt,
            bool @checked,
            bool explicitCastInCode,
            bool isExtensionMethod,
            bool isArrayIndex,
            ConstantValue constantValueOpt,
            TypeSymbol rewrittenType)
        {
            Debug.Assert(oldNode == null || oldNode.Syntax == syntax);
            Debug.Assert((object)rewrittenType != null);
            @checked = @checked &&
                (inExpressionLambda && (explicitCastInCode || DistinctSpecialTypes(rewrittenOperand.Type, rewrittenType)) ||
                NeedsChecked(rewrittenOperand.Type, rewrittenType));

            switch (conversionKind)
            {
                case ConversionKind.Identity:

                    // Spec 6.1.1:
                    //   An identity conversion converts from any type to the same type. 
                    //   This conversion exists such that an entity that already has a required type can be said to be convertible to that type.
                    //   Because object and dynamic are considered equivalent there is an identity conversion between object and dynamic, 
                    //   and between constructed types that are the same when replacing all occurrences of dynamic with object.

                    // Why ignoreDynamic: false?
                    // Lowering phase treats object and dynamic as equivalent types. So we don't need to produce any conversion here,
                    // but we need to change the Type property on the resulting BoundExpression to match the rewrittenType.
                    // This is necessary so that subsequent lowering transformations see that the expression is dynamic.

                    if (inExpressionLambda || !rewrittenOperand.Type.Equals(rewrittenType, ignoreCustomModifiers: false, ignoreDynamic: false))
                    {
                        break;
                    }

                    // 4.1.6 C# spec: To force a value of a floating point type to the exact precision of its type, an explicit cast can be used.
                    // If this is not an identity conversion of a float with unknown precision, strip away the identity conversion.
                    if (!(explicitCastInCode && IsFloatPointExpressionOfUnknownPrecision(rewrittenOperand)))
                    {
                        return rewrittenOperand;
                    }

                    break;

                case ConversionKind.ExplicitUserDefined:
                case ConversionKind.ImplicitUserDefined:
                    return RewriteUserDefinedConversion(
                        syntax: syntax,
                        rewrittenOperand: rewrittenOperand,
                        method: symbolOpt,
                        rewrittenType: rewrittenType,
                        conversionKind: conversionKind);

                case ConversionKind.IntPtr:
                    return RewriteIntPtrConversion(oldNode, syntax, rewrittenOperand, conversionKind, symbolOpt, @checked,
                        explicitCastInCode, isExtensionMethod, isArrayIndex, constantValueOpt, rewrittenType);

                case ConversionKind.ImplicitNullable:
                case ConversionKind.ExplicitNullable:
                    return RewriteNullableConversion(
                        syntax: syntax,
                        rewrittenOperand: rewrittenOperand,
                        conversionKind: conversionKind,
                        @checked: @checked,
                        explicitCastInCode: explicitCastInCode,
                        rewrittenType: rewrittenType);

                case ConversionKind.Boxing:

                    if (!inExpressionLambda)
                    {
                        // We can perform some optimizations if we have a nullable value type
                        // as the operand and we know its nullability:

                        // * (object)new int?() is the same as (object)null
                        // * (object)new int?(123) is the same as (object)123

                        if (NullableNeverHasValue(rewrittenOperand))
                        {
                            return new BoundDefaultOperator(syntax, rewrittenType);
                        }

                        BoundExpression nullableValue = NullableAlwaysHasValue(rewrittenOperand);
                        if (nullableValue != null)
                        {
                            // Recurse, eliminating the unnecessary ctor.
                            return MakeConversion(oldNode, syntax, nullableValue, conversionKind,
                                symbolOpt, @checked, explicitCastInCode, isExtensionMethod, isArrayIndex,
                                constantValueOpt, rewrittenType);
                        }
                    }
                    break;

                case ConversionKind.NullLiteral:
                    if (!inExpressionLambda || !explicitCastInCode)
                    {
                        return new BoundDefaultOperator(syntax, rewrittenType);
                    }

                    break;

                case ConversionKind.ImplicitReference:
                case ConversionKind.ExplicitReference:
                    if (rewrittenOperand.IsDefaultValue() && (!inExpressionLambda || !explicitCastInCode))
                    {
                        return new BoundDefaultOperator(syntax, rewrittenType);
                    }

                    break;

                case ConversionKind.ImplicitNumeric:
                case ConversionKind.ExplicitNumeric:
                    if (rewrittenOperand.IsDefaultValue() && (!inExpressionLambda || !explicitCastInCode))
                    {
                        return new BoundDefaultOperator(syntax, rewrittenType);
                    }

                    if (rewrittenType.SpecialType == SpecialType.System_Decimal || rewrittenOperand.Type.SpecialType == SpecialType.System_Decimal)
                    {
                        return RewriteDecimalConversion(oldNode, syntax, rewrittenOperand, rewrittenOperand.Type, rewrittenType);
                    }
                    break;

                case ConversionKind.ImplicitEnumeration:
                    // A conversion from constant zero to nullable is actually classified as an 
                    // implicit enumeration conversion, not an implicit nullable conversion. 
                    // Lower it to (E?)(E)0.
                    if (rewrittenType.IsNullableType())
                    {
                        var operand = MakeConversion(
                            oldNode,
                            syntax,
                            rewrittenOperand,
                            ConversionKind.ImplicitEnumeration,
                            symbolOpt,
                            @checked,
                            explicitCastInCode,
                            isExtensionMethod,
                            false,
                            constantValueOpt,
                            rewrittenType.GetNullableUnderlyingType());

                        return MakeConversion(
                            oldNode,
                            syntax,
                            operand,
                            ConversionKind.ImplicitNullable,
                            symbolOpt,
                            @checked,
                            explicitCastInCode,
                            isExtensionMethod,
                            isArrayIndex,
                            constantValueOpt,
                            rewrittenType);
                    }

                    goto case ConversionKind.ExplicitEnumeration;

                case ConversionKind.ExplicitEnumeration:
                    if (!rewrittenType.IsNullableType() &&
                        rewrittenOperand.IsDefaultValue() &&
                        (!inExpressionLambda || !explicitCastInCode))
                    {
                        return new BoundDefaultOperator(syntax, rewrittenType);
                    }

                    if (rewrittenType.SpecialType == SpecialType.System_Decimal)
                    {
                        Debug.Assert(rewrittenOperand.Type.IsEnumType());
                        var underlyingTypeFrom = rewrittenOperand.Type.GetEnumUnderlyingType();
                        rewrittenOperand = MakeConversion(rewrittenOperand, underlyingTypeFrom, false);
                        return RewriteDecimalConversion(oldNode, syntax, rewrittenOperand, underlyingTypeFrom, rewrittenType);
                    }
                    else if (rewrittenOperand.Type.SpecialType == SpecialType.System_Decimal)
                    {
                        // This is where we handle conversion from Decimal to Enum: e.g., E e = (E) d;
                        // where 'e' is of type Enum E and 'd' is of type Decimal.
                        // Conversion can be simply done by applying its underlying numeric type to RewriteDecimalConversion(). 

                        Debug.Assert(rewrittenType.IsEnumType());
                        var underlyingTypeTo = rewrittenType.GetEnumUnderlyingType();
                        var rewrittenNode = RewriteDecimalConversion(oldNode, syntax, rewrittenOperand, rewrittenOperand.Type, underlyingTypeTo);

                        // However, the type of the rewritten node becomes underlying numeric type, not Enum type,
                        // which violates the overall constraint saying the type cannot be changed during rewriting (see LocalRewriter.cs).

                        // Instead of loosening this constraint, we return BoundConversion from underlying numeric type to Enum type,
                        // which will be eliminated during emitting (see EmitEnumConversion): e.g., E e = (E)(int) d;

                        return new BoundConversion(
                            syntax,
                            rewrittenNode,
                            conversionKind,
                            LookupResultKind.Viable,
                            isBaseConversion: false,
                            symbolOpt: symbolOpt,
                            @checked: false,
                            explicitCastInCode: explicitCastInCode,
                            isExtensionMethod: isExtensionMethod,
                            isArrayIndex: false,
                            constantValueOpt: constantValueOpt,
                            type: rewrittenType);
                    }

                    break;

                case ConversionKind.ImplicitDynamic:
                case ConversionKind.ExplicitDynamic:
                    Debug.Assert((object)symbolOpt == null);
                    Debug.Assert(!isExtensionMethod);
                    Debug.Assert(constantValueOpt == null);
                    return dynamicFactory.MakeDynamicConversion(rewrittenOperand, explicitCastInCode || conversionKind == ConversionKind.ExplicitDynamic, isArrayIndex, @checked, rewrittenType).ToExpression();

                default:
                    break;
            }

            return oldNode != null ?
                oldNode.Update(
                    rewrittenOperand,
                    conversionKind,
                    oldNode.ResultKind,
                    isBaseConversion: oldNode.IsBaseConversion,
                    symbolOpt: symbolOpt,
                    @checked: @checked,
                    explicitCastInCode: explicitCastInCode,
                    isExtensionMethod: isExtensionMethod,
                    isArrayIndex: isArrayIndex,
                    constantValueOpt: constantValueOpt,
                    type: rewrittenType) :
                new BoundConversion(
                    syntax,
                    rewrittenOperand,
                    conversionKind,
                    LookupResultKind.Viable,
                    isBaseConversion: false,
                    symbolOpt: symbolOpt,
                    @checked: @checked,
                    explicitCastInCode: explicitCastInCode,
                    isExtensionMethod: isExtensionMethod,
                    isArrayIndex: isArrayIndex,
                    constantValueOpt: constantValueOpt,
                    type: rewrittenType);
        }
Beispiel #35
0
        private void CheckVacuousComparisons(BoundBinaryOperator tree, ConstantValue constantValue, BoundNode operand)
        {
            Debug.Assert(tree != null);
            Debug.Assert(constantValue != null);
            Debug.Assert(operand != null);

            // We wish to detect comparisons between integers and constants which are likely to be wrong
            // because we know at compile time whether they will be true or false. For example:
            //
            // const short s = 1000;
            // byte b = whatever;
            // if (b < s)
            //
            // We know that this will always be true because when b and s are both converted to int for
            // the comparison, the left side will always be less than the right side.
            //
            // We only give the warning if there is no explicit conversion involved on the operand.
            // For example, if we had:
            //
            // const uint s = 1000;
            // sbyte b = whatever;
            // if ((byte)b < s)
            //
            // Then we do not give a warning.
            //
            // Note that the native compiler has what looks to be some dead code. It checks to see
            // if the conversion on the operand is from an enum type. But this is unnecessary if
            // we are rejecting cases with explicit conversions. The only possible cases are:
            //
            // enum == enumConstant           -- enum types must be the same, so it must be in range.
            // enum == integralConstant       -- not legal unless the constant is zero, which is in range.
            // enum == (ENUM)anyConstant      -- if the constant is out of range then this is not legal in the first place
            //                                   unless we're in an unchecked context, in which case, the user probably does
            //                                   not want the warning.
            // integral == enumConstant       -- never legal in the first place
            //
            // Since it seems pointless to try to check enums, we simply look for vacuous comparisons of
            // integral types here.

            for (BoundConversion conversion = operand as BoundConversion;
                 conversion != null;
                 conversion = conversion.Operand as BoundConversion)
            {
                if (conversion.ConversionKind != ConversionKind.ImplicitNumeric &&
                    conversion.ConversionKind != ConversionKind.ImplicitConstant)
                {
                    return;
                }

                // As in dev11, we don't dig through explicit casts (see ExpressionBinder::WarnAboutBadRelationals).
                if (conversion.ExplicitCastInCode)
                {
                    return;
                }

                if (!conversion.Operand.Type.SpecialType.IsIntegralType() || !conversion.Type.SpecialType.IsIntegralType())
                {
                    return;
                }

                if (!Binder.CheckConstantBounds(conversion.Operand.Type.SpecialType, constantValue))
                {
                    Error(ErrorCode.WRN_VacuousIntegralComp, tree, conversion.Operand.Type);
                    return;
                }
            }
        }
        private BoundExpression RewriteIntPtrConversion(
            BoundConversion oldNode,
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenOperand,
            ConversionKind conversionKind,
            MethodSymbol symbolOpt,
            bool @checked,
            bool explicitCastInCode,
            bool isExtensionMethod,
            bool isArrayIndex,
            ConstantValue constantValueOpt,
            TypeSymbol rewrittenType)
        {
            Debug.Assert(rewrittenOperand != null);
            Debug.Assert((object)rewrittenType != null);

            TypeSymbol source = rewrittenOperand.Type;
            TypeSymbol target = rewrittenType;

            TypeSymbol t0 = target.StrippedType();
            TypeSymbol s0 = source.StrippedType();

            if (t0 != target && s0 != source)
            {
                // UNDONE: RewriteLiftedIntPtrConversion

                return oldNode != null ?
                    oldNode.Update(
                        rewrittenOperand,
                        conversionKind,
                        oldNode.ResultKind,
                        isBaseConversion: oldNode.IsBaseConversion,
                        symbolOpt: symbolOpt,
                        @checked: @checked,
                        explicitCastInCode: explicitCastInCode,
                        isExtensionMethod: isExtensionMethod,
                        isArrayIndex: isArrayIndex,
                        constantValueOpt: constantValueOpt,
                        type: rewrittenType) :
                    new BoundConversion(
                        syntax,
                        rewrittenOperand,
                        conversionKind,
                        LookupResultKind.Viable,
                        isBaseConversion: false,
                        symbolOpt: symbolOpt,
                        @checked: @checked,
                        explicitCastInCode: explicitCastInCode,
                        isExtensionMethod: isExtensionMethod,
                        isArrayIndex: isArrayIndex,
                        constantValueOpt: constantValueOpt,
                        type: rewrittenType);
            }

            SpecialMember member = GetIntPtrConversionMethod(source: rewrittenOperand.Type, target: rewrittenType);
            MethodSymbol method = GetSpecialTypeMethod(syntax, member);
            Debug.Assert(!method.ReturnsVoid);
            Debug.Assert(method.ParameterCount == 1);

            rewrittenOperand = MakeConversion(rewrittenOperand, method.ParameterTypes[0], @checked);

            var returnType = method.ReturnType;
            Debug.Assert((object)returnType != null);

            if (inExpressionLambda)
            {
                return BoundConversion.Synthesized(syntax, rewrittenOperand, new Conversion(conversionKind, method, false), @checked, explicitCastInCode, constantValueOpt, rewrittenType);
            }

            var rewrittenCall =
                inExpressionLambda && oldNode != null
                ? new BoundConversion(syntax, rewrittenOperand, new Conversion(conversionKind, method, false), @checked, explicitCastInCode, constantValueOpt, returnType)
                : MakeCall(
                    syntax: syntax,
                    rewrittenReceiver: null,
                    method: method,
                    rewrittenArguments: ImmutableArray.Create<BoundExpression>(rewrittenOperand),
                    type: returnType);

            return MakeConversion(rewrittenCall, rewrittenType, @checked);
        }
Beispiel #37
0
        // A "surprising" sign extension is:
        //
        // * a conversion with no cast in source code that goes from a smaller
        //   signed type to a larger signed or unsigned type.
        //
        // * an conversion (with or without a cast) from a smaller
        //   signed type to a larger unsigned type.

        private static ulong FindSurprisingSignExtensionBits(BoundExpression expr)
        {
            if (expr.Kind != BoundKind.Conversion)
            {
                return(0);
            }

            BoundConversion conv = (BoundConversion)expr;
            TypeSymbol      from = conv.Operand.Type;
            TypeSymbol      to   = conv.Type;

            if ((object)from == null || (object)to == null)
            {
                return(0);
            }

            if (from.IsNullableType())
            {
                from = from.GetNullableUnderlyingType();
            }

            if (to.IsNullableType())
            {
                to = to.GetNullableUnderlyingType();
            }

            SpecialType fromSpecialType = from.SpecialType;
            SpecialType toSpecialType   = to.SpecialType;

            if (!fromSpecialType.IsIntegralType() || !toSpecialType.IsIntegralType())
            {
                return(0);
            }

            int fromSize = fromSpecialType.SizeInBytes();
            int toSize   = toSpecialType.SizeInBytes();

            if (fromSize == 0 || toSize == 0)
            {
                return(0);
            }

            // The operand might itself be a conversion, and might be contributing
            // surprising bits. We might have more, fewer or the same surprising bits
            // as the operand.

            ulong recursive = FindSurprisingSignExtensionBits(conv.Operand);

            if (fromSize == toSize)
            {
                // No change.
                return(recursive);
            }

            if (toSize < fromSize)
            {
                // We are casting from a larger type to a smaller type, and are therefore
                // losing surprising bits.
                switch (toSize)
                {
                case 1: return(unchecked ((ulong)(byte)recursive));

                case 2: return(unchecked ((ulong)(ushort)recursive));

                case 4: return(unchecked ((ulong)(uint)recursive));
                }
                Debug.Assert(false, "How did we get here?");
                return(recursive);
            }

            // We are converting from a smaller type to a larger type, and therefore might
            // be adding surprising bits. First of all, the smaller type has got to be signed
            // for there to be sign extension.

            bool fromSigned = fromSpecialType.IsSignedIntegralType();

            if (!fromSigned)
            {
                return(recursive);
            }

            // OK, we know that the "from" type is a signed integer that is smaller than the
            // "to" type, so we are going to have sign extension. Is it surprising? The only
            // time that sign extension is *not* surprising is when we have a cast operator
            // to a *signed* type. That is, (int)myShort is not a surprising sign extension.

            if (conv.ExplicitCastInCode && toSpecialType.IsSignedIntegralType())
            {
                return(recursive);
            }

            // Note that we *could* be somewhat more clever here. Consider the following edge case:
            //
            // (ulong)(int)(uint)(ushort)mySbyte
            //
            // We could reason that the sbyte-to-ushort conversion is going to add one byte of
            // unexpected sign extension. The conversion from ushort to uint adds no more bytes.
            // The conversion from uint to int adds no more bytes. Does the conversion from int
            // to ulong add any more bytes of unexpected sign extension? Well, no, because we
            // know that the previous conversion from ushort to uint will ensure that the top bit
            // of the uint is off!
            //
            // But we are not going to try to be that clever. In the extremely unlikely event that
            // someone does this, we will record that the unexpectedly turned-on bits are
            // 0xFFFFFFFF0000FF00, even though we could in theory deduce that only 0x000000000000FF00
            // are the unexpected bits.

            ulong result = recursive;

            for (int i = fromSize; i < toSize; ++i)
            {
                result |= (0xFFUL) << (i * 8);
            }

            return(result);
        }
Beispiel #38
0
        private BoundExpression BindCodeblock(SyntaxNode syntax, UnboundLambda unboundLambda, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics)
        {
            if (!Compilation.Options.HasRuntime)
            {
                return(null);
            }
            var isCodeblock = syntax.XIsCodeBlock;

            if (!isCodeblock)
            {
                isCodeblock = !destination.IsDelegateType() && !destination.IsExpressionTree();
            }
            if (!isCodeblock)
            {
                return(null);
            }

            Conversion conv = Conversion.ImplicitReference;

            if (destination != Compilation.CodeBlockType() && !destination.IsObjectType())
            {
                HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                conv = Conversions.ClassifyConversionFromType(Compilation.CodeBlockType(), destination, ref useSiteDiagnostics);
                diagnostics.Add(syntax, useSiteDiagnostics);
            }
            if (Compilation.Options.HasRuntime)
            {
                Debug.Assert(destination == Compilation.CodeBlockType() || conv.Exists);
            }
            if (!syntax.XIsCodeBlock && !Compilation.Options.MacroScript && !syntax.XNode.IsAliasExpression())
            {
                Error(diagnostics, ErrorCode.ERR_CodeblockWithLambdaSyntax, syntax);
            }

            AnonymousTypeManager manager = this.Compilation.AnonymousTypeManager;
            var delegateSignature        = new TypeSymbol[unboundLambda.ParameterCount + 1];
            var usualType = this.Compilation.UsualType();

            for (int i = 0; i < delegateSignature.Length; i++)
            {
                delegateSignature[i] = usualType;
            }
            NamedTypeSymbol cbType       = manager.ConstructCodeblockTypeSymbol(delegateSignature, syntax.Location);
            var             delType      = manager.GetCodeblockDelegateType(cbType);
            var             _boundLambda = unboundLambda.Bind(delType);

            diagnostics.AddRange(_boundLambda.Diagnostics);
            var cbDel = new BoundConversion(
                syntax,
                _boundLambda,
                conversion,
                @checked: false,
                explicitCastInCode: false,
                constantValueOpt: ConstantValue.NotAvailable,
                type: delType)
            {
                WasCompilerGenerated = unboundLambda.WasCompilerGenerated
            };
            var             cbSrc  = new BoundLiteral(syntax, ConstantValue.Create(syntax.XCodeBlockSource), Compilation.GetSpecialType(SpecialType.System_String));
            BoundExpression cbInst = new BoundAnonymousObjectCreationExpression(syntax,
                                                                                cbType.InstanceConstructors[0],
                                                                                new BoundExpression[] { cbDel, cbSrc }.ToImmutableArrayOrEmpty(),
                                                                                System.Collections.Immutable.ImmutableArray <BoundAnonymousPropertyDeclaration> .Empty, cbType)
            {
                WasCompilerGenerated = unboundLambda.WasCompilerGenerated
            };;

            if (conv != Conversion.ImplicitReference)
            {
                cbInst = new BoundConversion(syntax, cbInst, Conversion.ImplicitReference, false, false, ConstantValue.NotAvailable, Compilation.CodeBlockType())
                {
                    WasCompilerGenerated = unboundLambda.WasCompilerGenerated
                };;
            }
            if (!conv.IsValid || (!isCast && conv.IsExplicit))
            {
                GenerateImplicitConversionError(diagnostics, syntax, conv, cbInst, destination);

                return(new BoundConversion(
                           syntax,
                           cbInst,
                           conv,
                           false,
                           explicitCastInCode: isCast,
                           constantValueOpt: ConstantValue.NotAvailable,
                           type: destination,
                           hasErrors: true)
                {
                    WasCompilerGenerated = unboundLambda.WasCompilerGenerated
                });
            }
            return(new BoundConversion(
                       syntax,
                       cbInst,
                       conv,
                       false,
                       explicitCastInCode: isCast,
                       constantValueOpt: ConstantValue.NotAvailable,
                       type: destination)
            {
                WasCompilerGenerated = unboundLambda.WasCompilerGenerated
            });
        }
Beispiel #39
0
        /// <summary>
        /// Generate a thread-safe accessor for a regular field-like event.
        ///
        /// DelegateType tmp0 = _event; //backing field
        /// DelegateType tmp1;
        /// DelegateType tmp2;
        /// do {
        ///     tmp1 = tmp0;
        ///     tmp2 = (DelegateType)Delegate.Combine(tmp1, value); //Remove for -=
        ///     tmp0 = Interlocked.CompareExchange&lt;DelegateType&gt;(ref _event, tmp2, tmp1);
        /// } while ((object)tmp0 != (object)tmp1);
        ///
        /// Note, if System.Threading.Interlocked.CompareExchange&lt;T&gt; is not available,
        /// we emit the following code and mark the method Synchronized (unless it is a struct).
        ///
        /// _event = (DelegateType)Delegate.Combine(_event, value); //Remove for -=
        ///
        /// </summary>
        internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, DiagnosticBag diagnostics)
        {
            CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode;

            TypeSymbol      delegateType  = eventSymbol.Type;
            MethodSymbol    accessor      = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod;
            ParameterSymbol thisParameter = accessor.ThisParameter;

            TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean);

            SpecialMember updateMethodId = isAddMethod ? SpecialMember.System_Delegate__Combine : SpecialMember.System_Delegate__Remove;
            MethodSymbol  updateMethod   = (MethodSymbol)compilation.GetSpecialTypeMember(updateMethodId);

            BoundStatement @return = new BoundReturnStatement(syntax,
                                                              expressionOpt: null)
            {
                WasCompilerGenerated = true
            };

            if (updateMethod == null)
            {
                MemberDescriptor memberDescriptor = SpecialMembers.GetDescriptor(updateMethodId);
                diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(ErrorCode.ERR_MissingPredefinedMember,
                                                                      memberDescriptor.DeclaringTypeMetadataName,
                                                                      memberDescriptor.Name),
                                                 syntax.Location));

                return(new BoundBlock(syntax,
                                      locals: ImmutableArray <LocalSymbol> .Empty,
                                      statements: ImmutableArray.Create <BoundStatement>(@return))
                {
                    WasCompilerGenerated = true
                });
            }

            Binder.ReportUseSiteDiagnostics(updateMethod, diagnostics, syntax);

            BoundThisReference fieldReceiver = eventSymbol.IsStatic ?
                                               null :
                                               new BoundThisReference(syntax, thisParameter.Type)
            {
                WasCompilerGenerated = true
            };

            BoundFieldAccess boundBackingField = new BoundFieldAccess(syntax,
                                                                      receiver: fieldReceiver,
                                                                      fieldSymbol: eventSymbol.AssociatedField,
                                                                      constantValueOpt: null)
            {
                WasCompilerGenerated = true
            };

            BoundParameter boundParameter = new BoundParameter(syntax,
                                                               parameterSymbol: accessor.Parameters[0])
            {
                WasCompilerGenerated = true
            };

            BoundExpression delegateUpdate;

            MethodSymbol compareExchangeMethod = (MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Threading_Interlocked__CompareExchange_T);

            if ((object)compareExchangeMethod == null)
            {
                // (DelegateType)Delegate.Combine(_event, value)
                delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax,
                                                                           operand: BoundCall.Synthesized(syntax,
                                                                                                          receiverOpt: null,
                                                                                                          method: updateMethod,
                                                                                                          arguments: ImmutableArray.Create <BoundExpression>(boundBackingField, boundParameter)),
                                                                           kind: ConversionKind.ExplicitReference,
                                                                           type: delegateType);

                // _event = (DelegateType)Delegate.Combine(_event, value);
                BoundStatement eventUpdate = new BoundExpressionStatement(syntax,
                                                                          expression: new BoundAssignmentOperator(syntax,
                                                                                                                  left: boundBackingField,
                                                                                                                  right: delegateUpdate,
                                                                                                                  type: delegateType)
                {
                    WasCompilerGenerated = true
                })
                {
                    WasCompilerGenerated = true
                };

                return(new BoundBlock(syntax,
                                      locals: ImmutableArray <LocalSymbol> .Empty,
                                      statements: ImmutableArray.Create <BoundStatement>(
                                          eventUpdate,
                                          @return))
                {
                    WasCompilerGenerated = true
                });
            }

            compareExchangeMethod = compareExchangeMethod.Construct(ImmutableArray.Create <TypeSymbol>(delegateType));

            Binder.ReportUseSiteDiagnostics(compareExchangeMethod, diagnostics, syntax);

            GeneratedLabelSymbol loopLabel = new GeneratedLabelSymbol("loop");

            const int numTemps = 3;

            LocalSymbol[] tmps      = new LocalSymbol[numTemps];
            BoundLocal[]  boundTmps = new BoundLocal[numTemps];

            for (int i = 0; i < numTemps; i++)
            {
                tmps[i]      = new SynthesizedLocal(accessor, delegateType, SynthesizedLocalKind.LoweringTemp);
                boundTmps[i] = new BoundLocal(syntax, tmps[i], null, delegateType);
            }

            // tmp0 = _event;
            BoundStatement tmp0Init = new BoundExpressionStatement(syntax,
                                                                   expression: new BoundAssignmentOperator(syntax,
                                                                                                           left: boundTmps[0],
                                                                                                           right: boundBackingField,
                                                                                                           type: delegateType)
            {
                WasCompilerGenerated = true
            })
            {
                WasCompilerGenerated = true
            };

            // LOOP:
            BoundStatement loopStart = new BoundLabelStatement(syntax,
                                                               label: loopLabel)
            {
                WasCompilerGenerated = true
            };

            // tmp1 = tmp0;
            BoundStatement tmp1Update = new BoundExpressionStatement(syntax,
                                                                     expression: new BoundAssignmentOperator(syntax,
                                                                                                             left: boundTmps[1],
                                                                                                             right: boundTmps[0],
                                                                                                             type: delegateType)
            {
                WasCompilerGenerated = true
            })
            {
                WasCompilerGenerated = true
            };

            // (DelegateType)Delegate.Combine(tmp1, value)
            delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax,
                                                                       operand: BoundCall.Synthesized(syntax,
                                                                                                      receiverOpt: null,
                                                                                                      method: updateMethod,
                                                                                                      arguments: ImmutableArray.Create <BoundExpression>(boundTmps[1], boundParameter)),
                                                                       kind: ConversionKind.ExplicitReference,
                                                                       type: delegateType);

            // tmp2 = (DelegateType)Delegate.Combine(tmp1, value);
            BoundStatement tmp2Update = new BoundExpressionStatement(syntax,
                                                                     expression: new BoundAssignmentOperator(syntax,
                                                                                                             left: boundTmps[2],
                                                                                                             right: delegateUpdate,
                                                                                                             type: delegateType)
            {
                WasCompilerGenerated = true
            })
            {
                WasCompilerGenerated = true
            };

            // Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1)
            BoundExpression compareExchange = BoundCall.Synthesized(syntax,
                                                                    receiverOpt: null,
                                                                    method: compareExchangeMethod,
                                                                    arguments: ImmutableArray.Create <BoundExpression>(boundBackingField, boundTmps[2], boundTmps[1]));

            // tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1);
            BoundStatement tmp0Update = new BoundExpressionStatement(syntax,
                                                                     expression: new BoundAssignmentOperator(syntax,
                                                                                                             left: boundTmps[0],
                                                                                                             right: compareExchange,
                                                                                                             type: delegateType)
            {
                WasCompilerGenerated = true
            })
            {
                WasCompilerGenerated = true
            };

            // tmp0 == tmp1 // i.e. exit when they are equal, jump to start otherwise
            BoundExpression loopExitCondition = new BoundBinaryOperator(syntax,
                                                                        operatorKind: BinaryOperatorKind.ObjectEqual,
                                                                        left: boundTmps[0],
                                                                        right: boundTmps[1],
                                                                        constantValueOpt: null,
                                                                        methodOpt: null,
                                                                        resultKind: LookupResultKind.Viable,
                                                                        type: boolType)
            {
                WasCompilerGenerated = true
            };

            // branchfalse (tmp0 == tmp1) LOOP
            BoundStatement loopEnd = new BoundConditionalGoto(syntax,
                                                              condition: loopExitCondition,
                                                              jumpIfTrue: false,
                                                              label: loopLabel)
            {
                WasCompilerGenerated = true
            };

            return(new BoundBlock(syntax,
                                  locals: tmps.AsImmutable(),
                                  statements: ImmutableArray.Create <BoundStatement>(
                                      tmp0Init,
                                      loopStart,
                                      tmp1Update,
                                      tmp2Update,
                                      tmp0Update,
                                      loopEnd,
                                      @return))
            {
                WasCompilerGenerated = true
            });
        }
Beispiel #40
0
        private BoundExpression CreateTupleLiteralConversion(CSharpSyntaxNode syntax, BoundTupleLiteral sourceTuple, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics)
        {
            // We have a successful tuple conversion; rather than producing a separate conversion node 
            // which is a conversion on top of a tuple literal, tuple conversion is an element-wise conversion of arguments.

            Debug.Assert(conversion.Kind == ConversionKind.ImplicitTupleLiteral || conversion.Kind == ConversionKind.ImplicitNullable);
            Debug.Assert((conversion.Kind == ConversionKind.ImplicitNullable) == destination.IsNullableType());

            TypeSymbol destinationWithoutNullable = conversion.Kind == ConversionKind.ImplicitNullable ?
                                                                           destinationWithoutNullable = destination.GetNullableUnderlyingType() :
                                                                           destination;

            NamedTypeSymbol targetType = (NamedTypeSymbol)destinationWithoutNullable;

            if (targetType.IsTupleType)
            {
                var destTupleType = (TupleTypeSymbol)targetType;
                // do not lose the original element names in the literal if different from names in the target

                // Come back to this, what about locations? (https://github.com/dotnet/roslyn/issues/11013)
                targetType = destTupleType.WithElementNames(sourceTuple.ArgumentNamesOpt);
            }

            var arguments = sourceTuple.Arguments;
            var convertedArguments = ArrayBuilder<BoundExpression>.GetInstance(arguments.Length);

            ImmutableArray<TypeSymbol> targetElementTypes = targetType.GetElementTypesOfTupleOrCompatible();
            Debug.Assert(targetElementTypes.Length == arguments.Length, "converting a tuple literal to incompatible type?");

            for (int i = 0; i < arguments.Length; i++)
            {
                var argument = arguments[i];
                var destType = targetElementTypes[i];

                HashSet<DiagnosticInfo> useSiteDiagnostics = null;
                Conversion elementConversion;
                if (isCast)
                {
                    elementConversion = this.Conversions.ClassifyConversionForCast(argument, destType, ref useSiteDiagnostics);
                }
                else
                {
                    elementConversion = this.Conversions.ClassifyConversionFromExpression(argument, destType, ref useSiteDiagnostics);
                }

                diagnostics.Add(syntax, useSiteDiagnostics);
                convertedArguments.Add(CreateConversion(argument.Syntax, argument, elementConversion, isCast, destType, diagnostics));
            }

            BoundExpression result = new BoundConvertedTupleLiteral(
                sourceTuple.Syntax,
                sourceTuple.Type,
                convertedArguments.ToImmutableAndFree(), 
                targetType);

            // We need to preserve any conversion that changes the type (even identity conversions),
            // or that was explicitly written in code (so that GetSemanticInfo can find the syntax in the bound tree).
            if (!isCast && targetType == destination)
            {
                return result;
            }

            // if we have a nullable cast combined with a name/dynamic cast
            // name/dynamic cast must happen before converting to nullable
            if (conversion.Kind == ConversionKind.ImplicitNullable &&
                destinationWithoutNullable != targetType)
            {
                Debug.Assert(destinationWithoutNullable.Equals(targetType, ignoreDynamic: true));

                result = new BoundConversion(
                    syntax,
                    result,
                    Conversion.Identity,
                    @checked: false,
                    explicitCastInCode: isCast,
                    constantValueOpt: ConstantValue.NotAvailable,
                    type: destinationWithoutNullable)
                { WasCompilerGenerated = sourceTuple.WasCompilerGenerated };
            }
                                  
            return new BoundConversion(
                syntax,
                result,
                conversion,
                @checked: false,
                explicitCastInCode: isCast,
                constantValueOpt: ConstantValue.NotAvailable,
                type: destination)
            { WasCompilerGenerated = sourceTuple.WasCompilerGenerated };
        }
Beispiel #41
0
            private TypedConstant VisitConversion(BoundConversion node, DiagnosticBag diagnostics, ref bool attrHasErrors, bool curArgumentHasErrors)
            {
                Debug.Assert(node.ConstantValue == null);

                // We have a bound conversion with a non-constant value.
                // According to statement 2) of the spec comment, this is not a valid attribute argument.
                // However, native compiler allows conversions to object type if the conversion operand is a valid attribute argument.
                // See method AttributeHelper::VerifyAttrArg(EXPR *arg).

                // We will match native compiler's behavior here.
                // Devdiv Bug #8763: Additionally we allow conversions from array type to object[], provided a conversion exists and each array element is a valid attribute argument.

                var type = node.Type;
                var operand = node.Operand;
                var operandType = operand.Type;

                if ((object)type != null && (object)operandType != null)
                {
                    if (type.SpecialType == SpecialType.System_Object ||
                        operandType.IsArray() && type.IsArray() &&
                        ((ArrayTypeSymbol)type).ElementType.SpecialType == SpecialType.System_Object)
                    {
                        var typedConstantKind = operandType.GetAttributeParameterTypedConstantKind(_binder.Compilation);
                        return VisitExpression(operand, typedConstantKind, diagnostics, ref attrHasErrors, curArgumentHasErrors);
                    }
                }

                return CreateTypedConstant(node, TypedConstantKind.Error, diagnostics, ref attrHasErrors, curArgumentHasErrors);
            }
        public override BoundNode VisitConversion(BoundConversion node)
        {
            CheckUnsafeType(node.Operand);
            CheckUnsafeType(node);
            bool wasInExpressionLambda = _inExpressionLambda;
            bool oldReportedUnsafe = _reportedUnsafe;
            switch (node.ConversionKind)
            {
                case ConversionKind.MethodGroup:
                    VisitMethodGroup((BoundMethodGroup)node.Operand, parentIsConversion: true);
                    return node;

                case ConversionKind.AnonymousFunction:
                    if (!wasInExpressionLambda && node.Type.IsExpressionTree())
                    {
                        _inExpressionLambda = true;
                        // we report "unsafe in expression tree" at most once for each expression tree
                        _reportedUnsafe = false;
                    }
                    break;

                case ConversionKind.ImplicitDynamic:
                case ConversionKind.ExplicitDynamic:
                    if (_inExpressionLambda)
                    {
                        Error(ErrorCode.ERR_ExpressionTreeContainsDynamicOperation, node);
                    }
                    break;

                default:
                    break;
            }

            var result = base.VisitConversion(node);
            _inExpressionLambda = wasInExpressionLambda;
            _reportedUnsafe = oldReportedUnsafe;
            return result;
        }