Beispiel #1
0
        public override Conversion GetMethodGroupDelegateConversion(BoundMethodGroup source, TypeSymbol destination, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            // Must be a bona fide delegate type, not an expression tree type.
            if (!(destination.IsDelegateType() || destination.SpecialType == SpecialType.System_Delegate))
            {
                return(Conversion.NoConversion);
            }

            var(methodSymbol, isFunctionPointer, callingConventionInfo) = GetDelegateInvokeOrFunctionPointerMethodIfAvailable(source, destination, ref useSiteInfo);
            if ((object)methodSymbol == null)
            {
                return(Conversion.NoConversion);
            }

            Debug.Assert(destination.SpecialType == SpecialType.System_Delegate || methodSymbol == ((NamedTypeSymbol)destination).DelegateInvokeMethod);

            var resolution = ResolveDelegateOrFunctionPointerMethodGroup(_binder, source, methodSymbol, isFunctionPointer, callingConventionInfo, ref useSiteInfo);
            var conversion = (resolution.IsEmpty || resolution.HasAnyErrors) ?
                             Conversion.NoConversion :
                             ToConversion(resolution.OverloadResolutionResult, resolution.MethodGroup, methodSymbol.ParameterCount);

            resolution.Free();
            return(conversion);
        }
Beispiel #2
0
        internal override void LookupSymbolsInSingleBinder(
            LookupResult result, string name, int arity, ConsList <TypeSymbol> basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            Debug.Assert(result.IsClear);

            if (IsSubmissionClass)
            {
                this.LookupMembersInternal(result, _container, name, arity, basesBeingResolved, options, originalBinder, diagnose, ref useSiteInfo);
                return;
            }

            var imports = GetImports(basesBeingResolved);

            // first lookup members of the namespace
            if ((options & LookupOptions.NamespaceAliasesOnly) == 0 && _container != null)
            {
                this.LookupMembersInternal(result, _container, name, arity, basesBeingResolved, options, originalBinder, diagnose, ref useSiteInfo);

                if (result.IsMultiViable)
                {
                    // symbols cannot conflict with using alias names
                    if (arity == 0 && imports.IsUsingAlias(name, originalBinder.IsSemanticModelBinder))
                    {
                        CSDiagnosticInfo diagInfo = new CSDiagnosticInfo(ErrorCode.ERR_ConflictAliasAndMember, name, _container);
                        var error = new ExtendedErrorTypeSymbol((NamespaceOrTypeSymbol)null, name, arity, diagInfo, unreported: true);
                        result.SetFrom(LookupResult.Good(error)); // force lookup to be done w/ error symbol as result
                    }

                    return;
                }
            }

            // next try using aliases or symbols in imported namespaces
            imports.LookupSymbol(originalBinder, result, name, arity, basesBeingResolved, options, diagnose, ref useSiteInfo);
        }
Beispiel #3
0
 internal override ManagedKind GetManagedKind(ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo) => ManagedKind.Managed;
 public override Conversion GetMethodGroupFunctionPointerConversion(BoundMethodGroup source, FunctionPointerTypeSymbol destination, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
 {
     // Conversions involving method groups require a Binder.
     throw ExceptionUtilities.Unreachable;
 }
 protected override Conversion GetInterpolatedStringConversion(BoundExpression source, TypeSymbol destination, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
 {
     // Conversions involving interpolated strings require a Binder.
     throw ExceptionUtilities.Unreachable;
 }
Beispiel #6
0
        private bool CandidateOperators(ArrayBuilder <UnaryOperatorSignature> operators, BoundExpression operand, ArrayBuilder <UnaryOperatorAnalysisResult> results, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            bool anyApplicable = false;

            foreach (var op in operators)
            {
                var conversion = Conversions.ClassifyConversionFromExpression(operand, op.OperandType, ref useSiteInfo);
                if (conversion.IsImplicit)
                {
                    anyApplicable = true;
                    results.Add(UnaryOperatorAnalysisResult.Applicable(op, conversion));
                }
                else
                {
                    results.Add(UnaryOperatorAnalysisResult.Inapplicable(op, conversion));
                }
            }

            return(anyApplicable);
        }
Beispiel #7
0
        private void UnaryOperatorOverloadResolution(
            BoundExpression operand,
            UnaryOperatorOverloadResolutionResult result,
            ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            // SPEC: Given the set of applicable candidate function members, the best function member in that set is located.
            // SPEC: If the set contains only one function member, then that function member is the best function member.

            if (result.SingleValid())
            {
                return;
            }

            // SPEC: Otherwise, the best function member is the one function member that is better than all other function
            // SPEC: members with respect to the given argument list, provided that each function member is compared to all
            // SPEC: other function members using the rules in 7.5.3.2. If there is not exactly one function member that is
            // SPEC: better than all other function members, then the function member invocation is ambiguous and a binding-time
            // SPEC: error occurs.

            var candidates = result.Results;
            // Try to find a single best candidate
            int bestIndex = GetTheBestCandidateIndex(operand, candidates, ref useSiteInfo);

            if (bestIndex != -1)
            {
                // Mark all other candidates as worse
                for (int index = 0; index < candidates.Count; ++index)
                {
                    if (candidates[index].Kind != OperatorAnalysisResultKind.Inapplicable && index != bestIndex)
                    {
                        candidates[index] = candidates[index].Worse();
                    }
                }

                return;
            }

            for (int i = 1; i < candidates.Count; ++i)
            {
                if (candidates[i].Kind != OperatorAnalysisResultKind.Applicable)
                {
                    continue;
                }

                // Is this applicable operator better than every other applicable method?
                for (int j = 0; j < i; ++j)
                {
                    if (candidates[j].Kind == OperatorAnalysisResultKind.Inapplicable)
                    {
                        continue;
                    }

                    var better = BetterOperator(candidates[i].Signature, candidates[j].Signature, operand, ref useSiteInfo);
                    if (better == BetterResult.Left)
                    {
                        candidates[j] = candidates[j].Worse();
                    }
                    else if (better == BetterResult.Right)
                    {
                        candidates[i] = candidates[i].Worse();
                    }
                }
            }
        }
Beispiel #8
0
        private UserDefinedConversionResult AnalyzeImplicitUserDefinedConversions(
            BoundExpression sourceExpression,
            TypeSymbol source,
            TypeSymbol target,
            ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            Debug.Assert(sourceExpression != null || (object)source != null);
            Debug.Assert((object)target != null);

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

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

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

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

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

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

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

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

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

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

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

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

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

            return(UserDefinedConversionResult.Valid(u, best.Value));
        }
Beispiel #9
0
        private TypeSymbol MostSpecificSourceTypeForImplicitUserDefinedConversion(ImmutableArray <UserDefinedConversionAnalysis> u, TypeSymbol source, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            // SPEC: If any of the operators in U convert from S then SX is S.
            if ((object)source != null)
            {
                if (u.Any(conv => TypeSymbol.Equals(conv.FromType, source, TypeCompareKind.ConsiderEverything2)))
                {
                    return(source);
                }
            }

            // SPEC: Otherwise, SX is the most encompassed type in the set of
            // SPEC: source types of the operators in U.
            return(MostEncompassedType(u, conv => conv.FromType, ref useSiteInfo));
        }
Beispiel #10
0
        private static void ComputeUserDefinedImplicitConversionTypeSet(TypeSymbol s, TypeSymbol t, ArrayBuilder <TypeSymbol> d, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            // Spec 6.4.4: User-defined implicit conversions
            //   Find the set of types D from which user-defined conversion operators
            //   will be considered. This set consists of S0 (if S0 is a class or struct),
            //   the base classes of S0 (if S0 is a class), and T0 (if T0 is a class or struct).

            AddTypesParticipatingInUserDefinedConversion(d, s, includeBaseTypes: true, useSiteInfo: ref useSiteInfo);
            AddTypesParticipatingInUserDefinedConversion(d, t, includeBaseTypes: false, useSiteInfo: ref useSiteInfo);
        }
Beispiel #11
0
        private void ComputeApplicableUserDefinedImplicitConversionSet(
            BoundExpression sourceExpression,
            TypeSymbol source,
            TypeSymbol target,
            ArrayBuilder <TypeSymbol> d,
            ArrayBuilder <UserDefinedConversionAnalysis> u,
            ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo,
            bool allowAnyTarget = false)
        {
            Debug.Assert(sourceExpression != null || (object)source != null);
            Debug.Assert(((object)target != null) == !allowAnyTarget);
            Debug.Assert(d != null);
            Debug.Assert(u != null);

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

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

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

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

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

            HashSet <NamedTypeSymbol> lookedInInterfaces = null;

            foreach (TypeSymbol declaringType in d)
            {
                if (declaringType is TypeParameterSymbol typeParameter)
                {
                    ImmutableArray <NamedTypeSymbol> interfaceTypes = typeParameter.AllEffectiveInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo);

                    if (!interfaceTypes.IsEmpty)
                    {
                        lookedInInterfaces ??= new HashSet <NamedTypeSymbol>(Symbols.SymbolEqualityComparer.AllIgnoreOptions); // Equivalent to has identity conversion check

                        foreach (var interfaceType in interfaceTypes)
                        {
                            if (lookedInInterfaces.Add(interfaceType))
                            {
                                addCandidatesFromType(constrainedToTypeOpt: typeParameter, interfaceType, sourceExpression, source, target, u, ref useSiteInfo, allowAnyTarget);
                            }
                        }
                    }
                }
                else
                {
                    addCandidatesFromType(constrainedToTypeOpt: null, (NamedTypeSymbol)declaringType, sourceExpression, source, target, u, ref useSiteInfo, allowAnyTarget);
                }
            }

            void addCandidatesFromType(
                TypeParameterSymbol constrainedToTypeOpt,
                NamedTypeSymbol declaringType,
                BoundExpression sourceExpression,
                TypeSymbol source,
                TypeSymbol target,
                ArrayBuilder <UserDefinedConversionAnalysis> u,
                ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo,
                bool allowAnyTarget)
            {
                foreach (MethodSymbol op in declaringType.GetOperators(WellKnownMemberNames.ImplicitConversionName))
                {
                    // We might have a bad operator and be in an error recovery situation. Ignore it.
                    if (op.ReturnsVoid || op.ParameterCount != 1)
                    {
                        continue;
                    }

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

                    if (fromConversion.Exists && toConversion.Exists)
                    {
                        // There is an additional spec violation in the native compiler. Suppose
                        // we have a conversion from X-->Y and are asked to do "Y? y = new X();"  Clearly
                        // the intention is to convert from X-->Y via the implicit conversion, and then
                        // stick a standard implicit conversion from Y-->Y? on the back end. **In this
                        // situation, the native compiler treats the conversion as though it were
                        // actually X-->Y? in source for the purposes of determining the best target
                        // type of an operator.
                        //
                        // We perpetuate this fiction here, except for cases when Y is not a valid type
                        // argument for Nullable<T>. This scenario should only be possible when the corlib
                        // defines a type such as int or long to be a ref struct (see
                        // LiftedConversion_InvalidTypeArgument02).

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

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

                        if (liftedFromConversion.Exists && liftedToConversion.Exists)
                        {
                            u.Add(UserDefinedConversionAnalysis.Lifted(constrainedToTypeOpt, op, liftedFromConversion, liftedToConversion, nullableFrom, nullableTo));
                        }
                    }
                }
            }
        }
Beispiel #12
0
        private static (bool definitelyManaged, bool hasGenerics) DependsOnDefinitelyManagedType(
            NamedTypeSymbol type,
            HashSet <Symbol> partialClosure,
            ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo
            )
        {
            Debug.Assert((object)type != null);

            var hasGenerics = false;

            if (partialClosure.Add(type))
            {
                foreach (var member in type.GetInstanceFieldsAndEvents())
                {
                    // Only instance fields (including field-like events) affect the outcome.
                    FieldSymbol field;
                    switch (member.Kind)
                    {
                    case SymbolKind.Field:
                        field = (FieldSymbol)member;
                        Debug.Assert(
                            (object)(field.AssociatedSymbol as EventSymbol) == null,
                            "Didn't expect to find a field-like event backing field in the member list."
                            );
                        break;

                    case SymbolKind.Event:
                        field = ((EventSymbol)member).AssociatedField;
                        break;

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

                    if ((object)field == null)
                    {
                        continue;
                    }

                    TypeSymbol fieldType = field.NonPointerType();
                    if (fieldType is null)
                    {
                        // pointers are unmanaged
                        continue;
                    }

                    fieldType.AddUseSiteInfo(ref useSiteInfo);
                    NamedTypeSymbol fieldNamedType = fieldType as NamedTypeSymbol;
                    if ((object)fieldNamedType == null)
                    {
                        if (fieldType.IsManagedType(ref useSiteInfo))
                        {
                            return(true, hasGenerics);
                        }
                    }
                    else
                    {
                        var result = IsManagedTypeHelper(fieldNamedType);
                        hasGenerics = hasGenerics || result.hasGenerics;
                        // NOTE: don't use ManagedKind.get on a NamedTypeSymbol - that could lead
                        // to infinite recursion.
                        switch (result.isManaged)
                        {
                        case ThreeState.True:
                            return(true, hasGenerics);

                        case ThreeState.False:
                            continue;

                        case ThreeState.Unknown:
                            if (!fieldNamedType.OriginalDefinition.KnownCircularStruct)
                            {
                                var(definitelyManaged, childHasGenerics) =
                                    DependsOnDefinitelyManagedType(
                                        fieldNamedType,
                                        partialClosure,
                                        ref useSiteInfo
                                        );
                                hasGenerics = hasGenerics || childHasGenerics;
                                if (definitelyManaged)
                                {
                                    return(true, hasGenerics);
                                }
                            }
                            continue;
                        }
                    }
                }
            }

            return(false, hasGenerics);
        }
Beispiel #13
0
 protected override Conversion GetInterpolatedStringConversion(BoundUnconvertedInterpolatedString source, TypeSymbol destination, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
 {
     // An interpolated string expression may be converted to the types
     // System.IFormattable and System.FormattableString
     return((TypeSymbol.Equals(destination, Compilation.GetWellKnownType(WellKnownType.System_IFormattable), TypeCompareKind.ConsiderEverything2) ||
             TypeSymbol.Equals(destination, Compilation.GetWellKnownType(WellKnownType.System_FormattableString), TypeCompareKind.ConsiderEverything2))
         ? Conversion.InterpolatedString : Conversion.NoConversion);
 }
Beispiel #14
0
        public override Conversion GetMethodGroupFunctionPointerConversion(BoundMethodGroup source, FunctionPointerTypeSymbol destination, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            var resolution = ResolveDelegateOrFunctionPointerMethodGroup(
                _binder,
                source,
                destination.Signature,
                isFunctionPointer: true,
                new CallingConventionInfo(destination.Signature.CallingConvention, destination.Signature.GetCallingConventionModifiers()),
                ref useSiteInfo);
            var conversion = (resolution.IsEmpty || resolution.HasAnyErrors) ?
                             Conversion.NoConversion :
                             ToConversion(resolution.OverloadResolutionResult, resolution.MethodGroup, destination.Signature.ParameterCount);

            resolution.Free();
            return(conversion);
        }
Beispiel #15
0
        public void UnaryOperatorOverloadResolution(UnaryOperatorKind kind, BoundExpression operand, UnaryOperatorOverloadResolutionResult result, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            Debug.Assert(operand != null);
            Debug.Assert(result.Results.Count == 0);

            // We can do a table lookup for well-known problems in overload resolution.
            UnaryOperatorEasyOut(kind, operand, result);
            if (result.Results.Count > 0)
            {
                return;
            }

            // SPEC: An operation of the form op x or x op, where op is an overloadable unary operator,
            // SPEC: and x is an expression of type X, is processed as follows:

            // SPEC: The set of candidate user-defined operators provided by X for the operation operator
            // SPEC: op(x) is determined using the rules of 7.3.5.

            bool hadUserDefinedCandidate = GetUserDefinedOperators(kind, operand, result.Results, ref useSiteInfo);

            // SPEC: If the set of candidate user-defined operators is not empty, then this becomes the
            // SPEC: set of candidate operators for the operation. Otherwise, the predefined unary operator
            // SPEC: implementations, including their lifted forms, become the set of candidate operators
            // SPEC: for the operation.

            if (!hadUserDefinedCandidate)
            {
                result.Results.Clear();
                GetAllBuiltInOperators(kind, operand, result.Results, ref useSiteInfo);
            }

            // SPEC: The overload resolution rules of 7.5.3 are applied to the set of candidate operators
            // SPEC: to select the best operator with respect to the argument list (x), and this operator
            // SPEC: becomes the result of the overload resolution process. If overload resolution fails
            // SPEC: to select a single best operator, a binding-time error occurs.

            UnaryOperatorOverloadResolution(operand, result, ref useSiteInfo);
        }
Beispiel #16
0
        private TypeSymbol MostSpecificTargetTypeForImplicitUserDefinedConversion(ImmutableArray <UserDefinedConversionAnalysis> u, TypeSymbol target, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            // SPEC: If any of the operators in U convert to T then TX is T.
            // SPEC: Otherwise, TX is the most encompassing type in the set of
            // SPEC: target types of the operators in U.

            // DELIBERATE SPEC VIOLATION:
            // The native compiler deviates from the specification in the way it
            // determines what the "converts to" type is. The specification is pretty
            // clear that the "converts to" type is the actual return type of the
            // conversion operator, or, in the case of a lifted operator, the lifted-to-
            // nullable type. That is, if we have X-->Y then the converts-to type of
            // the operator in its normal form is Y, and the converts-to type of the
            // operator in its lifted form is Y?.
            //
            // The native compiler does not do this. Suppose we have a user-defined
            // conversion X-->Y, and the assignment Y? y = new X(); -- the native
            // compiler will consider the converts-to type of X-->Y to be Y?, surprisingly
            // enough.
            //
            // We have previously written the appropriate "ToType" into the conversion analysis
            // to perpetuate this fiction.

            if (u.Any(conv => TypeSymbol.Equals(conv.ToType, target, TypeCompareKind.ConsiderEverything2)))
            {
                return(target);
            }

            return(MostEncompassingType(u, conv => conv.ToType, ref useSiteInfo));
        }
Beispiel #17
0
        private void GetAllBuiltInOperators(UnaryOperatorKind kind, BoundExpression operand, ArrayBuilder <UnaryOperatorAnalysisResult> results, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            // The spec states that overload resolution is performed upon the infinite set of
            // operators defined on enumerated types, pointers and delegates. Clearly we cannot
            // construct the infinite set; we have to pare it down. Previous implementations of C#
            // implement a much stricter rule; they only add the special operators to the candidate
            // set if one of the operands is of the relevant type. This means that operands
            // involving user-defined implicit conversions from class or struct types to enum,
            // pointer and delegate types do not cause the right candidates to participate in
            // overload resolution. It also presents numerous problems involving delegate variance
            // and conversions from lambdas to delegate types.
            //
            // It is onerous to require the actually specified behavior. We should change the
            // specification to match the previous implementation.

            var operators = ArrayBuilder <UnaryOperatorSignature> .GetInstance();

            this.Compilation.builtInOperators.GetSimpleBuiltInOperators(kind, operators, skipNativeIntegerOperators: !operand.Type.IsNativeIntegerOrNullableNativeIntegerType());

            GetEnumOperations(kind, operand, operators);

            var pointerOperator = GetPointerOperation(kind, operand);

            if (pointerOperator != null)
            {
                operators.Add(pointerOperator.Value);
            }

            CandidateOperators(operators, operand, results, ref useSiteInfo);
            operators.Free();
        }
Beispiel #18
0
        private bool IsEncompassedBy(BoundExpression aExpr, TypeSymbol a, TypeSymbol b, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            Debug.Assert((object)a != null);
            Debug.Assert((object)b != null);

            // SPEC: If a standard implicit conversion exists from a type A to a type B
            // SPEC: and if neither A nor B is an interface type then A is said to be
            // SPEC: encompassed by B, and B is said to encompass A.

            return(EncompassingImplicitConversion(aExpr, a, b, ref useSiteInfo).Exists);
        }
Beispiel #19
0
        private bool GetUserDefinedOperators(UnaryOperatorKind kind, BoundExpression operand, ArrayBuilder <UnaryOperatorAnalysisResult> results, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            Debug.Assert(operand != null);

            if ((object)operand.Type == null)
            {
                // If the operand has no type -- because it is a null reference or a lambda or a method group --
                // there is no way we can determine what type to search for user-defined operators.
                return(false);
            }

            // Spec 7.3.5 Candidate user-defined operators
            // SPEC: Given a type T and an operation op(A) ... the set of candidate user-defined
            // SPEC: operators provided by T for op(A) is determined as follows:

            // SPEC: If T is a nullable type then T0 is its underlying type; otherwise T0 is T.
            // SPEC: For all operator declarations in T0 and all lifted forms of such operators, if
            // SPEC: at least one operator is applicable with respect to A then the set of candidate
            // SPEC: operators consists of all such applicable operators. Otherwise, if T0 is object
            // SPEC: then the set of candidate operators is empty. Otherwise, the set of candidate
            // SPEC: operators is the set provided by the direct base class of T0, or the effective
            // SPEC: base class of T0 if T0 is a type parameter.

            // https://github.com/dotnet/roslyn/issues/34451: The spec quote should be adjusted to cover operators from interfaces as well.
            // From https://github.com/dotnet/csharplang/blob/main/meetings/2017/LDM-2017-06-27.md:
            // - We only even look for operator implementations in interfaces if one of the operands has a type that is an interface or
            // a type parameter with a non-empty effective base interface list.
            // - The applicable operators from classes / structs shadow those in interfaces.This matters for constrained type parameters:
            // the effective base class can shadow operators from effective base interfaces.
            // - If we find an applicable candidate in an interface, that candidate shadows all applicable operators in base interfaces:
            // we stop looking.

            TypeSymbol type0 = operand.Type.StrippedType();
            TypeSymbol constrainedToTypeOpt = type0 as TypeParameterSymbol;

            // Searching for user-defined operators is expensive; let's take an early out if we can.
            if (OperatorFacts.DefinitelyHasNoUserDefinedOperators(type0))
            {
                return(false);
            }

            string name      = OperatorFacts.UnaryOperatorNameFromOperatorKind(kind);
            var    operators = ArrayBuilder <UnaryOperatorSignature> .GetInstance();

            bool hadApplicableCandidates = false;

            NamedTypeSymbol current = type0 as NamedTypeSymbol;

            if ((object)current == null)
            {
                current = type0.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteInfo);
            }

            if ((object)current == null && type0.IsTypeParameter())
            {
                current = ((TypeParameterSymbol)type0).EffectiveBaseClass(ref useSiteInfo);
            }

            for (; (object)current != null; current = current.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteInfo))
            {
                operators.Clear();
                GetUserDefinedUnaryOperatorsFromType(constrainedToTypeOpt, current, kind, name, operators);
                results.Clear();
                if (CandidateOperators(operators, operand, results, ref useSiteInfo))
                {
                    hadApplicableCandidates = true;
                    break;
                }
            }

            // Look in base interfaces, or effective interfaces for type parameters
            if (!hadApplicableCandidates)
            {
                ImmutableArray <NamedTypeSymbol> interfaces = default;
                if (type0.IsInterfaceType())
                {
                    interfaces = type0.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo);
                }
                else if (type0.IsTypeParameter())
                {
                    interfaces = ((TypeParameterSymbol)type0).AllEffectiveInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo);
                }

                if (!interfaces.IsDefaultOrEmpty)
                {
                    var shadowedInterfaces = PooledHashSet <NamedTypeSymbol> .GetInstance();

                    var resultsFromInterface = ArrayBuilder <UnaryOperatorAnalysisResult> .GetInstance();

                    results.Clear();

                    foreach (NamedTypeSymbol @interface in interfaces)
                    {
                        if ([email protected])
                        {
                            // this code could be reachable in error situations
                            continue;
                        }

                        if (shadowedInterfaces.Contains(@interface))
                        {
                            // this interface is "shadowed" by a derived interface
                            continue;
                        }

                        operators.Clear();
                        resultsFromInterface.Clear();
                        GetUserDefinedUnaryOperatorsFromType(constrainedToTypeOpt, @interface, kind, name, operators);
                        if (CandidateOperators(operators, operand, resultsFromInterface, ref useSiteInfo))
                        {
                            hadApplicableCandidates = true;
                            results.AddRange(resultsFromInterface);

                            // this interface "shadows" all its base interfaces
                            shadowedInterfaces.AddAll(@interface.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo));
                        }
                    }

                    shadowedInterfaces.Free();
                    resultsFromInterface.Free();
                }
            }

            operators.Free();

            return(hadApplicableCandidates);
        }
Beispiel #20
0
        private Conversion EncompassingImplicitConversion(BoundExpression aExpr, TypeSymbol a, TypeSymbol b, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            Debug.Assert(aExpr != null || (object)a != null);
            Debug.Assert((object)b != null);

            // DELIBERATE SPEC VIOLATION:
            // We ought to be saying that an encompassing conversion never exists when one of
            // the types is an interface type, but due to a desire to be compatible with a
            // dev10 bug, we allow it. See the comment regarding bug 17021 above for more details.

            var result = ClassifyStandardImplicitConversion(aExpr, a, b, ref useSiteInfo);

            return(IsEncompassingImplicitConversionKind(result.Kind) ? result : Conversion.NoConversion);
        }
Beispiel #21
0
        private ImmutableArray <Symbol> ComputeSortedCrefMembers(NamespaceOrTypeSymbol?containerOpt, string memberName, int arity, bool hasParameterList, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            // Since we may find symbols without going through the lookup API,
            // expose the symbols via an ArrayBuilder.
            ArrayBuilder <Symbol> builder;

            {
                LookupResult result = LookupResult.GetInstance();
                this.LookupSymbolsOrMembersInternal(
                    result,
                    containerOpt,
                    name: memberName,
                    arity: arity,
                    basesBeingResolved: null,
                    options: LookupOptions.AllMethodsOnArityZero,
                    diagnose: false,
                    useSiteInfo: ref useSiteInfo);

                // CONSIDER: Dev11 also checks for a constructor in the event of an ambiguous result.
                if (result.IsMultiViable)
                {
                    // Dev11 doesn't consider members from System.Object when the container is an interface.
                    // Lookup should already have dropped such members.
                    builder = ArrayBuilder <Symbol> .GetInstance();

                    builder.AddRange(result.Symbols);
                    result.Free();
                }
                else
                {
                    result.Free(); // Won't be using this.

                    // Dev11 has a complicated two-stage process for determining when a cref is really referring to a constructor.
                    // Under two sets of conditions, XmlDocCommentBinder::bindXMLReferenceName will decide that a name refers
                    // to a constructor and under one set of conditions, the calling method, XmlDocCommentBinder::bindXMLReference,
                    // will roll back that decision and return null.

                    // In XmlDocCommentBinder::bindXMLReferenceName:
                    //   1) If an unqualified, non-generic name didn't bind to anything and the name matches the name of the type
                    //      to which the doc comment is applied, then bind to a constructor.
                    //   2) If a qualified, non-generic name didn't bind to anything and the LHS of the qualified name is a type
                    //      with the same name, then bind to a constructor.

                    // Quoted from XmlDocCommentBinder::bindXMLReference:
                    //   Filtering out the case where specifying the name of a generic type without specifying
                    //   any arity returns a constructor. This case shouldn't return anything. Note that
                    //   returning the constructors was a fix for the wonky constructor behavior, but in order
                    //   to not introduce a regression and breaking change we return NULL in this case.
                    //   e.g.
                    //
                    //   /// <see cref="Goo"/>
                    //   class Goo<T> { }
                    //
                    //   This cref used not to bind to anything, because before it was looking for a type and
                    //   since there was no arity, it didn't find Goo<T>. Now however, it finds Goo<T>.ctor,
                    //   which is arguably correct, but would be a breaking change (albeit with minimal impact)
                    //   so we catch this case and chuck out the symbol found.

                    // In Roslyn, we're doing everything in one pass, rather than guessing and rolling back.

                    // As in the native compiler, we treat this as a fallback case - something that actually has the
                    // specified name is preferred.

                    NamedTypeSymbol?constructorType = null;

                    if (arity == 0) // Member arity
                    {
                        NamedTypeSymbol?containerType = containerOpt as NamedTypeSymbol;
                        if ((object?)containerType != null)
                        {
                            // Case 1: If the name is qualified by a type with the same name, then we want a
                            // constructor (unless the type is generic, the cref is on/in the type (but not
                            // on/in a nested type), and there were no parens after the member name).

                            if (containerType.Name == memberName && (hasParameterList || containerType.Arity == 0 || !TypeSymbol.Equals(this.ContainingType, containerType.OriginalDefinition, TypeCompareKind.ConsiderEverything2)))
                            {
                                constructorType = containerType;
                            }
                        }
                        else if ((object?)containerOpt == null && hasParameterList)
                        {
                            // Case 2: If the name is not qualified by anything, but we're in the scope
                            // of a type with the same name (regardless of arity), then we want a constructor,
                            // as long as there were parens after the member name.

                            NamedTypeSymbol?binderContainingType = this.ContainingType;
                            if ((object?)binderContainingType != null && memberName == binderContainingType.Name)
                            {
                                constructorType = binderContainingType;
                            }
                        }
                    }

                    if ((object?)constructorType != null)
                    {
                        ImmutableArray <MethodSymbol> instanceConstructors = constructorType.InstanceConstructors;
                        int numInstanceConstructors = instanceConstructors.Length;

                        if (numInstanceConstructors == 0)
                        {
                            return(ImmutableArray <Symbol> .Empty);
                        }

                        builder = ArrayBuilder <Symbol> .GetInstance(numInstanceConstructors);

                        builder.AddRange(instanceConstructors);
                    }
                    else
                    {
                        return(ImmutableArray <Symbol> .Empty);
                    }
                }
            }

            Debug.Assert(builder != null);

            // Since we resolve ambiguities by just picking the first symbol we encounter,
            // the order of the symbols matters for repeatability.
            if (builder.Count > 1)
            {
                builder.Sort(ConsistentSymbolOrder.Instance);
            }

            return(builder.ToImmutableAndFree());
        }
Beispiel #22
0
        protected UserDefinedConversionResult AnalyzeImplicitUserDefinedConversionForV6SwitchGoverningType(TypeSymbol source, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            // SPEC:    The governing type of a switch statement is established by the switch expression.
            // SPEC:    1) If the type of the switch expression is sbyte, byte, short, ushort, int, uint,
            // SPEC:       long, ulong, bool, char, string, or an enum-type, or if it is the nullable type
            // SPEC:       corresponding to one of these types, then that is the governing type of the switch statement.
            // SPEC:    2) Otherwise, exactly one user-defined implicit conversion (§6.4) must exist from the
            // SPEC:       type of the switch expression to one of the following possible governing types:
            // SPEC:       sbyte, byte, short, ushort, int, uint, long, ulong, char, string, or, a nullable type
            // SPEC:       corresponding to one of those types

            // NOTE:    This method implements part (2) above, it should be called only if (1) is false for source type.
            Debug.Assert((object)source != null);
            Debug.Assert(!source.IsValidV6SwitchGoverningType());

            // NOTE: For (2) we use an approach similar to native compiler's approach, but call into the common code for analyzing user defined implicit conversions.
            // NOTE:    (a) Compute the set of types D from which user-defined conversion operators should be considered by considering only the source type.
            // NOTE:    (b) Instead of computing applicable user defined implicit conversions U from the source type to a specific target type,
            // NOTE:        we compute these from the source type to ANY target type.
            // NOTE:    (c) From the conversions in U, select the most specific of them that targets a valid switch governing type

            // SPEC VIOLATION: Because we use the same strategy for computing the most specific conversion, as the Dev10 compiler did (in fact
            // SPEC VIOLATION: we share the code), we inherit any spec deviances in that analysis. Specifically, the analysis only considers
            // SPEC VIOLATION: which conversion has the least amount of lifting, where a conversion may be considered to be in unlifted form,
            // SPEC VIOLATION: half-lifted form (only the argument type or return type is lifted) or fully lifted form. The most specific computation
            // SPEC VIOLATION: looks for a unique conversion that is least lifted. The spec, on the other hand, requires that the conversion
            // SPEC VIOLATION: be *unique*, not merely most use the least amount of lifting among the applicable conversions.

            // SPEC VIOLATION: This introduces a SPEC VIOLATION for the following tests in the native compiler:

            // NOTE:    // See test SwitchTests.CS0166_AggregateTypeWithMultipleImplicitConversions_07
            // NOTE:    struct Conv
            // NOTE:    {
            // NOTE:        public static implicit operator int (Conv C) { return 1; }
            // NOTE:        public static implicit operator int (Conv? C2) { return 0; }
            // NOTE:        public static int Main()
            // NOTE:        {
            // NOTE:            Conv? D = new Conv();
            // NOTE:            switch(D)
            // NOTE:            {   ...

            // SPEC VIOLATION: Native compiler allows the above code to compile
            // SPEC VIOLATION: even though there are two user-defined implicit conversions:
            // SPEC VIOLATION: 1) To int type (applicable in normal form): public static implicit operator int (Conv? C2)
            // SPEC VIOLATION: 2) To int? type (applicable in lifted form): public static implicit operator int (Conv C)

            // NOTE:    // See also test SwitchTests.TODO
            // NOTE:    struct Conv
            // NOTE:    {
            // NOTE:        public static implicit operator int? (Conv C) { return 1; }
            // NOTE:        public static implicit operator string (Conv? C2) { return 0; }
            // NOTE:        public static int Main()
            // NOTE:        {
            // NOTE:            Conv? D = new Conv();
            // NOTE:            switch(D)
            // NOTE:            {   ...

            // SPEC VIOLATION: Native compiler allows the above code to compile too
            // SPEC VIOLATION: even though there are two user-defined implicit conversions:
            // SPEC VIOLATION: 1) To string type (applicable in normal form): public static implicit operator string (Conv? C2)
            // SPEC VIOLATION: 2) To int? type (applicable in half-lifted form): public static implicit operator int? (Conv C)

            // SPEC VIOLATION: This occurs because the native compiler compares the applicable conversions to find one with the least amount
            // SPEC VIOLATION: of lifting, ignoring whether the return types are the same or not.
            // SPEC VIOLATION: We do the same to maintain compatibility with the native compiler.

            // (a) Compute the set of types D from which user-defined conversion operators should be considered by considering only the source type.
            var d = ArrayBuilder <TypeSymbol> .GetInstance();

            ComputeUserDefinedImplicitConversionTypeSet(source, t: null, d: d, useSiteInfo: ref useSiteInfo);

            // (b) Instead of computing applicable user defined implicit conversions U from the source type to a specific target type,
            //     we compute these from the source type to ANY target type. We will filter out those that are valid switch governing
            //     types later.
            var ubuild = ArrayBuilder <UserDefinedConversionAnalysis> .GetInstance();

            ComputeApplicableUserDefinedImplicitConversionSet(null, source, target: null, d: d, u: ubuild, useSiteInfo: ref useSiteInfo, allowAnyTarget: true);
            d.Free();
            ImmutableArray <UserDefinedConversionAnalysis> u = ubuild.ToImmutableAndFree();

            // (c) Find that conversion with the least amount of lifting
            int?best = MostSpecificConversionOperator(conv => conv.ToType.IsValidV6SwitchGoverningType(isTargetTypeOfUserDefinedOp: true), u);

            if (best != null)
            {
                return(UserDefinedConversionResult.Valid(u, best.Value));
            }

            return(UserDefinedConversionResult.NoApplicableOperators(u));
        }
 public override Conversion GetStackAllocConversion(BoundStackAllocArrayCreation sourceExpression, TypeSymbol destination, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
 {
     // Conversions involving stackalloc expressions require a Binder.
     throw ExceptionUtilities.Unreachable;
 }
Beispiel #24
0
        internal void LookupSymbolInAliases(
            Binder originalBinder,
            LookupResult result,
            string name,
            int arity,
            ConsList <TypeSymbol> basesBeingResolved,
            LookupOptions options,
            bool diagnose,
            ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo
            )
        {
            bool callerIsSemanticModel = originalBinder.IsSemanticModelBinder;

            AliasAndUsingDirective alias;

            if (this.UsingAliases.TryGetValue(name, out alias))
            {
                // Found a match in our list of normal aliases.  Mark the alias as being seen so that
                // it won't be reported to the user as something that can be removed.
                var res = originalBinder.CheckViability(
                    alias.Alias,
                    arity,
                    options,
                    null,
                    diagnose,
                    ref useSiteInfo,
                    basesBeingResolved
                    );
                if (res.Kind == LookupResultKind.Viable)
                {
                    MarkImportDirective(alias.UsingDirective, callerIsSemanticModel);
                }

                result.MergeEqual(res);
            }

            foreach (var a in this.ExternAliases)
            {
                if (a.Alias.Name == name)
                {
                    // Found a match in our list of extern aliases.  Mark the extern alias as being
                    // seen so that it won't be reported to the user as something that can be
                    // removed.
                    var res = originalBinder.CheckViability(
                        a.Alias,
                        arity,
                        options,
                        null,
                        diagnose,
                        ref useSiteInfo,
                        basesBeingResolved
                        );
                    if (res.Kind == LookupResultKind.Viable)
                    {
                        MarkImportDirective(a.ExternAliasDirective, callerIsSemanticModel);
                    }

                    result.MergeEqual(res);
                }
            }
        }
Beispiel #25
0
        internal override bool IsAccessibleHelper(Symbol symbol, TypeSymbol accessThroughType, out bool failedThroughTypeCheck, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo, ConsList <TypeSymbol> basesBeingResolved)
        {
            var type = _container as NamedTypeSymbol;

            if ((object)type != null)
            {
                return(this.IsSymbolAccessibleConditional(symbol, type, accessThroughType, out failedThroughTypeCheck, ref useSiteInfo));
            }
            else
            {
                return(Next.IsAccessibleHelper(symbol, accessThroughType, out failedThroughTypeCheck, ref useSiteInfo, basesBeingResolved));  // delegate to containing Binder, eventually checking assembly.
            }
        }
Beispiel #26
0
        /// <summary>
        /// Recursively builds a Conversion object with Kind=Deconstruction including information about any necessary
        /// Deconstruct method and any element-wise conversion.
        ///
        /// Note that the variables may either be plain or nested variables.
        /// The variables may be updated with inferred types if they didn't have types initially.
        /// Returns false if there was an error.
        /// </summary>
        private bool MakeDeconstructionConversion(
            TypeSymbol type,
            SyntaxNode syntax,
            SyntaxNode rightSyntax,
            BindingDiagnosticBag diagnostics,
            ArrayBuilder <DeconstructionVariable> variables,
            out Conversion conversion)
        {
            Debug.Assert((object)type != null);
            ImmutableArray <TypeSymbol> tupleOrDeconstructedTypes;

            conversion = Conversion.Deconstruction;

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

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

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

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

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

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

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

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

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

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

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

                    hasErrors |= !MakeDeconstructionConversion(tupleOrDeconstructedTypes[i], elementSyntax, rightSyntax, diagnostics,
                                                               variable.NestedVariables, out nestedConversion);
                }
                else
                {
                    var single = variable.Single;
                    Debug.Assert(single is object);
                    CompoundUseSiteInfo <AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics);
                    nestedConversion = this.Conversions.ClassifyConversionFromType(tupleOrDeconstructedTypes[i], single.Type, ref useSiteInfo);
                    diagnostics.Add(single.Syntax, useSiteInfo);

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

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

            return(!hasErrors);
        }
        internal override void LookupSymbolsInSingleBinder(
            LookupResult result, string name, int arity, ConsList <TypeSymbol> basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            Debug.Assert(result.IsClear);

            LookupSymbolInAliases(
                this.GetUsingAliasesMap(basesBeingResolved),
                this.ExternAliases,
                originalBinder,
                result,
                name,
                arity,
                basesBeingResolved,
                options,
                diagnose,
                ref useSiteInfo);
        }
Beispiel #28
0
        private BetterResult BetterOperator(UnaryOperatorSignature op1, UnaryOperatorSignature op2, BoundExpression operand, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            // First we see if the conversion from the operand to one operand type is better than
            // the conversion to the other.

            BetterResult better = BetterConversionFromExpression(operand, op1.OperandType, op2.OperandType, ref useSiteInfo);

            if (better == BetterResult.Left || better == BetterResult.Right)
            {
                return(better);
            }

            // There was no better member on the basis of conversions. Go to the tiebreaking round.

            // SPEC: In case the parameter type sequences P1, P2 and Q1, Q2 are equivalent -- that is, every Pi
            // SPEC: has an identity conversion to the corresponding Qi -- the following tie-breaking rules
            // SPEC: are applied:

            if (Conversions.HasIdentityConversion(op1.OperandType, op2.OperandType))
            {
                // SPEC: If Mp has more specific parameter types than Mq then Mp is better than Mq.

                // Under what circumstances can two unary operators with identical signatures be "more specific"
                // than another? With a binary operator you could have C<T>.op+(C<T>, T) and C<T>.op+(C<T>, int).
                // When doing overload resolution on C<int> + int, the latter is more specific. But with a unary
                // operator, the sole operand *must* be the containing type or its nullable type. Therefore
                // if there is an identity conversion, then the parameters really were identical. We therefore
                // skip checking for specificity.

                // SPEC: If one member is a non-lifted operator and the other is a lifted operator,
                // SPEC: the non-lifted one is better.

                bool lifted1 = op1.Kind.IsLifted();
                bool lifted2 = op2.Kind.IsLifted();

                if (lifted1 && !lifted2)
                {
                    return(BetterResult.Right);
                }
                else if (!lifted1 && lifted2)
                {
                    return(BetterResult.Left);
                }
            }

            // Always prefer operators with val parameters over operators with in parameters:
            if (op1.RefKind == RefKind.None && op2.RefKind == RefKind.In)
            {
                return(BetterResult.Left);
            }
            else if (op2.RefKind == RefKind.None && op1.RefKind == RefKind.In)
            {
                return(BetterResult.Right);
            }

            return(BetterResult.Neither);
        }
Beispiel #29
0
        private static MethodSymbol InferExtensionMethodTypeArguments(MethodSymbol method, TypeSymbol thisType, CSharpCompilation compilation, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            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(),
                useSiteInfo: ref useSiteInfo);

            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(new ConstraintsHelper.CheckConstraintsArgs(compilation, conversions, includeNullability: false, NoLocation.Singleton, diagnostics: null, template: new CompoundUseSiteInfo <AssemblySymbol>(useSiteInfo)),
                                                  substitution, typeParams, typeArgsForConstraintsCheck, diagnosticsBuilder, nullabilityDiagnosticsBuilderOpt: null,
                                                  ref useSiteDiagnosticsBuilder,
                                                  ignoreTypeConstraintsDependentOnTypeParametersOpt: notInferredTypeParameters.Count > 0 ? notInferredTypeParameters : null);

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

            if (useSiteDiagnosticsBuilder != null && useSiteDiagnosticsBuilder.Count > 0)
            {
                foreach (var diag in useSiteDiagnosticsBuilder)
                {
                    useSiteInfo.Add(diag.UseSiteInfo);
                }
            }

            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));
        }
            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));
                }