public SynthesizedIntrinsicOperatorSymbol(TypeSymbol leftType, string name, TypeSymbol rightType, TypeSymbol returnType, bool isCheckedBuiltin)
        {
            if (leftType.Equals(rightType, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds))
            {
                _containingType = leftType;
            }
            else if (rightType.Equals(returnType, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds))
            {
                _containingType = rightType;
            }
            else
            {
                Debug.Assert(leftType.Equals(returnType, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
                _containingType = leftType;
            }

            _name = name;
            _returnType = returnType;

            Debug.Assert((leftType.IsDynamic() || rightType.IsDynamic()) == returnType.IsDynamic());
            Debug.Assert(_containingType.IsDynamic() == returnType.IsDynamic());

            _parameters = (new ParameterSymbol[] {new SynthesizedOperatorParameterSymbol(this, leftType, 0, "left"),
                                                      new SynthesizedOperatorParameterSymbol(this, rightType, 1, "right")}).AsImmutableOrNull();
            _isCheckedBuiltin = isCheckedBuiltin;
        }
        public SynthesizedIntrinsicOperatorSymbol(TypeSymbol leftType, string name, TypeSymbol rightType, TypeSymbol returnType, bool isCheckedBuiltin)
        {
            if (leftType.Equals(rightType, ignoreCustomModifiers: true))
            {
                this.containingType = leftType;
            }
            else if (rightType.Equals(returnType, ignoreCustomModifiers: true))
            {
                this.containingType = rightType;
            }
            else
            {
                Debug.Assert(leftType.Equals(returnType, ignoreCustomModifiers: true));
                this.containingType = leftType;
            }

            this.name = name;
            this.returnType = returnType;

            Debug.Assert((leftType.IsDynamic() || rightType.IsDynamic()) == returnType.IsDynamic());
            Debug.Assert(containingType.IsDynamic() == returnType.IsDynamic());

            this.parameters = (new ParameterSymbol[] {new SynthesizedOperatorParameterSymbol(this, leftType, 0, "left"),
                                                      new SynthesizedOperatorParameterSymbol(this, rightType, 1, "right")}).AsImmutableOrNull();
            this.isCheckedBuiltin = isCheckedBuiltin;
        }
Example #3
0
        private static Symbol GetIntrinsicOperatorSymbol(BinaryOperatorKind op, bool isDynamic, TypeSymbol leftType, TypeSymbol rightType, TypeSymbol returnType, bool isChecked)
        {
            if (!isDynamic)
            {
                leftType = leftType.StrippedType();
                rightType = rightType.StrippedType();
                returnType = returnType.StrippedType();
            }
            else
            {
                Debug.Assert(returnType.IsDynamic());

                if ((object)leftType == null)
                {
                    Debug.Assert(rightType.IsDynamic());
                    leftType = rightType;
                }
                else if ((object)rightType == null)
                {
                    Debug.Assert(leftType.IsDynamic());
                    rightType = leftType;
                }
            }
            return new SynthesizedIntrinsicOperatorSymbol(leftType,
                                                          OperatorFacts.BinaryOperatorNameFromOperatorKind(op),
                                                          rightType,
                                                          returnType,
                                                          isChecked);
        }
Example #4
0
        private TypeSymbol TransformType(TypeSymbol type)
        {
            Debug.Assert(_index >= 0);

            if (!HasFlag ||
                PeekFlag() && (type.SpecialType != SpecialType.System_Object && !type.IsDynamic()))
            {
                // Bail, since flags are invalid.
                return null;
            }

            switch (type.Kind)
            {
                case SymbolKind.ErrorType:
                case SymbolKind.NamedType:
                    if (type.SpecialType == SpecialType.System_Object)
                    {
                        // Replace the given System.Object type with dynamic type if the corresponding dynamicTransformFlag is set to true.
                        return ConsumeFlag() ? DynamicTypeSymbol.Instance : type;
                    }

                    return TransformNamedType((NamedTypeSymbol)type);

                case SymbolKind.ArrayType:
                    return TransformArrayType((ArrayTypeSymbol)type);

                case SymbolKind.PointerType:
                    return TransformPointerType((PointerTypeSymbol)type);

                case SymbolKind.DynamicType:
                    Debug.Assert(!_haveCustomModifierFlags, "This shouldn't happen during decoding.");
                    return ConsumeFlag()
                        ? type
                        : _containingAssembly.GetSpecialType(SpecialType.System_Object);

                default:
                    ConsumeFlag();
                    return HandleCustomModifiers(type.CustomModifierCount()) ? type : null;
            }
        }
        private void TestBinaryIntrinsicSymbol(
            BinaryOperatorKind op,
            TypeSymbol leftType,
            TypeSymbol rightType,
            CSharpCompilation compilation,
            SemanticModel semanticModel,
            ExpressionSyntax node1,
            ExpressionSyntax node2,
            ExpressionSyntax node3,
            ExpressionSyntax node4,
            ExpressionSyntax node5,
            ExpressionSyntax node6,
            ExpressionSyntax node7,
            ExpressionSyntax node8
        )
        {
            SymbolInfo info1 = semanticModel.GetSymbolInfo(node1);
            HashSet<DiagnosticInfo> useSiteDiagnostics = null;

            if (info1.Symbol == null)
            {
                if (info1.CandidateSymbols.Length == 0)
                {
                    if (leftType.IsDynamic() || rightType.IsDynamic())
                    {
                        Assert.True(CandidateReason.LateBound == info1.CandidateReason || CandidateReason.None == info1.CandidateReason);
                    }
                    else
                    {
                        Assert.Equal(CandidateReason.None, info1.CandidateReason);
                    }
                }
                else
                {
                    Assert.Equal(CandidateReason.OverloadResolutionFailure, info1.CandidateReason);
                    foreach (MethodSymbol s in info1.CandidateSymbols)
                    {
                        Assert.Equal(MethodKind.UserDefinedOperator, s.MethodKind);
                    }
                }
            }
            else
            {
                Assert.Equal(leftType.IsDynamic() || rightType.IsDynamic() ? CandidateReason.LateBound : CandidateReason.None, info1.CandidateReason);
                Assert.Equal(0, info1.CandidateSymbols.Length);
            }

            var symbol1 = (MethodSymbol)info1.Symbol;
            var symbol2 = semanticModel.GetSymbolInfo(node2).Symbol;
            var symbol3 = semanticModel.GetSymbolInfo(node3).Symbol;
            var symbol4 = semanticModel.GetSymbolInfo(node4).Symbol;
            var symbol5 = (MethodSymbol)semanticModel.GetSymbolInfo(node5).Symbol;
            var symbol6 = semanticModel.GetSymbolInfo(node6).Symbol;
            var symbol7 = semanticModel.GetSymbolInfo(node7).Symbol;
            var symbol8 = semanticModel.GetSymbolInfo(node8).Symbol;

            Assert.Equal(symbol1, symbol5);
            Assert.Equal(symbol2, symbol6);
            Assert.Equal(symbol3, symbol7);
            Assert.Equal(symbol4, symbol8);

            if ((object)symbol1 != null && symbol1.IsImplicitlyDeclared)
            {
                Assert.NotSame(symbol1, symbol5);
                Assert.Equal(symbol1.GetHashCode(), symbol5.GetHashCode());

                for (int i = 0; i < 2; i++)
                {
                    Assert.Equal(symbol1.Parameters[i], symbol5.Parameters[i]);
                    Assert.Equal(symbol1.Parameters[i].GetHashCode(), symbol5.Parameters[i].GetHashCode());
                }

                Assert.NotEqual(symbol1.Parameters[0], symbol5.Parameters[1]);
            }

            switch (op)
            {
                case BinaryOperatorKind.LogicalAnd:
                case BinaryOperatorKind.LogicalOr:
                    Assert.Null(symbol1);
                    Assert.Null(symbol2);
                    Assert.Null(symbol3);
                    Assert.Null(symbol4);
                    return;
            }


            BinaryOperatorKind result = OverloadResolution.BinopEasyOut.OpKind(op, leftType, rightType);
            BinaryOperatorSignature signature;
            bool isDynamic = (leftType.IsDynamic() || rightType.IsDynamic());

            if (result == BinaryOperatorKind.Error)
            {
                if (leftType.IsDynamic() && !rightType.IsPointerType() && !rightType.IsRestrictedType())
                {
                    signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Dynamic, leftType, rightType, leftType);
                }
                else if (rightType.IsDynamic() && !leftType.IsPointerType() && !leftType.IsRestrictedType())
                {
                    signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Dynamic, leftType, rightType, rightType);
                }
                else if ((op == BinaryOperatorKind.Equal || op == BinaryOperatorKind.NotEqual) &&
                    leftType.IsReferenceType && rightType.IsReferenceType &&
                    (leftType == rightType || compilation.Conversions.ClassifyConversion(leftType, rightType, ref useSiteDiagnostics).IsReference))
                {
                    if (leftType.IsDelegateType() && rightType.IsDelegateType())
                    {
                        Assert.Equal(leftType, rightType);
                        signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Delegate,
                            leftType, // TODO: this feels like a spec violation
                            leftType, // TODO: this feels like a spec violation
                            compilation.GetSpecialType(SpecialType.System_Boolean));
                    }
                    else if (leftType.SpecialType == SpecialType.System_Delegate && rightType.SpecialType == SpecialType.System_Delegate)
                    {
                        signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Delegate,
                            compilation.GetSpecialType(SpecialType.System_Delegate), compilation.GetSpecialType(SpecialType.System_Delegate),
                            compilation.GetSpecialType(SpecialType.System_Boolean));
                    }
                    else
                    {
                        signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Object, compilation.ObjectType, compilation.ObjectType,
                            compilation.GetSpecialType(SpecialType.System_Boolean));
                    }
                }
                else if (op == BinaryOperatorKind.Addition &&
                    ((leftType.IsStringType() && !rightType.IsPointerType()) || (!leftType.IsPointerType() && rightType.IsStringType())))
                {
                    Assert.False(leftType.IsStringType() && rightType.IsStringType());

                    if (leftType.IsStringType())
                    {
                        signature = new BinaryOperatorSignature(op | BinaryOperatorKind.String, leftType, compilation.ObjectType, leftType);
                    }
                    else
                    {
                        Assert.True(rightType.IsStringType());
                        signature = new BinaryOperatorSignature(op | BinaryOperatorKind.String, compilation.ObjectType, rightType, rightType);
                    }
                }
                else if (op == BinaryOperatorKind.Addition &&
                    (((leftType.IsIntegralType() || leftType.IsCharType()) && rightType.IsPointerType()) ||
                    (leftType.IsPointerType() && (rightType.IsIntegralType() || rightType.IsCharType()))))
                {
                    if (leftType.IsPointerType())
                    {
                        signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Pointer, leftType, symbol1.Parameters[1].Type, leftType);
                        Assert.True(symbol1.Parameters[1].Type.IsIntegralType());
                    }
                    else
                    {
                        signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Pointer, symbol1.Parameters[0].Type, rightType, rightType);
                        Assert.True(symbol1.Parameters[0].Type.IsIntegralType());
                    }
                }
                else if (op == BinaryOperatorKind.Subtraction &&
                    (leftType.IsPointerType() && (rightType.IsIntegralType() || rightType.IsCharType())))
                {
                    signature = new BinaryOperatorSignature(op | BinaryOperatorKind.String, leftType, symbol1.Parameters[1].Type, leftType);
                    Assert.True(symbol1.Parameters[1].Type.IsIntegralType());
                }
                else if (op == BinaryOperatorKind.Subtraction && leftType.IsPointerType() && leftType == rightType)
                {
                    signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Pointer, leftType, rightType, compilation.GetSpecialType(SpecialType.System_Int64));
                }
                else if ((op == BinaryOperatorKind.Addition || op == BinaryOperatorKind.Subtraction) &&
                    leftType.IsEnumType() && (rightType.IsIntegralType() || rightType.IsCharType()) &&
                    (result = OverloadResolution.BinopEasyOut.OpKind(op, leftType.EnumUnderlyingType(), rightType)) != BinaryOperatorKind.Error &&
                    (signature = compilation.builtInOperators.GetSignature(result)).RightType == leftType.EnumUnderlyingType())
                {
                    signature = new BinaryOperatorSignature(signature.Kind | BinaryOperatorKind.EnumAndUnderlying, leftType, signature.RightType, leftType);
                }
                else if ((op == BinaryOperatorKind.Addition || op == BinaryOperatorKind.Subtraction) &&
                    rightType.IsEnumType() && (leftType.IsIntegralType() || leftType.IsCharType()) &&
                    (result = OverloadResolution.BinopEasyOut.OpKind(op, leftType, rightType.EnumUnderlyingType())) != BinaryOperatorKind.Error &&
                    (signature = compilation.builtInOperators.GetSignature(result)).LeftType == rightType.EnumUnderlyingType())
                {
                    signature = new BinaryOperatorSignature(signature.Kind | BinaryOperatorKind.EnumAndUnderlying, signature.LeftType, rightType, rightType);
                }
                else if (op == BinaryOperatorKind.Subtraction &&
                    leftType.IsEnumType() && leftType == rightType)
                {
                    signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Enum, leftType, rightType, leftType.EnumUnderlyingType());
                }
                else if ((op == BinaryOperatorKind.Equal ||
                          op == BinaryOperatorKind.NotEqual ||
                          op == BinaryOperatorKind.LessThan ||
                          op == BinaryOperatorKind.LessThanOrEqual ||
                          op == BinaryOperatorKind.GreaterThan ||
                          op == BinaryOperatorKind.GreaterThanOrEqual) &&
                    leftType.IsEnumType() && leftType == rightType)
                {
                    signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Enum, leftType, rightType, compilation.GetSpecialType(SpecialType.System_Boolean));
                }
                else if ((op == BinaryOperatorKind.Xor ||
                          op == BinaryOperatorKind.And ||
                          op == BinaryOperatorKind.Or) &&
                    leftType.IsEnumType() && leftType == rightType)
                {
                    signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Enum, leftType, rightType, leftType);
                }
                else if ((op == BinaryOperatorKind.Addition || op == BinaryOperatorKind.Subtraction) &&
                    leftType.IsDelegateType() && leftType == rightType)
                {
                    signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Delegate, leftType, leftType, leftType);
                }
                else if ((op == BinaryOperatorKind.Equal ||
                          op == BinaryOperatorKind.NotEqual ||
                          op == BinaryOperatorKind.LessThan ||
                          op == BinaryOperatorKind.LessThanOrEqual ||
                          op == BinaryOperatorKind.GreaterThan ||
                          op == BinaryOperatorKind.GreaterThanOrEqual) &&
                    leftType.IsPointerType() && rightType.IsPointerType())
                {
                    signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Pointer,
                        compilation.CreatePointerTypeSymbol(compilation.GetSpecialType(SpecialType.System_Void)),
                        compilation.CreatePointerTypeSymbol(compilation.GetSpecialType(SpecialType.System_Void)),
                        compilation.GetSpecialType(SpecialType.System_Boolean));
                }
                else
                {
                    if ((object)symbol1 != null)
                    {
                        Assert.False(symbol1.IsImplicitlyDeclared);
                        Assert.Equal(MethodKind.UserDefinedOperator, symbol1.MethodKind);

                        if (leftType.IsValueType && !leftType.IsPointerType())
                        {
                            if (rightType.IsValueType && !rightType.IsPointerType())
                            {
                                Assert.Same(symbol1, symbol2);
                                Assert.Same(symbol1, symbol3);
                                Assert.Same(symbol1, symbol4);
                                return;
                            }
                            else
                            {
                                Assert.Null(symbol2);
                                Assert.Same(symbol1, symbol3);
                                Assert.Null(symbol4);
                                return;
                            }
                        }
                        else if (rightType.IsValueType && !rightType.IsPointerType())
                        {
                            Assert.Null(symbol2);
                            Assert.Null(symbol3);
                            Assert.Same(symbol1, symbol4);
                            return;
                        }
                        else
                        {
                            Assert.Null(symbol2);
                            Assert.Null(symbol3);
                            Assert.Null(symbol4);
                            return;
                        }
                    }

                    Assert.Null(symbol1);
                    Assert.Null(symbol2);

                    if (!rightType.IsDynamic())
                    {
                        Assert.Null(symbol3);
                    }

                    if (!leftType.IsDynamic())
                    {
                        Assert.Null(symbol4);
                    }
                    return;
                }
            }
            else if ((op == BinaryOperatorKind.Equal || op == BinaryOperatorKind.NotEqual) &&
                leftType != rightType &&
                (!leftType.IsValueType || !rightType.IsValueType ||
                 leftType.SpecialType == SpecialType.System_Boolean || rightType.SpecialType == SpecialType.System_Boolean ||
                 (leftType.SpecialType == SpecialType.System_Decimal && (rightType.SpecialType == SpecialType.System_Double || rightType.SpecialType == SpecialType.System_Single)) ||
                 (rightType.SpecialType == SpecialType.System_Decimal && (leftType.SpecialType == SpecialType.System_Double || leftType.SpecialType == SpecialType.System_Single))) &&
                (!leftType.IsReferenceType || !rightType.IsReferenceType ||
                 !compilation.Conversions.ClassifyConversion(leftType, rightType, ref useSiteDiagnostics).IsReference))
            {
                Assert.Null(symbol1);
                Assert.Null(symbol2);
                Assert.Null(symbol3);
                Assert.Null(symbol4);
                return;
            }
            else
            {
                signature = compilation.builtInOperators.GetSignature(result);
            }

            Assert.NotNull(symbol1);

            string containerName = signature.LeftType.ToTestDisplayString();
            string leftName = containerName;
            string rightName = signature.RightType.ToTestDisplayString();
            string returnName = signature.ReturnType.ToTestDisplayString();

            if (isDynamic)
            {
                containerName = compilation.DynamicType.ToTestDisplayString();
            }
            else if (op == BinaryOperatorKind.Addition || op == BinaryOperatorKind.Subtraction)
            {
                if (signature.LeftType.IsObjectType() && signature.RightType.IsStringType())
                {
                    containerName = rightName;
                }
                else if ((leftType.IsEnumType() || leftType.IsPointerType()) && (rightType.IsIntegralType() || rightType.IsCharType()))
                {
                    containerName = leftType.ToTestDisplayString();
                    leftName = containerName;
                    returnName = containerName;
                }
                else if ((rightType.IsEnumType() || rightType.IsPointerType()) && (leftType.IsIntegralType() || leftType.IsCharType()))
                {
                    containerName = rightType.ToTestDisplayString();
                    rightName = containerName;
                    returnName = containerName;
                }
            }

            Assert.Equal(isDynamic, signature.ReturnType.IsDynamic());

            string expectedSymbol = String.Format("{4} {0}.{2}({1} left, {3} right)",
                                       containerName,
                                       leftName,
                                       OperatorFacts.BinaryOperatorNameFromOperatorKind(op),
                                       rightName,
                                       returnName);
            string actualSymbol = symbol1.ToTestDisplayString();

            Assert.Equal(expectedSymbol, actualSymbol);

            Assert.Equal(MethodKind.BuiltinOperator, symbol1.MethodKind);
            Assert.True(symbol1.IsImplicitlyDeclared);

            bool isChecked;

            switch (op)
            {
                case BinaryOperatorKind.Multiplication:
                case BinaryOperatorKind.Addition:
                case BinaryOperatorKind.Subtraction:
                case BinaryOperatorKind.Division:
                    isChecked = isDynamic || symbol1.ContainingSymbol.Kind == SymbolKind.PointerType || symbol1.ContainingType.EnumUnderlyingType().SpecialType.IsIntegralType();
                    break;

                default:
                    isChecked = isDynamic;
                    break;
            }

            Assert.Equal(isChecked, symbol1.IsCheckedBuiltin);

            Assert.False(symbol1.IsGenericMethod);
            Assert.False(symbol1.IsExtensionMethod);
            Assert.False(symbol1.IsExtern);
            Assert.False(symbol1.CanBeReferencedByName);
            Assert.Null(symbol1.DeclaringCompilation);
            Assert.Equal(symbol1.Name, symbol1.MetadataName);

            Assert.True(symbol1.ContainingSymbol == symbol1.Parameters[0].Type || symbol1.ContainingSymbol == symbol1.Parameters[1].Type);

            int match = 0;
            if (symbol1.ContainingSymbol == symbol1.ReturnType)
            {
                match++;
            }

            if (symbol1.ContainingSymbol == symbol1.Parameters[0].Type)
            {
                match++;
            }

            if (symbol1.ContainingSymbol == symbol1.Parameters[1].Type)
            {
                match++;
            }

            Assert.True(match >= 2);

            Assert.Equal(0, symbol1.Locations.Length);
            Assert.Null(symbol1.GetDocumentationCommentId());
            Assert.Equal("", symbol1.GetDocumentationCommentXml());

            Assert.True(symbol1.HasSpecialName);
            Assert.True(symbol1.IsStatic);
            Assert.Equal(Accessibility.Public, symbol1.DeclaredAccessibility);
            Assert.False(symbol1.HidesBaseMethodsByName);
            Assert.False(symbol1.IsOverride);
            Assert.False(symbol1.IsVirtual);
            Assert.False(symbol1.IsAbstract);
            Assert.False(symbol1.IsSealed);
            Assert.Equal(2, symbol1.ParameterCount);
            Assert.Equal(0, symbol1.Parameters[0].Ordinal);
            Assert.Equal(1, symbol1.Parameters[1].Ordinal);

            var otherSymbol = (MethodSymbol)semanticModel.GetSymbolInfo(node1).Symbol;
            Assert.Equal(symbol1, otherSymbol);

            if (leftType.IsValueType && !leftType.IsPointerType())
            {
                if (rightType.IsValueType && !rightType.IsPointerType())
                {
                    Assert.Equal(symbol1, symbol2);
                    Assert.Equal(symbol1, symbol3);
                    Assert.Equal(symbol1, symbol4);
                    return;
                }
                else
                {
                    Assert.Null(symbol2);

                    if (rightType.IsDynamic())
                    {
                        Assert.NotEqual(symbol1, symbol3);
                    }
                    else
                    {
                        Assert.Equal(rightType.IsPointerType() ? null : symbol1, symbol3);
                    }

                    Assert.Null(symbol4);
                    return;
                }
            }
            else if (rightType.IsValueType && !rightType.IsPointerType())
            {
                Assert.Null(symbol2);
                Assert.Null(symbol3);

                if (leftType.IsDynamic())
                {
                    Assert.NotEqual(symbol1, symbol4);
                }
                else
                {
                    Assert.Equal(leftType.IsPointerType() ? null : symbol1, symbol4);
                }

                return;
            }

            Assert.Null(symbol2);

            if (rightType.IsDynamic())
            {
                Assert.NotEqual(symbol1, symbol3);
            }
            else
            {
                Assert.Null(symbol3);
            }

            if (leftType.IsDynamic())
            {
                Assert.NotEqual(symbol1, symbol4);
            }
            else
            {
                Assert.Null(symbol4);
            }
        }
Example #6
0
        private TypeSymbol Better(TypeSymbol type1, TypeSymbol type2, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            // Anything is better than an error sym.
            if (type1.IsErrorType())
            {
                return type2;
            }

            if ((object)type2 == null || type2.IsErrorType())
            {
                return type1;
            }

            var t1tot2 = _conversions.ClassifyImplicitConversion(type1, type2, ref useSiteDiagnostics).Exists;
            var t2tot1 = _conversions.ClassifyImplicitConversion(type2, type1, ref useSiteDiagnostics).Exists;

            if (t1tot2 && t2tot1)
            {
                if (type1.IsDynamic())
                {
                    return type1;
                }

                if (type2.IsDynamic())
                {
                    return type2;
                }

                return null;
            }

            if (t1tot2)
            {
                return type2;
            }

            if (t2tot1)
            {
                return type1;
            }

            return null;
        }
            /// <summary>
            /// Return the side-effect expression corresponding to an evaluation.
            /// </summary>
            protected BoundExpression LowerEvaluation(BoundDagEvaluation evaluation)
            {
                BoundExpression input = _tempAllocator.GetTemp(evaluation.Input);

                switch (evaluation)
                {
                case BoundDagFieldEvaluation f:
                {
                    FieldSymbol     field      = f.Field;
                    var             outputTemp = new BoundDagTemp(f.Syntax, field.Type, f, index: 0);
                    BoundExpression output     = _tempAllocator.GetTemp(outputTemp);
                    BoundExpression access     = _localRewriter.MakeFieldAccess(f.Syntax, input, field, null, LookupResultKind.Viable, field.Type);
                    access.WasCompilerGenerated = true;
                    return(_factory.AssignmentExpression(output, access));
                }

                case BoundDagPropertyEvaluation p:
                {
                    PropertySymbol  property   = p.Property;
                    var             outputTemp = new BoundDagTemp(p.Syntax, property.Type, p, index: 0);
                    BoundExpression output     = _tempAllocator.GetTemp(outputTemp);
                    return(_factory.AssignmentExpression(output, _factory.Property(input, property)));
                }

                case BoundDagDeconstructEvaluation d:
                {
                    MethodSymbol method         = d.DeconstructMethod;
                    var          refKindBuilder = ArrayBuilder <RefKind> .GetInstance();

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

                    BoundExpression receiver;
                    void addArg(RefKind refKind, BoundExpression expression)
                    {
                        refKindBuilder.Add(refKind);
                        argBuilder.Add(expression);
                    }

                    Debug.Assert(method.Name == WellKnownMemberNames.DeconstructMethodName);
                    int extensionExtra;
                    if (method.IsStatic)
                    {
                        Debug.Assert(method.IsExtensionMethod);
                        receiver = _factory.Type(method.ContainingType);
                        addArg(method.ParameterRefKinds[0], input);
                        extensionExtra = 1;
                    }
                    else
                    {
                        receiver       = input;
                        extensionExtra = 0;
                    }

                    for (int i = extensionExtra; i < method.ParameterCount; i++)
                    {
                        ParameterSymbol parameter = method.Parameters[i];
                        Debug.Assert(parameter.RefKind == RefKind.Out);
                        var outputTemp = new BoundDagTemp(d.Syntax, parameter.Type, d, i - extensionExtra);
                        addArg(RefKind.Out, _tempAllocator.GetTemp(outputTemp));
                    }

                    return(_factory.Call(receiver, method, refKindBuilder.ToImmutableAndFree(), argBuilder.ToImmutableAndFree()));
                }

                case BoundDagTypeEvaluation t:
                {
                    TypeSymbol inputType = input.Type;
                    if (inputType.IsDynamic() || inputType.ContainsTypeParameter())
                    {
                        inputType = _factory.SpecialType(SpecialType.System_Object);
                    }

                    TypeSymbol               type               = t.Type;
                    var                      outputTemp         = new BoundDagTemp(t.Syntax, type, t, index: 0);
                    BoundExpression          output             = _tempAllocator.GetTemp(outputTemp);
                    HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                    Conversion               conversion         = _factory.Compilation.Conversions.ClassifyBuiltInConversion(inputType, output.Type, ref useSiteDiagnostics);
                    _localRewriter._diagnostics.Add(t.Syntax, useSiteDiagnostics);
                    BoundExpression evaluated;
                    if (conversion.Exists)
                    {
                        if (conversion.Kind == ConversionKind.ExplicitNullable &&
                            inputType.GetNullableUnderlyingType().Equals(output.Type, TypeCompareKind.AllIgnoreOptions) &&
                            _localRewriter.TryGetNullableMethod(t.Syntax, inputType, SpecialMember.System_Nullable_T_GetValueOrDefault, out MethodSymbol getValueOrDefault))
                        {
                            // As a special case, since the null test has already been done we can use Nullable<T>.GetValueOrDefault
                            evaluated = _factory.Call(input, getValueOrDefault);
                        }
                        else
                        {
                            evaluated = _factory.Convert(type, input, conversion);
                        }
                    }
                    else
                    {
                        evaluated = _factory.As(input, type);
                    }

                    return(_factory.AssignmentExpression(output, evaluated));
                }

                case BoundDagIndexEvaluation e:
                {
                    // This is an evaluation of an indexed property with a constant int value.
                    // The input type must be ITuple, and the property must be a property of ITuple.
                    Debug.Assert(e.Property.ContainingSymbol.Equals(input.Type));
                    Debug.Assert(e.Property.GetMethod.ParameterCount == 1);
                    Debug.Assert(e.Property.GetMethod.Parameters[0].Type.SpecialType == SpecialType.System_Int32);
                    TypeSymbol      type       = e.Property.GetMethod.ReturnType;
                    var             outputTemp = new BoundDagTemp(e.Syntax, type, e, index: 0);
                    BoundExpression output     = _tempAllocator.GetTemp(outputTemp);
                    return(_factory.AssignmentExpression(output, _factory.Call(input, e.Property.GetMethod, _factory.Literal(e.Index))));
                }

                default:
                    throw ExceptionUtilities.UnexpectedValue(evaluation);
                }
            }
        /// <summary>
        /// Lower "using [await] (expression) statement" to a try-finally block.
        /// </summary>
        private BoundBlock RewriteExpressionUsingStatement(BoundUsingStatement node, BoundBlock tryBlock)
        {
            Debug.Assert(node.ExpressionOpt != null);
            Debug.Assert(node.DeclarationsOpt == null);

            // See comments in BuildUsingTryFinally for the details of the lowering to try-finally.
            //
            // SPEC: A using statement of the form "using (expression) statement; " has the
            // SPEC: same three possible expansions [ as "using (ResourceType r = expression) statement; ]
            // SPEC: but in this case ResourceType is implicitly the compile-time type of the expression,
            // SPEC: and the resource variable is inaccessible to and invisible to the embedded statement.
            //
            // DELIBERATE SPEC VIOLATION:
            //
            // The spec quote above implies that the expression must have a type; in fact we allow
            // the expression to be null.
            //
            // If expr is the constant null then we can elide the whole thing and simply generate the statement.

            BoundExpression rewrittenExpression = (BoundExpression)Visit(node.ExpressionOpt);

            if (rewrittenExpression.ConstantValue == ConstantValue.Null)
            {
                Debug.Assert(node.Locals.IsEmpty); // TODO: This might not be a valid assumption in presence of semicolon operator.
                return(tryBlock);
            }

            // Otherwise, we lower "using(expression) statement;" as follows:
            //
            // * If the expression is of type dynamic then we lower as though the user had written
            //
            //   using(IDisposable temp = (IDisposable)expression) statement;
            //
            //   Note that we have to do the conversion early, not in the finally block, because
            //   if the conversion fails at runtime with an exception then the exception must happen
            //   before the statement runs.
            //
            // * Otherwise we lower as though the user had written
            //
            //   using(ResourceType temp = expression) statement;
            //

            TypeSymbol           expressionType   = rewrittenExpression.Type;
            SyntaxNode           expressionSyntax = rewrittenExpression.Syntax;
            UsingStatementSyntax usingSyntax      = (UsingStatementSyntax)node.Syntax;

            BoundAssignmentOperator tempAssignment;
            BoundLocal boundTemp;

            if ((object)expressionType == null || expressionType.IsDynamic())
            {
                // IDisposable temp = (IDisposable) expr;
                // or
                // IAsyncDisposable temp = (IAsyncDisposable) expr;
                TypeSymbol iDisposableType = node.AwaitOpt is null?
                                             _compilation.GetSpecialType(SpecialType.System_IDisposable) :
                                                 _compilation.GetWellKnownType(WellKnownType.System_IAsyncDisposable);

                BoundExpression tempInit = MakeConversionNode(
                    expressionSyntax,
                    rewrittenExpression,
                    Conversion.GetTrivialConversion(node.IDisposableConversion.Kind),
                    iDisposableType,
                    @checked: false,
                    constantValueOpt: rewrittenExpression.ConstantValue);

                boundTemp = _factory.StoreToTemp(tempInit, out tempAssignment, kind: SynthesizedLocalKind.Using);
            }
            else
            {
                // ResourceType temp = expr;
                boundTemp = _factory.StoreToTemp(rewrittenExpression, out tempAssignment, syntaxOpt: usingSyntax, kind: SynthesizedLocalKind.Using);
            }

            BoundStatement expressionStatement = new BoundExpressionStatement(expressionSyntax, tempAssignment);

            if (this.Instrument)
            {
                expressionStatement = _instrumenter.InstrumentUsingTargetCapture(node, expressionStatement);
            }

            BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundTemp, usingSyntax.AwaitKeyword, node.AwaitOpt);

            // { ResourceType temp = expr; try { ... } finally { ... } }
            return(new BoundBlock(
                       syntax: usingSyntax,
                       locals: node.Locals.Add(boundTemp.LocalSymbol),
                       statements: ImmutableArray.Create <BoundStatement>(expressionStatement, tryFinally)));
        }
Example #9
0
        /// <summary>
        /// Check that the pattern type is valid for the operand. Return true if an error was reported.
        /// </summary>
        private bool CheckValidPatternType(
            CSharpSyntaxNode typeSyntax,
            BoundExpression operand,
            TypeSymbol operandType,
            TypeSymbol patternType,
            bool patternTypeWasInSource,
            bool isVar,
            DiagnosticBag diagnostics)
        {
            Debug.Assert((object)operandType != null);
            Debug.Assert((object)patternType != null);

            // Because we do not support recursive patterns, we always have an operand
            Debug.Assert((object)operand != null);
            // Once we support recursive patterns that will be relaxed.

            if (operandType.IsErrorType() || patternType.IsErrorType())
            {
                return(false);
            }
            else if (patternType.IsNullableType() && !isVar && patternTypeWasInSource)
            {
                // It is an error to use pattern-matching with a nullable type, because you'll never get null. Use the underlying type.
                Error(diagnostics, ErrorCode.ERR_PatternNullableType, typeSyntax, patternType, patternType.GetNullableUnderlyingType());
                return(true);
            }
            else if (patternType.IsStatic)
            {
                Error(diagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, patternType);
                return(true);
            }
            else if (!isVar)
            {
                if (patternType.IsDynamic())
                {
                    Error(diagnostics, ErrorCode.ERR_PatternDynamicType, typeSyntax);
                    return(true);
                }

                HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                Conversion conversion =
                    operand != null
                    ? this.Conversions.ClassifyConversionFromExpression(operand, patternType, ref useSiteDiagnostics, forCast : true)
                    : this.Conversions.ClassifyConversionFromType(operandType, patternType, ref useSiteDiagnostics, forCast: true);

                diagnostics.Add(typeSyntax, useSiteDiagnostics);
                switch (conversion.Kind)
                {
                case ConversionKind.ExplicitDynamic:
                case ConversionKind.ImplicitDynamic:
                // Since the input was `dynamic`, which is equivalent to `object`, there must also
                // exist some unboxing, identity, or reference conversion as well, making the conversion legal.
                case ConversionKind.Boxing:
                case ConversionKind.ExplicitNullable:
                case ConversionKind.ExplicitReference:
                case ConversionKind.Identity:
                case ConversionKind.ImplicitReference:
                case ConversionKind.Unboxing:
                case ConversionKind.ImplicitNullable:
                    // these are the conversions allowed by a pattern match
                    break;

                case ConversionKind.DefaultOrNullLiteral:
                    throw ExceptionUtilities.UnexpectedValue(conversion.Kind);

                //case ConversionKind.ExplicitNumeric:  // we do not perform numeric conversions of the operand
                //case ConversionKind.ImplicitConstant:
                //case ConversionKind.ImplicitNumeric:
                default:
                    if (operandType.ContainsTypeParameter() || patternType.ContainsTypeParameter())
                    {
                        LanguageVersion requiredVersion = MessageID.IDS_FeatureGenericPatternMatching.RequiredVersion();
                        if (requiredVersion > Compilation.LanguageVersion)
                        {
                            Error(diagnostics, ErrorCode.ERR_PatternWrongGenericTypeInVersion, typeSyntax,
                                  operandType, patternType,
                                  Compilation.LanguageVersion.ToDisplayString(),
                                  new CSharpRequiredLanguageVersion(requiredVersion));
                            return(true);
                        }

                        // permit pattern-matching when one of the types is an open type in C# 7.1.
                        break;
                    }
                    else
                    {
                        Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, operandType, patternType);
                        return(true);
                    }
                }
            }

            return(false);
        }
Example #10
0
        /// <summary>
        /// Check that the pattern type is valid for the operand. Return true if an error was reported.
        /// </summary>
        private bool CheckValidPatternType(
            CSharpSyntaxNode typeSyntax,
            TypeSymbol operandType,
            TypeSymbol patternType,
            bool patternTypeWasInSource,
            bool isVar,
            DiagnosticBag diagnostics)
        {
            Debug.Assert((object)operandType != null);
            Debug.Assert((object)patternType != null);

            if (operandType.IsErrorType() || patternType.IsErrorType())
            {
                return(false);
            }
            else if (patternType.IsNullableType() && !isVar && patternTypeWasInSource)
            {
                // It is an error to use pattern-matching with a nullable type, because you'll never get null. Use the underlying type.
                Error(diagnostics, ErrorCode.ERR_PatternNullableType, typeSyntax, patternType, patternType.GetNullableUnderlyingType());
                return(true);
            }
            else if (patternType.IsStatic)
            {
                Error(diagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, patternType);
                return(true);
            }
            else if (!isVar)
            {
                if (patternType.IsDynamic())
                {
                    Error(diagnostics, ErrorCode.ERR_PatternDynamicType, typeSyntax);
                    return(true);
                }

                HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                var matchPossible = ExpressionOfTypeMatchesPatternType(Conversions, operandType, patternType, ref useSiteDiagnostics, out Conversion conversion, operandConstantValue: null, operandCouldBeNull: true);
                diagnostics.Add(typeSyntax, useSiteDiagnostics);
                if (matchPossible != false)
                {
                    if (!conversion.Exists && (operandType.ContainsTypeParameter() || patternType.ContainsTypeParameter()))
                    {
                        // permit pattern-matching when one of the types is an open type in C# 7.1.
                        LanguageVersion requiredVersion = MessageID.IDS_FeatureGenericPatternMatching.RequiredVersion();
                        if (requiredVersion > Compilation.LanguageVersion)
                        {
                            Error(diagnostics, ErrorCode.ERR_PatternWrongGenericTypeInVersion, typeSyntax,
                                  operandType, patternType,
                                  Compilation.LanguageVersion.ToDisplayString(),
                                  new CSharpRequiredLanguageVersion(requiredVersion));
                            return(true);
                        }
                    }
                }
                else
                {
                    Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, operandType, patternType);
                    return(true);
                }
            }

            return(false);
        }
Example #11
0
        /// <summary>
        /// Returns the better type amongst the two, with some possible modifications (dynamic/object or tuple names).
        /// </summary>
        private static TypeSymbol Better(
            TypeSymbol type1,
            TypeSymbol type2,
            ConversionsBase conversions,
            out bool hadNullabilityMismatch,
            ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            hadNullabilityMismatch = false;

            // Anything is better than an error sym.
            if (type1.IsErrorType())
            {
                return(type2);
            }

            if ((object)type2 == null || type2.IsErrorType())
            {
                return(type1);
            }

            var conversionsWithoutNullability = conversions.WithNullability(false);
            var t1tot2 = conversionsWithoutNullability.ClassifyImplicitConversionFromType(type1, type2, ref useSiteDiagnostics).Exists;
            var t2tot1 = conversionsWithoutNullability.ClassifyImplicitConversionFromType(type2, type1, ref useSiteDiagnostics).Exists;

            if (t1tot2 && t2tot1)
            {
                if (type1.IsDynamic())
                {
                    return(type1);
                }

                if (type2.IsDynamic())
                {
                    return(type2);
                }

                if (type1.Equals(type2, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes))
                {
                    return(MethodTypeInferrer.Merge(
                               TypeSymbolWithAnnotations.Create(type1),
                               TypeSymbolWithAnnotations.Create(type2),
                               VarianceKind.Out,
                               conversions,
                               out hadNullabilityMismatch).TypeSymbol);
                }

                return(null);
            }

            if (t1tot2)
            {
                return(type2);
            }

            if (t2tot1)
            {
                return(type1);
            }

            return(null);
        }
Example #12
0
        private BoundExpression MakeUnaryOperator(
            BoundUnaryOperator oldNode,
            UnaryOperatorKind kind,
            CSharpSyntaxNode syntax,
            MethodSymbol method,
            BoundExpression loweredOperand,
            TypeSymbol type)
        {
            if (kind.IsDynamic())
            {
                Debug.Assert(kind == UnaryOperatorKind.DynamicTrue && type.SpecialType == SpecialType.System_Boolean || type.IsDynamic());
                Debug.Assert((object)method == null);

                // Logical operators on boxed Boolean constants:
                var constant = UnboxConstant(loweredOperand);
                if (constant == ConstantValue.True || constant == ConstantValue.False)
                {
                    if (kind == UnaryOperatorKind.DynamicTrue)
                    {
                        return(_factory.Literal(constant.BooleanValue));
                    }
                    else if (kind == UnaryOperatorKind.DynamicLogicalNegation)
                    {
                        return(MakeConversionNode(_factory.Literal(!constant.BooleanValue), type, @checked: false));
                    }
                }

                return(_dynamicFactory.MakeDynamicUnaryOperator(kind, loweredOperand, type).ToExpression());
            }
            else if (kind.IsLifted())
            {
                if (!_inExpressionLambda)
                {
                    return(LowerLiftedUnaryOperator(kind, syntax, method, loweredOperand, type));
                }
            }
            else if (kind.IsUserDefined())
            {
                Debug.Assert((object)method != null);
                Debug.Assert(type == method.ReturnType);
                if (!_inExpressionLambda || kind == UnaryOperatorKind.UserDefinedTrue || kind == UnaryOperatorKind.UserDefinedFalse)
                {
                    // @t-mawind
                    //   As usual, concept accesses need to be rewritten down to their
                    //   default() form.
                    // Is this correct?  It's mostly a copy over from the binary case,
                    // but the unary case is different enough to make me nervous.
                    if (method is SynthesizedWitnessMethodSymbol)
                    {
                        return(BoundCall.Synthesized(syntax, SynthesizeWitnessInvocationReceiver(syntax, ((SynthesizedWitnessMethodSymbol)method).Parent), method, loweredOperand));
                    }

                    return(BoundCall.Synthesized(syntax, null, method, loweredOperand));
                }
            }
            else if (kind.Operator() == UnaryOperatorKind.UnaryPlus)
            {
                // We do not call the operator even for decimal; we simply optimize it away entirely.
                return(loweredOperand);
            }

            if (kind == UnaryOperatorKind.EnumBitwiseComplement)
            {
                var underlyingType       = loweredOperand.Type.GetEnumUnderlyingType();
                var upconvertSpecialType = Binder.GetEnumPromotedType(underlyingType.SpecialType);
                var upconvertType        = upconvertSpecialType == underlyingType.SpecialType ?
                                           underlyingType :
                                           _compilation.GetSpecialType(upconvertSpecialType);


                var newOperand            = MakeConversionNode(loweredOperand, upconvertType, false);
                UnaryOperatorKind newKind = kind.Operator().WithType(upconvertSpecialType);

                var newNode = (oldNode != null) ?
                              oldNode.Update(
                    newKind,
                    newOperand,
                    oldNode.ConstantValueOpt,
                    method,
                    newOperand.ResultKind,
                    upconvertType) :
                              new BoundUnaryOperator(
                    syntax,
                    newKind,
                    newOperand,
                    null,
                    method,
                    LookupResultKind.Viable,
                    upconvertType);

                return(MakeConversionNode(newNode.Syntax, newNode, Conversion.ExplicitEnumeration, type, @checked: false));
            }

            if (kind == UnaryOperatorKind.DecimalUnaryMinus)
            {
                method = (MethodSymbol)_compilation.Assembly.GetSpecialTypeMember(SpecialMember.System_Decimal__op_UnaryNegation);
                if (!_inExpressionLambda)
                {
                    return(BoundCall.Synthesized(syntax, null, method, loweredOperand));
                }
            }

            return((oldNode != null) ?
                   oldNode.Update(kind, loweredOperand, oldNode.ConstantValueOpt, method, oldNode.ResultKind, type) :
                   new BoundUnaryOperator(syntax, kind, loweredOperand, null, method, LookupResultKind.Viable, type));
        }
Example #13
0
        internal static BoundStatement BindUsingStatementOrDeclarationFromParts(SyntaxNode syntax, SyntaxToken usingKeyword, SyntaxToken awaitKeyword, Binder originalBinder, UsingStatementBinder usingBinderOpt, DiagnosticBag diagnostics)
        {
            bool isUsingDeclaration = syntax.Kind() == SyntaxKind.LocalDeclarationStatement;
            bool isExpression       = !isUsingDeclaration && syntax.Kind() != SyntaxKind.VariableDeclaration;
            bool hasAwait           = awaitKeyword != default;

            Debug.Assert(isUsingDeclaration || usingBinderOpt != null);

            TypeSymbol disposableInterface = getDisposableInterface(hasAwait);

            Debug.Assert((object)disposableInterface != null);
            bool hasErrors = ReportUseSiteDiagnostics(disposableInterface, diagnostics, hasAwait ? awaitKeyword : usingKeyword);

            Conversion iDisposableConversion;
            ImmutableArray <BoundLocalDeclaration> declarationsOpt         = default;
            BoundMultipleLocalDeclarations         multipleDeclarationsOpt = null;
            BoundExpression expressionOpt      = null;
            TypeSymbol      declarationTypeOpt = null;
            MethodSymbol    disposeMethodOpt;
            TypeSymbol      awaitableTypeOpt;

            if (isExpression)
            {
                expressionOpt = usingBinderOpt.BindTargetExpression(diagnostics, originalBinder);
                hasErrors    |= !populateDisposableConversionOrDisposeMethod(fromExpression: true, out iDisposableConversion, out disposeMethodOpt, out awaitableTypeOpt);
            }
            else
            {
                VariableDeclarationSyntax declarationSyntax = isUsingDeclaration ? ((LocalDeclarationStatementSyntax)syntax).Declaration : (VariableDeclarationSyntax)syntax;
                originalBinder.BindForOrUsingOrFixedDeclarations(declarationSyntax, LocalDeclarationKind.UsingVariable, diagnostics, out declarationsOpt);

                Debug.Assert(!declarationsOpt.IsEmpty);
                multipleDeclarationsOpt = new BoundMultipleLocalDeclarations(declarationSyntax, declarationsOpt);
                declarationTypeOpt      = declarationsOpt[0].DeclaredTypeOpt.Type;

                if (declarationTypeOpt.IsDynamic())
                {
                    iDisposableConversion = Conversion.ImplicitDynamic;
                    disposeMethodOpt      = null;
                    awaitableTypeOpt      = null;
                }
                else
                {
                    hasErrors |= !populateDisposableConversionOrDisposeMethod(fromExpression: false, out iDisposableConversion, out disposeMethodOpt, out awaitableTypeOpt);
                }
            }

            BoundAwaitableInfo awaitOpt = null;

            if (hasAwait)
            {
                // even if we don't have a proper value to await, we'll still report bad usages of `await`
                originalBinder.ReportBadAwaitDiagnostics(syntax, awaitKeyword.GetLocation(), diagnostics, ref hasErrors);

                if (awaitableTypeOpt is null)
                {
                    awaitOpt = new BoundAwaitableInfo(syntax, awaitableInstancePlaceholder: null, isDynamic: true, getAwaiter: null, isCompleted: null, getResult: null)
                    {
                        WasCompilerGenerated = true
                    };
                }
                else
                {
                    hasErrors |= ReportUseSiteDiagnostics(awaitableTypeOpt, diagnostics, awaitKeyword);
                    var placeholder = new BoundAwaitableValuePlaceholder(syntax, valEscape: originalBinder.LocalScopeDepth, awaitableTypeOpt).MakeCompilerGenerated();
                    awaitOpt = originalBinder.BindAwaitInfo(placeholder, syntax, diagnostics, ref hasErrors);
                }
            }

            // This is not awesome, but its factored.
            // In the future it might be better to have a separate shared type that we add the info to, and have the callers create the appropriate bound nodes from it
            if (isUsingDeclaration)
            {
                return(new BoundUsingLocalDeclarations(syntax, disposeMethodOpt, iDisposableConversion, awaitOpt, declarationsOpt, hasErrors));
            }
            else
            {
                BoundStatement boundBody = originalBinder.BindPossibleEmbeddedStatement(usingBinderOpt._syntax.Statement, diagnostics);

                return(new BoundUsingStatement(
                           usingBinderOpt._syntax,
                           usingBinderOpt.Locals,
                           multipleDeclarationsOpt,
                           expressionOpt,
                           iDisposableConversion,
                           boundBody,
                           awaitOpt,
                           disposeMethodOpt,
                           hasErrors));
            }

            bool populateDisposableConversionOrDisposeMethod(bool fromExpression, out Conversion iDisposableConversion, out MethodSymbol disposeMethodOpt, out TypeSymbol awaitableTypeOpt)
            {
                HashSet <DiagnosticInfo> useSiteDiagnostics = null;

                iDisposableConversion = classifyConversion(fromExpression, disposableInterface, ref useSiteDiagnostics);
                disposeMethodOpt      = null;
                awaitableTypeOpt      = null;

                diagnostics.Add(syntax, useSiteDiagnostics);

                if (iDisposableConversion.IsImplicit)
                {
                    if (hasAwait)
                    {
                        awaitableTypeOpt = originalBinder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_ValueTask);
                    }
                    return(true);
                }

                TypeSymbol type = fromExpression ? expressionOpt.Type : declarationTypeOpt;

                // If this is a ref struct, or we're in a valid asynchronous using, try binding via pattern.
                // We won't need to try and bind a second time if it fails, as async dispose can't be pattern based (ref structs are not allowed in async methods)
                if (type is object && (type.IsRefLikeType || hasAwait))
                {
                    BoundExpression receiver = fromExpression
                                               ? expressionOpt
                                               : new BoundLocal(syntax, declarationsOpt[0].LocalSymbol, null, type)
                    {
                        WasCompilerGenerated = true
                    };

                    DiagnosticBag patternDiagnostics = originalBinder.Compilation.IsFeatureEnabled(MessageID.IDS_FeatureUsingDeclarations)
                                                       ? diagnostics
                                                       : new DiagnosticBag();
                    disposeMethodOpt = originalBinder.TryFindDisposePatternMethod(receiver, syntax, hasAwait, patternDiagnostics);
                    if (disposeMethodOpt is object)
                    {
                        MessageID.IDS_FeatureUsingDeclarations.CheckFeatureAvailability(diagnostics, originalBinder.Compilation, syntax.Location);
                        if (hasAwait)
                        {
                            awaitableTypeOpt = disposeMethodOpt.ReturnType;
                        }
                        return(true);
                    }
                }

                if (type is null || !type.IsErrorType())
                {
                    // Retry with a different assumption about whether the `using` is async
                    TypeSymbol alternateInterface    = getDisposableInterface(!hasAwait);
                    HashSet <DiagnosticInfo> ignored = null;
                    Conversion alternateConversion   = classifyConversion(fromExpression, alternateInterface, ref ignored);

                    bool      wrongAsync = alternateConversion.IsImplicit;
                    ErrorCode errorCode  = wrongAsync
                        ? (hasAwait ? ErrorCode.ERR_NoConvToIAsyncDispWrongAsync : ErrorCode.ERR_NoConvToIDispWrongAsync)
                        : (hasAwait ? ErrorCode.ERR_NoConvToIAsyncDisp : ErrorCode.ERR_NoConvToIDisp);

                    Error(diagnostics, errorCode, syntax, declarationTypeOpt ?? expressionOpt.Display);
                }

                return(false);
            }

            Conversion classifyConversion(bool fromExpression, TypeSymbol targetInterface, ref HashSet <DiagnosticInfo> diag)
            {
                return(fromExpression ?
                       originalBinder.Conversions.ClassifyImplicitConversionFromExpression(expressionOpt, targetInterface, ref diag) :
                       originalBinder.Conversions.ClassifyImplicitConversionFromType(declarationTypeOpt, targetInterface, ref diag));
            }

            TypeSymbol getDisposableInterface(bool isAsync)
            {
                return(isAsync
                    ? originalBinder.Compilation.GetWellKnownType(WellKnownType.System_IAsyncDisposable)
                    : originalBinder.Compilation.GetSpecialType(SpecialType.System_IDisposable));
            }
        }
Example #14
0
        private BoundExpression GetDefaultParameterValue(CSharpSyntaxNode syntax, ParameterSymbol parameter)
        {
            TypeSymbol      parameterType        = parameter.Type;
            ConstantValue   defaultConstantValue = parameter.ExplicitDefaultConstantValue;
            BoundExpression defaultValue;

            SourceLocation callerSourceLocation;

            if (parameter.IsCallerLineNumber && ((callerSourceLocation = GetCallerLocation(syntax)) != null))
            {
                int             line        = callerSourceLocation.SourceTree.GetDisplayLineNumber(callerSourceLocation.SourceSpan);
                BoundExpression lineLiteral = MakeLiteral(syntax, ConstantValue.Create(line), compilation.GetSpecialType(SpecialType.System_Int32));

                if (parameterType.IsNullableType())
                {
                    defaultValue = MakeConversion(lineLiteral, parameterType.GetNullableUnderlyingType(), false);

                    // wrap it in a nullable ctor.
                    defaultValue = new BoundObjectCreationExpression(
                        syntax,
                        GetNullableMethod(syntax, parameterType, SpecialMember.System_Nullable_T__ctor),
                        defaultValue);
                }
                else
                {
                    defaultValue = MakeConversion(lineLiteral, parameterType, false);
                }
            }
            else if (parameter.IsCallerFilePath && ((callerSourceLocation = GetCallerLocation(syntax)) != null))
            {
                string          path = callerSourceLocation.SourceTree.GetDisplayPath(callerSourceLocation.SourceSpan, compilation.Options.SourceReferenceResolver);
                BoundExpression memberNameLiteral = MakeLiteral(syntax, ConstantValue.Create(path), compilation.GetSpecialType(SpecialType.System_String));
                defaultValue = MakeConversion(memberNameLiteral, parameterType, false);
            }
            else if (parameter.IsCallerMemberName && ((callerSourceLocation = GetCallerLocation(syntax)) != null))
            {
                string memberName = this.factory.TopLevelMethod.GetMemberCallerName();

                BoundExpression memberNameLiteral = MakeLiteral(syntax, ConstantValue.Create(memberName), compilation.GetSpecialType(SpecialType.System_String));
                defaultValue = MakeConversion(memberNameLiteral, parameterType, false);
            }
            else if (defaultConstantValue == ConstantValue.NotAvailable)
            {
                // There is no constant value given for the parameter in source/metadata.
                if (parameterType.IsDynamic() || parameterType.SpecialType == SpecialType.System_Object)
                {
                    // We have something like M([Optional] object x). We have special handling for such situations.
                    defaultValue = GetDefaultParameterSpecial(syntax, parameter);
                }
                else
                {
                    // The argument to M([Optional] int x) becomes default(int)
                    defaultValue = new BoundDefaultOperator(syntax, parameterType);
                }
            }
            else if (defaultConstantValue.IsNull && parameterType.IsValueType)
            {
                // We have something like M(int? x = null) or M(S x = default(S)),
                // so replace the argument with default(int?).
                defaultValue = new BoundDefaultOperator(syntax, parameterType);
            }
            else if (parameterType.IsNullableType())
            {
                // We have something like M(double? x = 1.23), so replace the argument
                // with new double?(1.23).

                TypeSymbol constantType = compilation.GetSpecialType(defaultConstantValue.SpecialType);
                defaultValue = MakeLiteral(syntax, defaultConstantValue, constantType);

                // The parameter's underlying type might not match the constant type. For example, we might have
                // a default value of 5 (an integer) but a parameter type of decimal?.

                defaultValue = MakeConversion(defaultValue, parameterType.GetNullableUnderlyingType(), @checked: false, acceptFailingConversion: true);

                // Finally, wrap it in a nullable ctor.
                defaultValue = new BoundObjectCreationExpression(
                    syntax,
                    GetNullableMethod(syntax, parameterType, SpecialMember.System_Nullable_T__ctor),
                    defaultValue);
            }
            else if (defaultConstantValue.IsNull || defaultConstantValue.IsBad)
            {
                defaultValue = MakeLiteral(syntax, defaultConstantValue, parameterType);
            }
            else
            {
                // We have something like M(double = 1.23), so replace the argument with 1.23.

                TypeSymbol constantType = compilation.GetSpecialType(defaultConstantValue.SpecialType);
                defaultValue = MakeLiteral(syntax, defaultConstantValue, constantType);
                // The parameter type might not match the constant type.
                defaultValue = MakeConversion(defaultValue, parameterType, @checked: false, acceptFailingConversion: true);
            }

            return(defaultValue);
        }
        /// <summary>
        /// Lower "using (expression) statement" to a try-finally block.
        /// </summary>
        private BoundBlock RewriteExpressionUsingStatement(BoundUsingStatement node, BoundBlock tryBlock)
        {
            Debug.Assert(node.ExpressionOpt != null);
            Debug.Assert(node.DeclarationsOpt == null);

            // See comments in BuildUsingTryFinally for the details of the lowering to try-finally.
            //
            // SPEC: A using statement of the form "using (expression) statement; " has the
            // SPEC: same three possible expansions [ as "using (ResourceType r = expression) statement; ]
            // SPEC: but in this case ResourceType is implicitly the compile-time type of the expression,
            // SPEC: and the resource variable is inaccessible to and invisible to the embedded statement.
            //
            // DELIBERATE SPEC VIOLATION:
            //
            // The spec quote above implies that the expression must have a type; in fact we allow
            // the expression to be null.
            //
            // If expr is the constant null then we can elide the whole thing and simply generate the statement.

            BoundExpression rewrittenExpression = (BoundExpression)Visit(node.ExpressionOpt);

            if (rewrittenExpression.ConstantValue == ConstantValue.Null)
            {
                return(tryBlock);
            }

            // Otherwise, we lower "using(expression) statement;" as follows:
            //
            // * If the expression is of type dynamic then we lower as though the user had written
            //
            //   using(IDisposable temp = (IDisposable)expression) statement;
            //
            //   Note that we have to do the conversion early, not in the finally block, because
            //   if the conversion fails at runtime with an exception then the exception must happen
            //   before the statement runs.
            //
            // * Otherwise we lower as though the user had written
            //
            //   using(ResourceType temp = expression) statement;
            //

            TypeSymbol           expressionType   = rewrittenExpression.Type;
            CSharpSyntaxNode     expressionSyntax = rewrittenExpression.Syntax;
            UsingStatementSyntax usingSyntax      = (UsingStatementSyntax)node.Syntax;

            BoundAssignmentOperator tempAssignment;
            BoundLocal boundTemp;

            if ((object)expressionType == null || expressionType.IsDynamic())
            {
                // IDisposable temp = (IDisposable) expr;
                BoundExpression tempInit = MakeConversion(
                    expressionSyntax,
                    rewrittenExpression,
                    node.IDisposableConversion.Kind,
                    this.compilation.GetSpecialType(SpecialType.System_IDisposable),
                    @checked: false,
                    constantValueOpt: rewrittenExpression.ConstantValue);

                boundTemp = this.factory.StoreToTemp(tempInit, out tempAssignment);
            }
            else
            {
                // ResourceType temp = expr;
                boundTemp = this.factory.StoreToTemp(rewrittenExpression, tempKind: TempKind.Using, store: out tempAssignment);
            }

            BoundStatement expressionStatement = new BoundExpressionStatement(expressionSyntax, tempAssignment);

            if (this.generateDebugInfo)
            {
                // NOTE: unlike in the assignment case, the sequence point is on the using keyword, not the expression.
                expressionStatement = new BoundSequencePointWithSpan(usingSyntax, expressionStatement, usingSyntax.UsingKeyword.Span);
            }

            BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundTemp);

            // { ResourceType temp = expr; try { ... } finally { ... } }
            return(new BoundBlock(
                       syntax: usingSyntax,
                       localsOpt: ImmutableArray.Create <LocalSymbol>(boundTemp.LocalSymbol),
                       statements: ImmutableArray.Create <BoundStatement>(expressionStatement, tryFinally)));
        }
Example #16
0
        /// <summary>
        /// If the extension method is applicable based on the "this" argument type, return
        /// the method constructed with the inferred type arguments. If the method is not an
        /// unconstructed generic method, type inference is skipped. If the method is not
        /// applicable, or if constraints when inferring type parameters from the "this" type
        /// are not satisfied, the return value is null.
        /// </summary>
        public static MethodSymbol InferExtensionMethodTypeArguments(this MethodSymbol method, TypeSymbol thisType, Compilation compilation, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert(method.IsExtensionMethod);
            Debug.Assert((object)thisType != null);

            if (!method.IsGenericMethod || method != method.ConstructedFrom)
            {
                return method;
            }

            // We never resolve extension methods on a dynamic receiver.
            if (thisType.IsDynamic())
            {
                return null;
            }

            var containingAssembly = method.ContainingAssembly;
            var errorNamespace = containingAssembly.GlobalNamespace;
            var conversions = new TypeConversions(containingAssembly.CorLibrary);

            // There is absolutely no plausible syntax/tree that we could use for these
            // synthesized literals.  We could be speculatively binding a call to a PE method.
            var syntaxTree = CSharpSyntaxTree.Dummy;
            var syntax = (CSharpSyntaxNode)syntaxTree.GetRoot();

            // Create an argument value for the "this" argument of specific type,
            // and pass the same bad argument value for all other arguments.
            var thisArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, thisType) { WasCompilerGenerated = true };
            var otherArgumentType = new ExtendedErrorTypeSymbol(errorNamespace, name: string.Empty, arity: 0, errorInfo: null, unreported: false);
            var otherArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, otherArgumentType) { WasCompilerGenerated = true };

            var paramCount = method.ParameterCount;
            var arguments = new BoundExpression[paramCount];
            var argumentTypes = new TypeSymbol[paramCount];
            for (int i = 0; i < paramCount; i++)
            {
                var argument = (i == 0) ? thisArgumentValue : otherArgumentValue;
                arguments[i] = argument;
                argumentTypes[i] = argument.Type;
            }

            var typeArgs = MethodTypeInferrer.InferTypeArgumentsFromFirstArgument(
                conversions,
                method,
                argumentTypes.AsImmutableOrNull(),
                arguments.AsImmutableOrNull(),
                ref useSiteDiagnostics);

            if (typeArgs.IsDefault)
            {
                return null;
            }

            int firstNullInTypeArgs = -1;

            // For the purpose of constraint checks we use error type symbol in place of type arguments that we couldn't infer from the first argument.
            // This prevents constraint checking from failing for corresponding type parameters. 
            var typeArgsForConstraintsCheck = typeArgs;
            for (int i = 0; i < typeArgsForConstraintsCheck.Length; i++)
            {
                if ((object)typeArgsForConstraintsCheck[i] == null)
                {
                    firstNullInTypeArgs = i;
                    var builder = ArrayBuilder<TypeSymbol>.GetInstance();
                    builder.AddRange(typeArgs, firstNullInTypeArgs);

                    for (; i < typeArgsForConstraintsCheck.Length; i++)
                    {
                        builder.Add(typeArgsForConstraintsCheck[i] ?? ErrorTypeSymbol.UnknownResultType);
                    }

                    typeArgsForConstraintsCheck = builder.ToImmutableAndFree();
                    break;
                }
            }

            // Check constraints.
            var diagnosticsBuilder = ArrayBuilder<TypeParameterDiagnosticInfo>.GetInstance();
            var typeParams = method.TypeParameters;
            var substitution = new TypeMap(typeParams, typeArgsForConstraintsCheck.SelectAsArray(TypeMap.TypeSymbolAsTypeWithModifiers));
            ArrayBuilder<TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder = null;
            var success = method.CheckConstraints(conversions, substitution, typeParams, typeArgsForConstraintsCheck, compilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder);
            diagnosticsBuilder.Free();

            if (useSiteDiagnosticsBuilder != null && useSiteDiagnosticsBuilder.Count > 0)
            {
                if (useSiteDiagnostics == null)
                {
                    useSiteDiagnostics = new HashSet<DiagnosticInfo>();
                }

                foreach (var diag in useSiteDiagnosticsBuilder)
                {
                    useSiteDiagnostics.Add(diag.DiagnosticInfo);
                }
            }

            if (!success)
            {
                return null;
            }

            // For the purpose of construction we use original type parameters in place of type arguments that we couldn't infer from the first argument.
            var typeArgsForConstruct = typeArgs;
            if (firstNullInTypeArgs != -1)
            {
                var builder = ArrayBuilder<TypeSymbol>.GetInstance();
                builder.AddRange(typeArgs, firstNullInTypeArgs);

                for (int i = firstNullInTypeArgs; i < typeArgsForConstruct.Length; i++)
                {
                    builder.Add(typeArgsForConstruct[i] ?? typeParams[i]);
                }

                typeArgsForConstruct = builder.ToImmutableAndFree();
            }

            return method.Construct(typeArgsForConstruct);
        }
        private bool IsCheckedConversion(TypeSymbol source, TypeSymbol target)
        {
            Debug.Assert((object)target != null);

            if ((object)source == null || !CheckOverflowAtRuntime)
            {
                return false;
            }

            if (source.IsDynamic())
            {
                return true;
            }

            SpecialType sourceST = source.StrippedType().EnumUnderlyingType().SpecialType;
            SpecialType targetST = target.StrippedType().EnumUnderlyingType().SpecialType;

            // integral to double or float is never checked, but float/double to integral 
            // may be checked.
            bool sourceIsNumeric = SpecialType.System_Char <= sourceST && sourceST <= SpecialType.System_Double;
            bool targetIsNumeric = SpecialType.System_Char <= targetST && targetST <= SpecialType.System_UInt64;

            return
                sourceIsNumeric && (targetIsNumeric || target.IsPointerType()) ||
                targetIsNumeric && source.IsPointerType();
        }
            protected BoundExpression LowerEvaluation(BoundDagEvaluation evaluation)
            {
                BoundExpression input = _tempAllocator.GetTemp(evaluation.Input);

                switch (evaluation)
                {
                case BoundDagFieldEvaluation f:
                {
                    FieldSymbol     field      = f.Field;
                    var             outputTemp = new BoundDagTemp(f.Syntax, field.Type, f);
                    BoundExpression output     = _tempAllocator.GetTemp(outputTemp);
                    BoundExpression access     = _localRewriter.MakeFieldAccess(f.Syntax, input, field, null, LookupResultKind.Viable, field.Type);
                    access.WasCompilerGenerated = true;
                    return(_factory.AssignmentExpression(output, access));
                }

                case BoundDagPropertyEvaluation p:
                {
                    PropertySymbol  property   = p.Property;
                    var             outputTemp = new BoundDagTemp(p.Syntax, property.Type, p);
                    BoundExpression output     = _tempAllocator.GetTemp(outputTemp);
                    return(_factory.AssignmentExpression(output, _localRewriter.MakePropertyAccess(_factory.Syntax, input, property, LookupResultKind.Viable, property.Type, isLeftOfAssignment: false)));
                }

                case BoundDagDeconstructEvaluation d:
                {
                    MethodSymbol method         = d.DeconstructMethod;
                    var          refKindBuilder = ArrayBuilder <RefKind> .GetInstance();

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

                    BoundExpression receiver;
                    void addArg(RefKind refKind, BoundExpression expression)
                    {
                        refKindBuilder.Add(refKind);
                        argBuilder.Add(expression);
                    }

                    Debug.Assert(method.Name == WellKnownMemberNames.DeconstructMethodName);
                    int extensionExtra;
                    if (method.IsStatic)
                    {
                        Debug.Assert(method.IsExtensionMethod);
                        receiver = _factory.Type(method.ContainingType);
                        addArg(method.ParameterRefKinds[0], input);
                        extensionExtra = 1;
                    }
                    else
                    {
                        receiver       = input;
                        extensionExtra = 0;
                    }

                    for (int i = extensionExtra; i < method.ParameterCount; i++)
                    {
                        ParameterSymbol parameter = method.Parameters[i];
                        Debug.Assert(parameter.RefKind == RefKind.Out);
                        var outputTemp = new BoundDagTemp(d.Syntax, parameter.Type, d, i - extensionExtra);
                        addArg(RefKind.Out, _tempAllocator.GetTemp(outputTemp));
                    }

                    return(_factory.Call(receiver, method, refKindBuilder.ToImmutableAndFree(), argBuilder.ToImmutableAndFree()));
                }

                case BoundDagTypeEvaluation t:
                {
                    TypeSymbol inputType = input.Type;
                    Debug.Assert(inputType is { });
                    if (inputType.IsDynamic())
                    {
                        // Avoid using dynamic conversions for pattern-matching.
                        inputType = _factory.SpecialType(SpecialType.System_Object);
                        input     = _factory.Convert(inputType, input);
                    }

                    TypeSymbol      type       = t.Type;
                    var             outputTemp = new BoundDagTemp(t.Syntax, type, t);
                    BoundExpression output     = _tempAllocator.GetTemp(outputTemp);
                    CompoundUseSiteInfo <AssemblySymbol> useSiteInfo = _localRewriter.GetNewCompoundUseSiteInfo();
                    Conversion conversion = _factory.Compilation.Conversions.ClassifyBuiltInConversion(inputType, output.Type, ref useSiteInfo);
                    _localRewriter._diagnostics.Add(t.Syntax, useSiteInfo);
                    BoundExpression evaluated;
                    if (conversion.Exists)
                    {
                        if (conversion.Kind == ConversionKind.ExplicitNullable &&
                            inputType.GetNullableUnderlyingType().Equals(output.Type, TypeCompareKind.AllIgnoreOptions) &&
                            _localRewriter.TryGetNullableMethod(t.Syntax, inputType, SpecialMember.System_Nullable_T_GetValueOrDefault, out MethodSymbol getValueOrDefault))
                        {
                            // As a special case, since the null test has already been done we can use Nullable<T>.GetValueOrDefault
                            evaluated = _factory.Call(input, getValueOrDefault);
                        }
                        else
                        {
                            evaluated = _factory.Convert(type, input, conversion);
                        }
                    }
                    else
                    {
                        evaluated = _factory.As(input, type);
                    }

                    return(_factory.AssignmentExpression(output, evaluated));
                }
Example #19
0
        /// <summary>
        /// If the extension method is applicable based on the "this" argument type, return
        /// the method constructed with the inferred type arguments. If the method is not an
        /// unconstructed generic method, type inference is skipped. If the method is not
        /// applicable, or if constraints when inferring type parameters from the "this" type
        /// are not satisfied, the return value is null.
        /// </summary>
        /// <param name="compilation">Compilation used to check constraints.  The latest language version is assumed if this is null.</param>
        private static MethodSymbol InferExtensionMethodTypeArguments(MethodSymbol method, TypeSymbol thisType, CSharpCompilation compilation, ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert(method.IsExtensionMethod);
            Debug.Assert((object)thisType != null);

            if (!method.IsGenericMethod || method != method.ConstructedFrom)
            {
                return(method);
            }

            // We never resolve extension methods on a dynamic receiver.
            if (thisType.IsDynamic())
            {
                return(null);
            }

            var containingAssembly = method.ContainingAssembly;
            var errorNamespace     = containingAssembly.GlobalNamespace;
            var conversions        = new TypeConversions(containingAssembly.CorLibrary);

            // There is absolutely no plausible syntax/tree that we could use for these
            // synthesized literals.  We could be speculatively binding a call to a PE method.
            var syntaxTree = CSharpSyntaxTree.Dummy;
            var syntax     = (CSharpSyntaxNode)syntaxTree.GetRoot();

            // Create an argument value for the "this" argument of specific type,
            // and pass the same bad argument value for all other arguments.
            var thisArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, thisType)
            {
                WasCompilerGenerated = true
            };
            var otherArgumentType  = new ExtendedErrorTypeSymbol(errorNamespace, name: string.Empty, arity: 0, errorInfo: null, unreported: false);
            var otherArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, otherArgumentType)
            {
                WasCompilerGenerated = true
            };

            var paramCount = method.ParameterCount;
            var arguments  = new BoundExpression[paramCount];

            for (int i = 0; i < paramCount; i++)
            {
                var argument = (i == 0) ? thisArgumentValue : otherArgumentValue;
                arguments[i] = argument;
            }

            var typeArgs = MethodTypeInferrer.InferTypeArgumentsFromFirstArgument(
                conversions,
                method,
                arguments.AsImmutable(),
                useSiteDiagnostics: ref useSiteDiagnostics);

            if (typeArgs.IsDefault)
            {
                return(null);
            }

            // For the purpose of constraint checks we use error type symbol in place of type arguments that we couldn't infer from the first argument.
            // This prevents constraint checking from failing for corresponding type parameters.
            int firstNullInTypeArgs       = -1;
            var notInferredTypeParameters = PooledHashSet <TypeParameterSymbol> .GetInstance();

            var typeParams = method.TypeParameters;
            var typeArgsForConstraintsCheck = typeArgs;

            for (int i = 0; i < typeArgsForConstraintsCheck.Length; i++)
            {
                if (!typeArgsForConstraintsCheck[i].HasType)
                {
                    firstNullInTypeArgs = i;
                    var builder = ArrayBuilder <TypeWithAnnotations> .GetInstance();

                    builder.AddRange(typeArgsForConstraintsCheck, firstNullInTypeArgs);

                    for (; i < typeArgsForConstraintsCheck.Length; i++)
                    {
                        var typeArg = typeArgsForConstraintsCheck[i];
                        if (!typeArg.HasType)
                        {
                            notInferredTypeParameters.Add(typeParams[i]);
                            builder.Add(TypeWithAnnotations.Create(ErrorTypeSymbol.UnknownResultType));
                        }
                        else
                        {
                            builder.Add(typeArg);
                        }
                    }

                    typeArgsForConstraintsCheck = builder.ToImmutableAndFree();
                    break;
                }
            }

            // Check constraints.
            var diagnosticsBuilder = ArrayBuilder <TypeParameterDiagnosticInfo> .GetInstance();

            var substitution = new TypeMap(typeParams, typeArgsForConstraintsCheck);
            ArrayBuilder <TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder = null;
            var success = method.CheckConstraints(conversions, substitution, typeParams, typeArgsForConstraintsCheck, compilation, diagnosticsBuilder, nullabilityDiagnosticsBuilderOpt: null, ref useSiteDiagnosticsBuilder,
                                                  ignoreTypeConstraintsDependentOnTypeParametersOpt: notInferredTypeParameters.Count > 0 ? notInferredTypeParameters : null);

            diagnosticsBuilder.Free();
            notInferredTypeParameters.Free();

            if (useSiteDiagnosticsBuilder != null && useSiteDiagnosticsBuilder.Count > 0)
            {
                if (useSiteDiagnostics == null)
                {
                    useSiteDiagnostics = new HashSet <DiagnosticInfo>();
                }

                foreach (var diag in useSiteDiagnosticsBuilder)
                {
                    useSiteDiagnostics.Add(diag.DiagnosticInfo);
                }
            }

            if (!success)
            {
                return(null);
            }

            // For the purpose of construction we use original type parameters in place of type arguments that we couldn't infer from the first argument.
            ImmutableArray <TypeWithAnnotations> typeArgsForConstruct = typeArgs;

            if (typeArgs.Any(t => !t.HasType))
            {
                typeArgsForConstruct = typeArgs.ZipAsArray(
                    method.TypeParameters,
                    (t, tp) => t.HasType ? t : TypeWithAnnotations.Create(tp));
            }

            return(method.Construct(typeArgsForConstruct));
        }
Example #20
0
        /// <summary>
        /// Check that the pattern type is valid for the operand. Return true if an error was reported.
        /// </summary>
        private bool CheckValidPatternType(
            CSharpSyntaxNode typeSyntax,
            BoundExpression operand,
            TypeSymbol operandType,
            TypeSymbol patternType,
            bool patternTypeWasInSource,
            bool isVar,
            DiagnosticBag diagnostics)
        {
            Debug.Assert((object)operandType != null);
            Debug.Assert((object)patternType != null);

            // Because we do not support recursive patterns, we always have an operand
            Debug.Assert((object)operand != null);
            // Once we support recursive patterns that will be relaxed.

            if (operandType.IsErrorType() || patternType.IsErrorType())
            {
                return(false);
            }
            else if (patternType.IsNullableType() && !isVar && patternTypeWasInSource)
            {
                // It is an error to use pattern-matching with a nullable type, because you'll never get null. Use the underlying type.
                Error(diagnostics, ErrorCode.ERR_PatternNullableType, typeSyntax, patternType, patternType.GetNullableUnderlyingType());
                return(true);
            }
            else if (patternType.IsStatic)
            {
                Error(diagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, patternType);
                return(true);
            }
            else if (!isVar)
            {
                if (patternType.IsDynamic())
                {
                    Error(diagnostics, ErrorCode.ERR_PatternDynamicType, typeSyntax);
                    return(true);
                }

                HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                Conversion conversion =
                    operand != null
                    ? this.Conversions.ClassifyConversionFromExpression(operand, patternType, ref useSiteDiagnostics, forCast : true)
                    : this.Conversions.ClassifyConversionFromType(operandType, patternType, ref useSiteDiagnostics, forCast: true);

                diagnostics.Add(typeSyntax, useSiteDiagnostics);
                switch (conversion.Kind)
                {
                case ConversionKind.ExplicitDynamic:
                case ConversionKind.ImplicitDynamic:
                // Since the input was `dynamic`, which is equivalent to `object`, there must also
                // exist some unboxing, identity, or reference conversion as well, making the conversion legal.
                case ConversionKind.Boxing:
                case ConversionKind.ExplicitNullable:
                case ConversionKind.ExplicitReference:
                case ConversionKind.Identity:
                case ConversionKind.ImplicitReference:
                case ConversionKind.Unboxing:
                case ConversionKind.NullLiteral:
                case ConversionKind.ImplicitNullable:
                    // these are the conversions allowed by a pattern match
                    break;

                //case ConversionKind.ExplicitNumeric:  // we do not perform numeric conversions of the operand
                //case ConversionKind.ImplicitConstant:
                //case ConversionKind.ImplicitNumeric:
                default:
                    Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, operandType, patternType);
                    return(true);
                }
            }

            return(false);
        }
Example #21
0
        private BoundExpression MakeUnaryOperator(
            BoundUnaryOperator oldNode,
            UnaryOperatorKind kind,
            CSharpSyntaxNode syntax,
            MethodSymbol method,
            BoundExpression loweredOperand,
            TypeSymbol type)
        {
            if (kind.IsDynamic())
            {
                Debug.Assert(kind == UnaryOperatorKind.DynamicTrue && type.SpecialType == SpecialType.System_Boolean || type.IsDynamic());
                Debug.Assert((object)method == null);

                // Logical operators on boxed Boolean constants:
                var constant = UnboxConstant(loweredOperand);
                if (constant == ConstantValue.True || constant == ConstantValue.False)
                {
                    if (kind == UnaryOperatorKind.DynamicTrue)
                    {
                        return(factory.Literal(constant.BooleanValue));
                    }
                    else if (kind == UnaryOperatorKind.DynamicLogicalNegation)
                    {
                        return(MakeConversion(factory.Literal(!constant.BooleanValue), type, @checked: false));
                    }
                }

                return(dynamicFactory.MakeDynamicUnaryOperator(kind, loweredOperand, type).ToExpression());
            }
            else if (kind.IsLifted())
            {
                if (!inExpressionLambda)
                {
                    return(LowerLiftedUnaryOperator(kind, syntax, method, loweredOperand, type));
                }
            }
            else if (kind.IsUserDefined())
            {
                Debug.Assert((object)method != null);
                Debug.Assert(type == method.ReturnType);
                if (!inExpressionLambda || kind == UnaryOperatorKind.UserDefinedTrue || kind == UnaryOperatorKind.UserDefinedFalse)
                {
                    return(BoundCall.Synthesized(syntax, null, method, loweredOperand));
                }
            }
            else if (kind.Operator() == UnaryOperatorKind.UnaryPlus)
            {
                // We do not call the operator even for decimal; we simply optimize it away entirely.
                return(loweredOperand);
            }

            if (kind == UnaryOperatorKind.EnumBitwiseComplement)
            {
                var underlyingType       = loweredOperand.Type.GetEnumUnderlyingType();
                var upconvertSpecialType = Binder.GetEnumPromotedType(underlyingType.SpecialType);
                var upconvertType        = upconvertSpecialType == underlyingType.SpecialType ?
                                           underlyingType :
                                           compilation.GetSpecialType(upconvertSpecialType);


                var newOperand            = MakeConversion(loweredOperand, upconvertType, false);
                UnaryOperatorKind newKind = kind.Operator().WithType(upconvertSpecialType);

                var newNode = (oldNode != null) ?
                              oldNode.Update(
                    newKind,
                    newOperand,
                    oldNode.ConstantValueOpt,
                    method,
                    newOperand.ResultKind,
                    upconvertType) :
                              new BoundUnaryOperator(
                    syntax,
                    newKind,
                    newOperand,
                    null,
                    method,
                    LookupResultKind.Viable,
                    upconvertType);

                return(MakeConversion(newNode.Syntax, newNode, ConversionKind.ExplicitEnumeration, type, @checked: false));
            }

            if (kind == UnaryOperatorKind.DecimalUnaryMinus)
            {
                method = (MethodSymbol)this.compilation.Assembly.GetSpecialTypeMember(SpecialMember.System_Decimal__op_UnaryNegation);
                if (!inExpressionLambda)
                {
                    return(BoundCall.Synthesized(syntax, null, method, loweredOperand));
                }
            }

            return((oldNode != null) ?
                   oldNode.Update(kind, loweredOperand, oldNode.ConstantValueOpt, method, oldNode.ResultKind, type) :
                   new BoundUnaryOperator(syntax, kind, loweredOperand, null, method, LookupResultKind.Viable, type));
        }
        /// <summary>
        /// If the extension method is applicable based on the "this" argument type, return
        /// the method constructed with the inferred type arguments. If the method is not an
        /// unconstructed generic method, type inference is skipped. If the method is not
        /// applicable, or if constraints when inferring type parameters from the "this" type
        /// are not satisfied, the return value is null.
        /// </summary>
        private static MethodSymbol InferExtensionMethodTypeArguments(MethodSymbol method, TypeSymbol thisType, ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert(method.IsExtensionMethod);
            Debug.Assert((object)thisType != null);

            if (!method.IsGenericMethod || method != method.ConstructedFrom)
            {
                return(method);
            }

            // We never resolve extension methods on a dynamic receiver.
            if (thisType.IsDynamic())
            {
                return(null);
            }

            var containingAssembly = method.ContainingAssembly;
            var errorNamespace     = containingAssembly.GlobalNamespace;
            var conversions        = new TypeConversions(containingAssembly.CorLibrary);

            // There is absolutely no plausible syntax/tree that we could use for these
            // synthesized literals.  We could be speculatively binding a call to a PE method.
            var syntaxTree = CSharpSyntaxTree.Dummy;
            var syntax     = (CSharpSyntaxNode)syntaxTree.GetRoot();

            // Create an argument value for the "this" argument of specific type,
            // and pass the same bad argument value for all other arguments.
            var thisArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, thisType)
            {
                WasCompilerGenerated = true
            };
            var otherArgumentType  = new ExtendedErrorTypeSymbol(errorNamespace, name: string.Empty, arity: 0, errorInfo: null, unreported: false);
            var otherArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, otherArgumentType)
            {
                WasCompilerGenerated = true
            };

            var paramCount = method.ParameterCount;
            var arguments  = new BoundExpression[paramCount];

            for (int i = 0; i < paramCount; i++)
            {
                var argument = (i == 0) ? thisArgumentValue : otherArgumentValue;
                arguments[i] = argument;
            }

            var typeArgs = MethodTypeInferrer.InferTypeArgumentsFromFirstArgument(
                conversions,
                method,
                arguments.AsImmutable(),
                useSiteDiagnostics: ref useSiteDiagnostics);

            if (typeArgs.IsDefault)
            {
                return(null);
            }

            // For the purpose of construction we use original type parameters in place of type arguments that we couldn't infer from the first argument.
            ImmutableArray <TypeWithAnnotations> typeArgsForConstruct = typeArgs;

            if (typeArgs.Any(t => !t.HasType))
            {
                typeArgsForConstruct = typeArgs.ZipAsArray(
                    method.TypeParameters,
                    (t, tp) => t.HasType ? t : TypeWithAnnotations.Create(tp));
            }

            return(method.Construct(typeArgsForConstruct));
        }
Example #23
0
        private ForEachEnumeratorInfo.Builder GetDefaultEnumeratorInfo(ForEachEnumeratorInfo.Builder builder, DiagnosticBag diagnostics, TypeSymbol collectionExprType)
        {
            // NOTE: for arrays, we won't actually use any of these members - they're just for the API.
            builder.CollectionType = GetSpecialType(SpecialType.System_Collections_IEnumerable, diagnostics, _syntax);

            if (collectionExprType.IsDynamic())
            {
                builder.ElementType = _syntax.Type.IsVar ?
                    (TypeSymbol)DynamicTypeSymbol.Instance :
                    GetSpecialType(SpecialType.System_Object, diagnostics, _syntax);
            }
            else
            {
                builder.ElementType = collectionExprType.SpecialType == SpecialType.System_String?
                    GetSpecialType(SpecialType.System_Char, diagnostics, _syntax) :
                    ((ArrayTypeSymbol)collectionExprType).ElementType;
            }


            // CONSIDER: 
            // For arrays and string none of these members will actually be emitted, so it seems strange to prevent compilation if they can't be found.
            // skip this work in the batch case?
            builder.GetEnumeratorMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerable__GetEnumerator, diagnostics, _syntax);
            builder.CurrentPropertyGetter = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__get_Current, diagnostics, _syntax);
            builder.MoveNextMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext, diagnostics, _syntax);

            Debug.Assert((object)builder.GetEnumeratorMethod == null ||
                builder.GetEnumeratorMethod.ReturnType == this.Compilation.GetSpecialType(SpecialType.System_Collections_IEnumerator));

            // We don't know the runtime type, so we will have to insert a runtime check for IDisposable (with a conditional call to IDisposable.Dispose).
            builder.NeedsDisposeMethod = true;
            return builder;
        }
Example #24
0
        /// <summary>
        /// Returns the better type amongst the two, with some possible modifications (dynamic/object or tuple names).
        /// </summary>
        private TypeSymbol Better(TypeSymbol type1, TypeSymbol type2, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            // Anything is better than an error sym.
            if (type1.IsErrorType())
            {
                return type2;
            }

            if ((object)type2 == null || type2.IsErrorType())
            {
                return type1;
            }

            var t1tot2 = _conversions.ClassifyImplicitConversionFromType(type1, type2, ref useSiteDiagnostics).Exists;
            var t2tot1 = _conversions.ClassifyImplicitConversionFromType(type2, type1, ref useSiteDiagnostics).Exists;

            if (t1tot2 && t2tot1)
            {
                if (type1.IsDynamic())
                {
                    return type1;
                }

                if (type2.IsDynamic())
                {
                    return type2;
                }

                if (type1.Equals(type2, TypeCompareKind.IgnoreDynamicAndTupleNames))
                {
                    return MethodTypeInferrer.MergeTupleNames(type1, type2, MethodTypeInferrer.MergeDynamic(type1, type2, type1, _conversions.CorLibrary), _conversions.CorLibrary);
                }

                return null;
            }

            if (t1tot2)
            {
                return type2;
            }

            if (t2tot1)
            {
                return type1;
            }

            return null;
        }
        private void TestUnaryIntrinsicSymbol(
            UnaryOperatorKind op,
            TypeSymbol type,
            CSharpCompilation compilation,
            SemanticModel semanticModel,
            ExpressionSyntax node1,
            ExpressionSyntax node2,
            ExpressionSyntax node3,
            ExpressionSyntax node4
        )
        {
            SymbolInfo info1 = semanticModel.GetSymbolInfo(node1);
            Assert.Equal(type.IsDynamic() ? CandidateReason.LateBound : CandidateReason.None, info1.CandidateReason);
            Assert.Equal(0, info1.CandidateSymbols.Length);

            var symbol1 = (MethodSymbol)info1.Symbol;
            var symbol2 = semanticModel.GetSymbolInfo(node2).Symbol;
            var symbol3 = (MethodSymbol)semanticModel.GetSymbolInfo(node3).Symbol;
            var symbol4 = semanticModel.GetSymbolInfo(node4).Symbol;

            Assert.Equal(symbol1, symbol3);

            if ((object)symbol1 != null)
            {
                Assert.NotSame(symbol1, symbol3);
                Assert.Equal(symbol1.GetHashCode(), symbol3.GetHashCode());

                Assert.Equal(symbol1.Parameters[0], symbol3.Parameters[0]);
                Assert.Equal(symbol1.Parameters[0].GetHashCode(), symbol3.Parameters[0].GetHashCode());
            }

            Assert.Equal(symbol2, symbol4);

            TypeSymbol underlying = type;

            if (op == UnaryOperatorKind.BitwiseComplement ||
                op == UnaryOperatorKind.PrefixDecrement || op == UnaryOperatorKind.PrefixIncrement ||
                op == UnaryOperatorKind.PostfixDecrement || op == UnaryOperatorKind.PostfixIncrement)
            {
                underlying = type.EnumUnderlyingType();
            }

            UnaryOperatorKind result = OverloadResolution.UnopEasyOut.OpKind(op, underlying);
            UnaryOperatorSignature signature;

            if (result == UnaryOperatorKind.Error)
            {
                if (type.IsDynamic())
                {
                    signature = new UnaryOperatorSignature(op | UnaryOperatorKind.Dynamic, type, type);
                }
                else if (type.IsPointerType() &&
                    (op == UnaryOperatorKind.PrefixDecrement || op == UnaryOperatorKind.PrefixIncrement ||
                        op == UnaryOperatorKind.PostfixDecrement || op == UnaryOperatorKind.PostfixIncrement))
                {
                    signature = new UnaryOperatorSignature(op | UnaryOperatorKind.Pointer, type, type);
                }
                else
                {
                    Assert.Null(symbol1);
                    Assert.Null(symbol2);
                    Assert.Null(symbol3);
                    Assert.Null(symbol4);
                    return;
                }
            }
            else
            {
                signature = compilation.builtInOperators.GetSignature(result);

                if ((object)underlying != (object)type)
                {
                    Assert.Equal(underlying, signature.OperandType);
                    Assert.Equal(underlying, signature.ReturnType);

                    signature = new UnaryOperatorSignature(signature.Kind, type, type);
                }
            }

            Assert.NotNull(symbol1);

            string containerName = signature.OperandType.ToTestDisplayString();
            string returnName = signature.ReturnType.ToTestDisplayString();

            if (op == UnaryOperatorKind.LogicalNegation && type.IsEnumType())
            {
                containerName = type.ToTestDisplayString();
                returnName = containerName;
            }

            Assert.Equal(String.Format("{2} {0}.{1}({0} value)",
                                       containerName,
                                       OperatorFacts.UnaryOperatorNameFromOperatorKind(op),
                                       returnName),
                         symbol1.ToTestDisplayString());

            Assert.Equal(MethodKind.BuiltinOperator, symbol1.MethodKind);
            Assert.True(symbol1.IsImplicitlyDeclared);

            bool expectChecked = false;

            switch (op)
            {
                case UnaryOperatorKind.UnaryMinus:
                    expectChecked = (type.IsDynamic() || symbol1.ContainingType.EnumUnderlyingType().SpecialType.IsIntegralType());
                    break;

                case UnaryOperatorKind.PrefixDecrement:
                case UnaryOperatorKind.PrefixIncrement:
                case UnaryOperatorKind.PostfixDecrement:
                case UnaryOperatorKind.PostfixIncrement:
                    expectChecked = (type.IsDynamic() || type.IsPointerType() ||
                                     symbol1.ContainingType.EnumUnderlyingType().SpecialType.IsIntegralType() ||
                                     symbol1.ContainingType.SpecialType == SpecialType.System_Char);
                    break;

                default:
                    expectChecked = type.IsDynamic();
                    break;
            }

            Assert.Equal(expectChecked, symbol1.IsCheckedBuiltin);

            Assert.False(symbol1.IsGenericMethod);
            Assert.False(symbol1.IsExtensionMethod);
            Assert.False(symbol1.IsExtern);
            Assert.False(symbol1.CanBeReferencedByName);
            Assert.Null(symbol1.DeclaringCompilation);
            Assert.Equal(symbol1.Name, symbol1.MetadataName);
            Assert.Same(symbol1.ContainingSymbol, symbol1.Parameters[0].Type);
            Assert.Equal(0, symbol1.Locations.Length);
            Assert.Null(symbol1.GetDocumentationCommentId());
            Assert.Equal("", symbol1.GetDocumentationCommentXml());

            Assert.True(symbol1.HasSpecialName);
            Assert.True(symbol1.IsStatic);
            Assert.Equal(Accessibility.Public, symbol1.DeclaredAccessibility);
            Assert.False(symbol1.HidesBaseMethodsByName);
            Assert.False(symbol1.IsOverride);
            Assert.False(symbol1.IsVirtual);
            Assert.False(symbol1.IsAbstract);
            Assert.False(symbol1.IsSealed);
            Assert.Equal(1, symbol1.ParameterCount);
            Assert.Equal(0, symbol1.Parameters[0].Ordinal);

            var otherSymbol = (MethodSymbol)semanticModel.GetSymbolInfo(node1).Symbol;
            Assert.Equal(symbol1, otherSymbol);

            if (type.IsValueType && !type.IsPointerType())
            {
                Assert.Equal(symbol1, symbol2);
                return;
            }

            Assert.Null(symbol2);
        }
Example #26
0
        internal override BoundStatement BindUsingStatementParts(DiagnosticBag diagnostics, Binder originalBinder)
        {
            ExpressionSyntax          expressionSyntax  = TargetExpressionSyntax;
            VariableDeclarationSyntax declarationSyntax = _syntax.Declaration;
            bool hasAwait = _syntax.AwaitKeyword.Kind() != default;

            Debug.Assert((expressionSyntax == null) ^ (declarationSyntax == null)); // Can't have both or neither.
            TypeSymbol disposableInterface = getDisposableInterface(hasAwait);

            Debug.Assert((object)disposableInterface != null);
            bool hasErrors = ReportUseSiteDiagnostics(disposableInterface, diagnostics, hasAwait ? _syntax.AwaitKeyword : _syntax.UsingKeyword);

            Conversion iDisposableConversion = Conversion.NoConversion;
            BoundMultipleLocalDeclarations declarationsOpt = null;
            BoundExpression expressionOpt      = null;
            AwaitableInfo   awaitOpt           = null;
            TypeSymbol      declarationTypeOpt = null;

            if (expressionSyntax != null)
            {
                expressionOpt = this.BindTargetExpression(diagnostics, originalBinder);
                hasErrors    |= !initConversion(fromExpression: true);
            }
            else
            {
                ImmutableArray <BoundLocalDeclaration> declarations;
                originalBinder.BindForOrUsingOrFixedDeclarations(declarationSyntax, LocalDeclarationKind.UsingVariable, diagnostics, out declarations);

                Debug.Assert(!declarations.IsEmpty);
                declarationsOpt    = new BoundMultipleLocalDeclarations(declarationSyntax, declarations);
                declarationTypeOpt = declarations[0].DeclaredType.Type;

                if (declarationTypeOpt.IsDynamic())
                {
                    iDisposableConversion = Conversion.ImplicitDynamic;
                }
                else
                {
                    hasErrors |= !initConversion(fromExpression: false);
                }
            }

            if (hasAwait)
            {
                TypeSymbol taskType = this.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_ValueTask);
                hasErrors |= ReportUseSiteDiagnostics(taskType, diagnostics, _syntax.AwaitKeyword);

                var             resource    = (SyntaxNode)expressionSyntax ?? declarationSyntax;
                BoundExpression placeholder = new BoundAwaitableValuePlaceholder(resource, taskType).MakeCompilerGenerated();
                awaitOpt = BindAwaitInfo(placeholder, resource, _syntax.AwaitKeyword.GetLocation(), diagnostics, ref hasErrors);
            }

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

            Debug.Assert(GetDeclaredLocalsForScope(_syntax) == this.Locals);
            return(new BoundUsingStatement(
                       _syntax,
                       this.Locals,
                       declarationsOpt,
                       expressionOpt,
                       iDisposableConversion,
                       boundBody,
                       awaitOpt,
                       hasErrors));

            bool initConversion(bool fromExpression)
            {
                HashSet <DiagnosticInfo> useSiteDiagnostics = null;

                iDisposableConversion = classifyConversion(fromExpression, disposableInterface, ref useSiteDiagnostics);

                diagnostics.Add(fromExpression ? (CSharpSyntaxNode)expressionSyntax : declarationSyntax, useSiteDiagnostics);

                if (iDisposableConversion.IsImplicit)
                {
                    return(true);
                }

                TypeSymbol type = fromExpression ? expressionOpt.Type : declarationTypeOpt;

                if (type is null || !type.IsErrorType())
                {
                    // Retry with a different assumption about whether the `using` is async
                    TypeSymbol alternateInterface    = getDisposableInterface(!hasAwait);
                    HashSet <DiagnosticInfo> ignored = null;
                    Conversion alternateConversion   = classifyConversion(fromExpression, alternateInterface, ref ignored);

                    bool      wrongAsync = alternateConversion.IsImplicit;
                    ErrorCode errorCode  = wrongAsync
                        ? (hasAwait ? ErrorCode.ERR_NoConvToIAsyncDispWrongAsync : ErrorCode.ERR_NoConvToIDispWrongAsync)
                        : (hasAwait ? ErrorCode.ERR_NoConvToIAsyncDisp : ErrorCode.ERR_NoConvToIDisp);

                    Error(diagnostics, errorCode, (CSharpSyntaxNode)declarationSyntax ?? expressionSyntax, declarationTypeOpt ?? expressionOpt.Display);
                }

                return(false);
            }

            Conversion classifyConversion(bool fromExpression, TypeSymbol targetInterface, ref HashSet <DiagnosticInfo> diag)
            {
                return(fromExpression ?
                       originalBinder.Conversions.ClassifyImplicitConversionFromExpression(expressionOpt, targetInterface, ref diag) :
                       originalBinder.Conversions.ClassifyImplicitConversionFromType(declarationTypeOpt, targetInterface, ref diag));
            }

            TypeSymbol getDisposableInterface(bool isAsync)
            {
                return(isAsync
                    ? this.Compilation.GetWellKnownType(WellKnownType.System_IAsyncDisposable)
                    : this.Compilation.GetSpecialType(SpecialType.System_IDisposable));
            }
        }
Example #27
0
        private bool ImplicitConversionExists(TypeSymbol source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            // SPEC VIOLATION: For the purpose of algorithm in Fix method, dynamic type is not considered convertible to any other type, including object.
            if (source.IsDynamic() && !destination.IsDynamic())
            {
                return false;
            }

            return _conversions.ClassifyImplicitConversionFromType(source, destination, ref useSiteDiagnostics).Exists;
        }
Example #28
0
        internal static bool TryCreate(SyntheticBoundNodeFactory F, MethodSymbol method, TypeMap typeMap, out AsyncMethodBuilderMemberCollection collection)
        {
            if (method.IsVoidReturningAsync())
            {
                return(TryCreate(
                           F: F,

                           builderType: F.WellKnownType(WellKnownType.System_Runtime_CompilerServices_AsyncVoidMethodBuilder),
                           resultType: F.SpecialType(SpecialType.System_Void),

                           setException: WellKnownMember.System_Runtime_CompilerServices_AsyncVoidMethodBuilder__SetException,
                           setResult: WellKnownMember.System_Runtime_CompilerServices_AsyncVoidMethodBuilder__SetResult,
                           awaitOnCompleted: WellKnownMember.System_Runtime_CompilerServices_AsyncVoidMethodBuilder__AwaitOnCompleted,
                           awaitUnsafeOnCompleted: WellKnownMember.System_Runtime_CompilerServices_AsyncVoidMethodBuilder__AwaitUnsafeOnCompleted,
                           start: WellKnownMember.System_Runtime_CompilerServices_AsyncVoidMethodBuilder__Start_T,
                           setStateMachine: WellKnownMember.System_Runtime_CompilerServices_AsyncVoidMethodBuilder__SetStateMachine,
                           task: null,
                           collection: out collection));
            }

            if (method.IsTaskReturningAsync(F.Compilation))
            {
                NamedTypeSymbol builderType = F.WellKnownType(WellKnownType.System_Runtime_CompilerServices_AsyncTaskMethodBuilder);

                PropertySymbol task;
                if (!TryGetWellKnownPropertyAsMember(F, WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder__Task, builderType, out task))
                {
                    collection = default(AsyncMethodBuilderMemberCollection);
                    return(false);
                }

                return(TryCreate(
                           F: F,

                           builderType: F.WellKnownType(WellKnownType.System_Runtime_CompilerServices_AsyncTaskMethodBuilder),
                           resultType: F.SpecialType(SpecialType.System_Void),

                           setException: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder__SetException,
                           setResult: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder__SetResult,
                           awaitOnCompleted: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder__AwaitOnCompleted,
                           awaitUnsafeOnCompleted: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder__AwaitUnsafeOnCompleted,
                           start: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder__Start_T,
                           setStateMachine: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder__SetStateMachine,
                           task: task,
                           collection: out collection));
            }

            if (method.IsGenericTaskReturningAsync(F.Compilation))
            {
                TypeSymbol resultType = method.ReturnType.GetMemberTypeArgumentsNoUseSiteDiagnostics().Single();

                if (resultType.IsDynamic())
                {
                    resultType = F.SpecialType(SpecialType.System_Object);
                }

                if (typeMap != null)
                {
                    resultType = typeMap.SubstituteType(resultType);
                }

                NamedTypeSymbol builderType = F.WellKnownType(WellKnownType.System_Runtime_CompilerServices_AsyncTaskMethodBuilder_T).Construct(resultType);

                PropertySymbol task;
                if (!TryGetWellKnownPropertyAsMember(F, WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder_T__Task, builderType, out task))
                {
                    collection = default(AsyncMethodBuilderMemberCollection);
                    return(false);
                }

                return(TryCreate(
                           F: F,

                           builderType: builderType,
                           resultType: resultType,

                           setException: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder_T__SetException,
                           setResult: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder_T__SetResult,
                           awaitOnCompleted: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder_T__AwaitOnCompleted,
                           awaitUnsafeOnCompleted: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder_T__AwaitUnsafeOnCompleted,
                           start: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder_T__Start_T,
                           setStateMachine: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder_T__SetStateMachine,
                           task: task,
                           collection: out collection));
            }

            throw ExceptionUtilities.UnexpectedValue(method);
        }
        // Determine if the conversion can actually overflow at runtime.  If not, no need to generate a checked instruction.
        private static bool NeedsChecked(TypeSymbol source, TypeSymbol target)
        {
            Debug.Assert((object)target != null);

            if ((object)source == null)
            {
                return false;
            }

            if (source.IsDynamic())
            {
                return true;
            }

            SpecialType sourceST = source.StrippedType().EnumUnderlyingType().SpecialType;
            SpecialType targetST = target.StrippedType().EnumUnderlyingType().SpecialType;

            // integral to double or float is never checked, but float/double to integral 
            // may be checked.
            bool sourceIsNumeric = SpecialType.System_Char <= sourceST && sourceST <= SpecialType.System_Double;
            bool targetIsNumeric = SpecialType.System_Char <= targetST && targetST <= SpecialType.System_UInt64;
            return
                sourceIsNumeric && target.IsPointerType() ||
                targetIsNumeric && source.IsPointerType() ||
                sourceIsNumeric && targetIsNumeric && needsChecked[sourceST - SpecialType.System_Char, targetST - SpecialType.System_Char];
        }
Example #30
0
        internal override BoundStatement BindUsingStatementParts(DiagnosticBag diagnostics, Binder originalBinder)
        {
            ExpressionSyntax          expressionSyntax  = TargetExpressionSyntax;
            VariableDeclarationSyntax declarationSyntax = _syntax.Declaration;

            Debug.Assert((expressionSyntax == null) ^ (declarationSyntax == null)); // Can't have both or neither.

            bool hasErrors = false;
            BoundMultipleLocalDeclarations declarationsOpt = null;
            BoundExpression expressionOpt         = null;
            Conversion      iDisposableConversion = Conversion.NoConversion;
            TypeSymbol      iDisposable           = this.Compilation.GetSpecialType(SpecialType.System_IDisposable); // no need for diagnostics, so use the Compilation version

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

            if (expressionSyntax != null)
            {
                expressionOpt = this.BindTargetExpression(diagnostics, originalBinder);

                HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                iDisposableConversion = originalBinder.Conversions.ClassifyImplicitConversionFromExpression(expressionOpt, iDisposable, ref useSiteDiagnostics);
                diagnostics.Add(expressionSyntax, useSiteDiagnostics);

                if (!iDisposableConversion.IsImplicit)
                {
                    TypeSymbol expressionType = expressionOpt.Type;
                    if ((object)expressionType == null || !expressionType.IsErrorType())
                    {
                        Error(diagnostics, ErrorCode.ERR_NoConvToIDisp, expressionSyntax, expressionOpt.Display);
                    }
                    hasErrors = true;
                }
            }
            else
            {
                ImmutableArray <BoundLocalDeclaration> declarations;
                originalBinder.BindForOrUsingOrFixedDeclarations(declarationSyntax, LocalDeclarationKind.UsingVariable, diagnostics, out declarations);

                Debug.Assert(!declarations.IsEmpty);

                declarationsOpt = new BoundMultipleLocalDeclarations(declarationSyntax, declarations);

                TypeSymbol declType = declarations[0].DeclaredType.Type;

                if (declType.IsDynamic())
                {
                    iDisposableConversion = Conversion.ImplicitDynamic;
                }
                else
                {
                    HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                    iDisposableConversion = originalBinder.Conversions.ClassifyImplicitConversionFromType(declType, iDisposable, ref useSiteDiagnostics);
                    diagnostics.Add(declarationSyntax, useSiteDiagnostics);

                    if (!iDisposableConversion.IsImplicit)
                    {
                        if (!declType.IsErrorType())
                        {
                            Error(diagnostics, ErrorCode.ERR_NoConvToIDisp, declarationSyntax, declType);
                        }

                        hasErrors = true;
                    }
                }
            }

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

            Debug.Assert(GetDeclaredLocalsForScope(_syntax) == this.Locals);
            return(new BoundUsingStatement(
                       _syntax,
                       this.Locals,
                       declarationsOpt,
                       expressionOpt,
                       iDisposableConversion,
                       boundBody,
                       hasErrors));
        }
Example #31
0
        /// <summary>
        /// If the extension method is applicable based on the "this" argument type, return
        /// the method constructed with the inferred type arguments. If the method is not an
        /// unconstructed generic method, type inference is skipped. If the method is not
        /// applicable, or if constraints when inferring type parameters from the "this" type
        /// are not satisfied, the return value is null.
        /// </summary>
        public static MethodSymbol InferExtensionMethodTypeArguments(this MethodSymbol method, TypeSymbol thisType, Compilation compilation, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert(method.IsExtensionMethod);
            Debug.Assert((object)thisType != null);

            if (!method.IsGenericMethod || method != method.ConstructedFrom)
            {
                return method;
            }

            // We never resolve extension methods on a dynamic receiver.
            if (thisType.IsDynamic())
            {
                return null;
            }

            var containingAssembly = method.ContainingAssembly;
            var errorNamespace = containingAssembly.GlobalNamespace;
            var conversions = new TypeConversions(containingAssembly.CorLibrary);

            // There is absolutely no plausible syntax/tree that we could use for these
            // synthesized literals.  We could be speculatively binding a call to a PE method.
            var syntaxTree = CSharpSyntaxTree.Dummy;
            var syntax = (CSharpSyntaxNode)syntaxTree.GetRoot();

            // Create an argument value for the "this" argument of specific type,
            // and pass the same bad argument value for all other arguments.
            var thisArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, thisType) { WasCompilerGenerated = true };
            var otherArgumentType = new ExtendedErrorTypeSymbol(errorNamespace, name: string.Empty, arity: 0, errorInfo: null, unreported: false);
            var otherArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, otherArgumentType) { WasCompilerGenerated = true };

            var paramCount = method.ParameterCount;
            var arguments = new BoundExpression[paramCount];
            var argumentTypes = new TypeSymbol[paramCount];
            for (int i = 0; i < paramCount; i++)
            {
                var argument = (i == 0) ? thisArgumentValue : otherArgumentValue;
                arguments[i] = argument;
                argumentTypes[i] = argument.Type;
            }

            var typeArgs = MethodTypeInferrer.InferTypeArgumentsFromFirstArgument(
                conversions,
                method,
                argumentTypes.AsImmutableOrNull(),
                arguments.AsImmutableOrNull(),
                ref useSiteDiagnostics);

            if (typeArgs.IsDefault)
            {
                return null;
            }

            // Check constraints.
            var diagnosticsBuilder = ArrayBuilder<TypeParameterDiagnosticInfo>.GetInstance();
            var typeParams = method.TypeParameters;
            var substitution = new TypeMap(typeParams, typeArgs);
            ArrayBuilder<TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder = null;
            var success = method.CheckConstraints(conversions, substitution, method.TypeParameters, typeArgs, compilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder);
            diagnosticsBuilder.Free();

            if (useSiteDiagnosticsBuilder != null && useSiteDiagnosticsBuilder.Count > 0)
            {
                if (useSiteDiagnostics == null)
                {
                    useSiteDiagnostics = new HashSet<DiagnosticInfo>();
                }

                foreach (var diag in useSiteDiagnosticsBuilder)
                {
                    useSiteDiagnostics.Add(diag.DiagnosticInfo);
                }
            }

            if (!success)
            {
                return null;
            }

            return method.Construct(typeArgs);
        }
        private TupleBinaryOperatorInfo BindTupleBinaryOperatorInfo(BinaryExpressionSyntax node, BinaryOperatorKind kind,
                                                                    BoundExpression left, BoundExpression right, BindingDiagnosticBag diagnostics)
        {
            TypeSymbol leftType  = left.Type;
            TypeSymbol rightType = right.Type;

            if ((object)leftType != null && leftType.IsDynamic() || (object)rightType != null && rightType.IsDynamic())
            {
                return(BindTupleDynamicBinaryOperatorSingleInfo(node, kind, left, right, diagnostics));
            }

            if (IsTupleBinaryOperation(left, right))
            {
                return(BindTupleBinaryOperatorNestedInfo(node, kind, left, right, diagnostics));
            }

            BoundExpression comparison = BindSimpleBinaryOperator(node, diagnostics, left, right);

            switch (comparison)
            {
            case BoundLiteral _:
                // this case handles `null == null` and the like
                return(new TupleBinaryOperatorInfo.NullNull(kind));

            case BoundBinaryOperator binary:
                PrepareBoolConversionAndTruthOperator(binary.Type, node, kind, diagnostics, out Conversion conversionIntoBoolOperator, out UnaryOperatorSignature boolOperator);
                CheckConstraintLanguageVersionAndRuntimeSupportForOperator(node, boolOperator.Method, boolOperator.ConstrainedToTypeOpt, diagnostics);

                return(new TupleBinaryOperatorInfo.Single(binary.Left.Type, binary.Right.Type, binary.OperatorKind, binary.MethodOpt, binary.ConstrainedToTypeOpt, conversionIntoBoolOperator, boolOperator));

            default:
                throw ExceptionUtilities.UnexpectedValue(comparison);
            }
        }
        private BoundExpression MakeUnaryOperator(
            BoundUnaryOperator oldNode,
            UnaryOperatorKind kind,
            CSharpSyntaxNode syntax,
            MethodSymbol method,
            BoundExpression loweredOperand,
            TypeSymbol type)
        {
            if (kind.IsDynamic())
            {
                Debug.Assert(kind == UnaryOperatorKind.DynamicTrue && type.SpecialType == SpecialType.System_Boolean || type.IsDynamic());
                Debug.Assert((object)method == null);

                // Logical operators on boxed Boolean constants:
                var constant = UnboxConstant(loweredOperand);
                if (constant == ConstantValue.True || constant == ConstantValue.False)
                {
                    if (kind == UnaryOperatorKind.DynamicTrue)
                    {
                        return _factory.Literal(constant.BooleanValue);
                    }
                    else if (kind == UnaryOperatorKind.DynamicLogicalNegation)
                    {
                        return MakeConversionNode(_factory.Literal(!constant.BooleanValue), type, @checked: false);
                    }
                }

                return _dynamicFactory.MakeDynamicUnaryOperator(kind, loweredOperand, type).ToExpression();
            }
            else if (kind.IsLifted())
            {
                if (!_inExpressionLambda)
                {
                    return LowerLiftedUnaryOperator(kind, syntax, method, loweredOperand, type);
                }
            }
            else if (kind.IsUserDefined())
            {
                Debug.Assert((object)method != null);
                Debug.Assert(type == method.ReturnType);
                if (!_inExpressionLambda || kind == UnaryOperatorKind.UserDefinedTrue || kind == UnaryOperatorKind.UserDefinedFalse)
                {
                    return BoundCall.Synthesized(syntax, null, method, loweredOperand);
                }
            }
            else if (kind.Operator() == UnaryOperatorKind.UnaryPlus)
            {
                // We do not call the operator even for decimal; we simply optimize it away entirely.
                return loweredOperand;
            }

            if (kind == UnaryOperatorKind.EnumBitwiseComplement)
            {
                var underlyingType = loweredOperand.Type.GetEnumUnderlyingType();
                var upconvertSpecialType = Binder.GetEnumPromotedType(underlyingType.SpecialType);
                var upconvertType = upconvertSpecialType == underlyingType.SpecialType ?
                    underlyingType :
                    _compilation.GetSpecialType(upconvertSpecialType);


                var newOperand = MakeConversionNode(loweredOperand, upconvertType, false);
                UnaryOperatorKind newKind = kind.Operator().WithType(upconvertSpecialType);

                var newNode = (oldNode != null) ?
                    oldNode.Update(
                        newKind,
                        newOperand,
                        oldNode.ConstantValueOpt,
                        method,
                        newOperand.ResultKind,
                        upconvertType) :
                    new BoundUnaryOperator(
                        syntax,
                        newKind,
                        newOperand,
                        null,
                        method,
                        LookupResultKind.Viable,
                        upconvertType);

                return MakeConversionNode(newNode.Syntax, newNode, Conversion.ExplicitEnumeration, type, @checked: false);
            }

            if (kind == UnaryOperatorKind.DecimalUnaryMinus)
            {
                method = (MethodSymbol)_compilation.Assembly.GetSpecialTypeMember(SpecialMember.System_Decimal__op_UnaryNegation);
                if (!_inExpressionLambda)
                {
                    return BoundCall.Synthesized(syntax, null, method, loweredOperand);
                }
            }

            return (oldNode != null) ?
                oldNode.Update(kind, loweredOperand, oldNode.ConstantValueOpt, method, oldNode.ResultKind, type) :
                new BoundUnaryOperator(syntax, kind, loweredOperand, null, method, LookupResultKind.Viable, type);
        }
Example #34
0
        /// <summary>
        /// The spec describes an algorithm for finding the following types:
        ///   1) Collection type
        ///   2) Enumerator type
        ///   3) Element type
        ///
        /// The implementation details are a bit difference.  If we're iterating over a string or an array, then we don't need to record anything
        /// but the inferredType (in case the iteration variable is implicitly typed).  If we're iterating over anything else, then we want the
        /// inferred type plus a ForEachEnumeratorInfo.Builder with:
        ///   1) Collection type
        ///   2) Element type
        ///   3) GetEnumerator method of the collection type (return type will be the enumerator type from the spec)
        ///   4) Current property of the enumerator type
        ///   5) MoveNext method of the enumerator type
        ///
        /// The caller will have to do some extra conversion checks before creating a ForEachEnumeratorInfo for the BoundForEachStatement.
        /// </summary>
        /// <param name="builder">Builder to fill in (partially, all but conversions).</param>
        /// <param name="collectionExpr">The expression over which to iterate.</param>
        /// <param name="diagnostics">Populated with binding diagnostics.</param>
        /// <returns>Partially populated (all but conversions) or null if there was an error.</returns>
        private bool GetEnumeratorInfo(ref ForEachEnumeratorInfo.Builder builder, BoundExpression collectionExpr, DiagnosticBag diagnostics)
        {
            TypeSymbol collectionExprType = collectionExpr.Type;

            if (collectionExpr.ConstantValue != null && collectionExpr.ConstantValue.IsNull)
            {
                // Spec seems to refer to null literals, but Dev10 reports anything known to be null.
                Debug.Assert(collectionExpr.ConstantValue.IsNull); // only constant value with no type
                diagnostics.Add(ErrorCode.ERR_NullNotValid, syntax.Expression.Location);

                return(false);
            }

            if ((object)collectionExprType == null) // There's no way to enumerate something without a type.
            {
                // The null literal was caught above, so anything else with a null type is a method group or anonymous function
                diagnostics.Add(ErrorCode.ERR_AnonMethGrpInForEach, syntax.Expression.Location, collectionExpr.Display);
                // CONSIDER: dev10 also reports ERR_ForEachMissingMember (i.e. failed pattern match).

                return(false);
            }

            if (collectionExpr.ResultKind == LookupResultKind.NotAValue)
            {
                // Short-circuiting to prevent strange behavior in the case where the collection
                // expression is a type expression and the type is enumerable.
                Debug.Assert(collectionExpr.HasAnyErrors); // should already have been reported
                return(false);
            }

            // The spec specifically lists the collection, enumerator, and element types for arrays and dynamic.
            if (collectionExprType.Kind == SymbolKind.ArrayType || collectionExprType.Kind == SymbolKind.DynamicType)
            {
                // NOTE: for arrays, we won't actually use any of these members - they're just for the API.
                builder.CollectionType = GetSpecialType(SpecialType.System_Collections_IEnumerable, diagnostics, this.syntax);
                builder.ElementType    =
                    collectionExprType.IsDynamic() ?
                    (this.syntax.Type.IsVar ? (TypeSymbol)DynamicTypeSymbol.Instance : GetSpecialType(SpecialType.System_Object, diagnostics, this.syntax)) :
                    ((ArrayTypeSymbol)collectionExprType).ElementType;

                // CONSIDER:
                // For arrays none of these members will actually be emitted, so it seems strange to prevent compilation if they can't be found.
                // skip this work in the batch case? (If so, also special case string, which won't use the pattern methods.)
                builder.GetEnumeratorMethod   = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerable__GetEnumerator, diagnostics, this.syntax);
                builder.CurrentPropertyGetter = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__get_Current, diagnostics, this.syntax);
                builder.MoveNextMethod        = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext, diagnostics, this.syntax);

                Debug.Assert((object)builder.GetEnumeratorMethod == null ||
                             builder.GetEnumeratorMethod.ReturnType == this.Compilation.GetSpecialType(SpecialType.System_Collections_IEnumerator));

                // We don't know the runtime type, so we will have to insert a runtime check for IDisposable (with a conditional call to IDisposable.Dispose).
                builder.NeedsDisposeMethod = true;

                return(true);
            }

            bool foundMultipleGenericIEnumerableInterfaces;

            if (SatisfiesGetEnumeratorPattern(ref builder, collectionExprType, diagnostics))
            {
                Debug.Assert((object)builder.GetEnumeratorMethod != null);

                builder.CollectionType = collectionExprType;

                if (SatisfiesForEachPattern(ref builder, diagnostics))
                {
                    builder.ElementType = ((PropertySymbol)builder.CurrentPropertyGetter.AssociatedSymbol).Type;

                    // NOTE: if IDisposable is not available at all, no diagnostics will be reported - we will just assume that
                    // the enumerator is not disposable.  If it has IDisposable in its interface list, there will be a diagnostic there.
                    // If IDisposable is available but its Dispose method is not, then diagnostics will be reported only if the enumerator
                    // is potentially disposable.

                    var        useSiteDiagnosticBag             = DiagnosticBag.GetInstance();
                    TypeSymbol enumeratorType                   = builder.GetEnumeratorMethod.ReturnType;
                    HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                    if (!enumeratorType.IsSealed || this.Conversions.ClassifyImplicitConversion(enumeratorType, this.Compilation.GetSpecialType(SpecialType.System_IDisposable), ref useSiteDiagnostics).IsImplicit)
                    {
                        builder.NeedsDisposeMethod = true;
                        diagnostics.AddRange(useSiteDiagnosticBag);
                    }
                    useSiteDiagnosticBag.Free();

                    diagnostics.Add(this.syntax, useSiteDiagnostics);
                    return(true);
                }

                MethodSymbol getEnumeratorMethod = builder.GetEnumeratorMethod;
                diagnostics.Add(ErrorCode.ERR_BadGetEnumerator, this.syntax.Expression.Location, getEnumeratorMethod.ReturnType, getEnumeratorMethod);
                return(false);
            }

            if (IsIEnumerable(collectionExprType))
            {
                // This indicates a problem with the special IEnumerable type - it should have satisfied the GetEnumerator pattern.
                diagnostics.Add(ErrorCode.ERR_ForEachMissingMember, syntax.Expression.Location, collectionExprType, GetEnumeratorMethodName);
                return(false);
            }

            if (AllInterfacesContainsIEnumerable(ref builder, collectionExprType, diagnostics, out foundMultipleGenericIEnumerableInterfaces))
            {
                CSharpSyntaxNode errorLocationSyntax = this.syntax.Expression;

                if (foundMultipleGenericIEnumerableInterfaces)
                {
                    diagnostics.Add(ErrorCode.ERR_MultipleIEnumOfT, errorLocationSyntax.Location, collectionExprType, this.Compilation.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T));
                    return(false);
                }

                Debug.Assert((object)builder.CollectionType != null);

                NamedTypeSymbol collectionType = (NamedTypeSymbol)builder.CollectionType;
                if (collectionType.IsGenericType)
                {
                    // If the type is generic, we have to search for the methods
                    Debug.Assert(collectionType.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T);
                    builder.ElementType = collectionType.TypeArgumentsNoUseSiteDiagnostics.Single();

                    MethodSymbol getEnumeratorMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_Generic_IEnumerable_T__GetEnumerator, diagnostics, errorLocationSyntax);
                    if ((object)getEnumeratorMethod != null)
                    {
                        builder.GetEnumeratorMethod = getEnumeratorMethod.AsMember(collectionType);

                        TypeSymbol enumeratorType = builder.GetEnumeratorMethod.ReturnType;
                        Debug.Assert(enumeratorType.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerator_T);
                        MethodSymbol currentPropertyGetter = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_Generic_IEnumerator_T__get_Current, diagnostics, errorLocationSyntax);
                        if ((object)currentPropertyGetter != null)
                        {
                            builder.CurrentPropertyGetter = currentPropertyGetter.AsMember((NamedTypeSymbol)enumeratorType);
                        }
                    }

                    builder.MoveNextMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext, diagnostics, errorLocationSyntax); // NOTE: MoveNext is actually inherited from System.Collections.IEnumerator
                }
                else
                {
                    // Non-generic - use special members to avoid re-computing
                    Debug.Assert(collectionType.SpecialType == SpecialType.System_Collections_IEnumerable);
                    builder.ElementType = GetSpecialType(SpecialType.System_Object, diagnostics, errorLocationSyntax);

                    builder.GetEnumeratorMethod   = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerable__GetEnumerator, diagnostics, errorLocationSyntax);
                    builder.CurrentPropertyGetter = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__get_Current, diagnostics, errorLocationSyntax);
                    builder.MoveNextMethod        = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext, diagnostics, errorLocationSyntax);

                    Debug.Assert((object)builder.GetEnumeratorMethod == null ||
                                 builder.GetEnumeratorMethod.ReturnType == GetSpecialType(SpecialType.System_Collections_IEnumerator, diagnostics, errorLocationSyntax));
                }

                // We don't know the runtime type, so we will have to insert a runtime check for IDisposable (with a conditional call to IDisposable.Dispose).
                builder.NeedsDisposeMethod = true;
                return(true);
            }


            if (!string.IsNullOrEmpty(collectionExprType.Name) || !collectionExpr.HasErrors)
            {
                diagnostics.Add(ErrorCode.ERR_ForEachMissingMember, syntax.Expression.Location, collectionExprType.ToDisplayString(), GetEnumeratorMethodName);
            }
            return(false);
        }
        private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpression resultPlace)
        {
            var expression = (BoundExpression)Visit(node.Expression);

            resultPlace = (BoundExpression)Visit(resultPlace);
            MethodSymbol getAwaiter        = VisitMethodSymbol(node.GetAwaiter);
            MethodSymbol getResult         = VisitMethodSymbol(node.GetResult);
            MethodSymbol isCompletedMethod = ((object)node.IsCompleted != null) ? VisitMethodSymbol(node.IsCompleted.GetMethod) : null;
            TypeSymbol   type = VisitType(node.Type);

            LocalSymbol awaiterTemp;

            if ((object)getResult == null)
            {
                awaiterTemp = F.SynthesizedLocal(DynamicTypeSymbol.Instance);
            }
            else if (type.IsDynamic())
            {
                var awaiterType = ((NamedTypeSymbol)getAwaiter.ReturnType).OriginalDefinition.Construct(F.SpecialType(SpecialType.System_Object));
                awaiterTemp       = F.SynthesizedLocal(awaiterType);
                getResult         = getResult.OriginalDefinition.AsMember(awaiterType);
                isCompletedMethod = isCompletedMethod.OriginalDefinition.AsMember(awaiterType);
            }
            else
            {
                awaiterTemp = F.SynthesizedLocal(getAwaiter.ReturnType);
            }

            var awaitIfIncomplete = F.Block(
                // temp $awaiterTemp = <expr>.GetAwaiter();
                F.Assignment(
                    F.Local(awaiterTemp),
                    MakeCallMaybeDynamic(expression, getAwaiter, WellKnownMemberNames.GetAwaiter)),

                // if(!($awaiterTemp.IsCompleted)) { ... }
                F.If(
                    condition: F.Not(GenerateGetIsCompleted(awaiterTemp, isCompletedMethod)),
                    thenClause: GenerateAwaitForIncompleteTask(awaiterTemp)));

            BoundExpression getResultCall = MakeCallMaybeDynamic(
                F.Local(awaiterTemp),
                getResult,
                WellKnownMemberNames.GetResult,
                resultsDiscarded: resultPlace == null);

            var nullAwaiter = F.AssignmentExpression(F.Local(awaiterTemp), F.NullOrDefault(awaiterTemp.Type));

            if (resultPlace != null && type.SpecialType != SpecialType.System_Void)
            {
                // $resultTemp = $awaiterTemp.GetResult();
                // $awaiterTemp = null;
                // $resultTemp
                LocalSymbol resultTemp = F.SynthesizedLocal(type);
                return(F.Block(
                           ImmutableArray.Create(awaiterTemp, resultTemp),
                           awaitIfIncomplete,
                           F.Assignment(F.Local(resultTemp), getResultCall),
                           F.ExpressionStatement(nullAwaiter),
                           F.Assignment(resultPlace, F.Local(resultTemp))));
            }
            else
            {
                // $awaiterTemp.GetResult();
                // $awaiterTemp = null;
                return(F.Block(
                           ImmutableArray.Create(awaiterTemp),
                           awaitIfIncomplete,
                           F.ExpressionStatement(getResultCall),
                           F.ExpressionStatement(nullAwaiter)));
            }
        }
        /// <summary>
        /// If the extension method is applicable based on the "this" argument type, return
        /// the method constructed with the inferred type arguments. If the method is not an
        /// unconstructed generic method, type inference is skipped. If the method is not
        /// applicable, or if constraints when inferring type parameters from the "this" type
        /// are not satisfied, the return value is null.
        /// </summary>
        public static MethodSymbol InferExtensionMethodTypeArguments(this MethodSymbol method, TypeSymbol thisType, Compilation compilation, ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert(method.IsExtensionMethod);
            Debug.Assert((object)thisType != null);

            if (!method.IsGenericMethod || method != method.ConstructedFrom)
            {
                return(method);
            }

            // We never resolve extension methods on a dynamic receiver.
            if (thisType.IsDynamic())
            {
                return(null);
            }

            var containingAssembly = method.ContainingAssembly;
            var errorNamespace     = containingAssembly.GlobalNamespace;
            var conversions        = new TypeConversions(containingAssembly.CorLibrary);

            // There is absolutely no plausible syntax/tree that we could use for these
            // synthesized literals.  We could be speculatively binding a call to a PE method.
            var syntaxTree = CSharpSyntaxTree.Dummy;
            var syntax     = (CSharpSyntaxNode)syntaxTree.GetRoot();

            // Create an argument value for the "this" argument of specific type,
            // and pass the same bad argument value for all other arguments.
            var thisArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, thisType)
            {
                WasCompilerGenerated = true
            };
            var otherArgumentType  = new ExtendedErrorTypeSymbol(errorNamespace, name: string.Empty, arity: 0, errorInfo: null, unreported: false);
            var otherArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, otherArgumentType)
            {
                WasCompilerGenerated = true
            };

            var paramCount    = method.ParameterCount;
            var arguments     = new BoundExpression[paramCount];
            var argumentTypes = new TypeSymbol[paramCount];

            for (int i = 0; i < paramCount; i++)
            {
                var argument = (i == 0) ? thisArgumentValue : otherArgumentValue;
                arguments[i]     = argument;
                argumentTypes[i] = argument.Type;
            }

            var typeArgs = MethodTypeInferrer.InferTypeArgumentsFromFirstArgument(
                conversions,
                method,
                argumentTypes.AsImmutableOrNull(),
                arguments.AsImmutableOrNull(),
                ref useSiteDiagnostics);

            if (typeArgs.IsDefault)
            {
                return(null);
            }

            // Check constraints.
            var diagnosticsBuilder = ArrayBuilder <TypeParameterDiagnosticInfo> .GetInstance();

            var typeParams   = method.TypeParameters;
            var substitution = new TypeMap(typeParams, typeArgs);
            ArrayBuilder <TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder = null;
            var success = method.CheckConstraints(conversions, substitution, method.TypeParameters, typeArgs, compilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder);

            diagnosticsBuilder.Free();

            if (useSiteDiagnosticsBuilder != null && useSiteDiagnosticsBuilder.Count > 0)
            {
                if (useSiteDiagnostics == null)
                {
                    useSiteDiagnostics = new HashSet <DiagnosticInfo>();
                }

                foreach (var diag in useSiteDiagnosticsBuilder)
                {
                    useSiteDiagnostics.Add(diag.DiagnosticInfo);
                }
            }

            if (!success)
            {
                return(null);
            }

            return(method.Construct(typeArgs));
        }