Пример #1
0
        internal override void GenerateMethodBody(TypeCompilationState compilationState, BindingDiagnosticBag diagnostics)
        {
            var F = new SyntheticBoundNodeFactory(this, ContainingType.GetNonNullSyntaxNode(), compilationState, diagnostics);

            if (ParameterCount != _properties.Length)
            {
                // There is a mismatch, an error was reported elsewhere
                F.CloseMethod(F.ThrowNull());
                return;
            }

            var statementsBuilder = ArrayBuilder <BoundStatement> .GetInstance(_properties.Length + 1);

            for (int i = 0; i < _properties.Length; i++)
            {
                var parameter = Parameters[i];
                var property  = _properties[i];

                if (!parameter.Type.Equals(property.Type, TypeCompareKind.AllIgnoreOptions))
                {
                    // There is a mismatch, an error was reported elsewhere
                    statementsBuilder.Free();
                    F.CloseMethod(F.ThrowNull());
                    return;
                }

                // parameter_i = property_i;
                statementsBuilder.Add(F.Assignment(F.Parameter(parameter), F.Property(F.This(), property)));
            }

            statementsBuilder.Add(F.Return());
            F.CloseMethod(F.Block(statementsBuilder.ToImmutableAndFree()));
        }
Пример #2
0
        internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics)
        {
            var F = new SyntheticBoundNodeFactory(this, ContainingType.GetNonNullSyntaxNode(), compilationState, diagnostics);

            var paramAccess = F.Parameter(Parameters[0]);

            BoundExpression expression;

            if (ContainingType.IsStructType())
            {
                // For structs:
                //
                //      return param is ContainingType i ? this.Equals(in i) : false;
                expression = F.Conditional(
                    F.Is(paramAccess, ContainingType),
                    F.Call(
                        F.This(),
                        _typedRecordEquals,
                        ImmutableArray.Create <RefKind>(RefKind.In),
                        ImmutableArray.Create <BoundExpression>(F.Convert(ContainingType, paramAccess))),
                    F.Literal(false),
                    F.SpecialType(SpecialType.System_Boolean));
            }
            else
            {
                // For classes:
                //      return this.Equals(param as ContainingType);
                expression = F.Call(F.This(), _typedRecordEquals, F.As(paramAccess, ContainingType));
            }

            F.CloseMethod(F.Block(ImmutableArray.Create <BoundStatement>(F.Return(expression))));
        }
        internal override void GenerateMethodBody(
            TypeCompilationState compilationState,
            BindingDiagnosticBag diagnostics
            )
        {
            var F = new SyntheticBoundNodeFactory(
                this,
                ContainingType.GetNonNullSyntaxNode(),
                compilationState,
                diagnostics
                );

            try
            {
                // => (object)left == right || ((object)left != null && left.Equals(right));
                MethodSymbol?equals = null;
                foreach (var member in ContainingType.GetMembers(WellKnownMemberNames.ObjectEquals))
                {
                    if (
                        member is MethodSymbol candidate &&
                        candidate.ParameterCount == 1 &&
                        candidate.Parameters[0].RefKind == RefKind.None &&
                        candidate.ReturnType.SpecialType == SpecialType.System_Boolean &&
                        !candidate.IsStatic &&
                        candidate.Parameters[0].Type.Equals(
                            ContainingType,
                            TypeCompareKind.AllIgnoreOptions
                            )
                        )
                    {
                        equals = candidate;
                        break;
                    }
                }

                if (equals is null)
                {
                    // Unable to locate expected method, an error was reported elsewhere
                    F.CloseMethod(F.ThrowNull());
                    return;
                }

                var left  = F.Parameter(Parameters[0]);
                var right = F.Parameter(Parameters[1]);

                BoundExpression objectEqual  = F.ObjectEqual(left, right);
                BoundExpression recordEquals = F.LogicalAnd(
                    F.ObjectNotEqual(left, F.Null(F.SpecialType(SpecialType.System_Object))),
                    F.Call(left, equals, right)
                    );

                F.CloseMethod(F.Block(F.Return(F.LogicalOr(objectEqual, recordEquals))));
            }
            catch (SyntheticBoundNodeFactory.MissingPredefinedMember ex)
            {
                diagnostics.Add(ex.Diagnostic);
                F.CloseMethod(F.ThrowNull());
            }
        }
        internal override void GenerateMethodBody(TypeCompilationState compilationState, BindingDiagnosticBag diagnostics)
        {
            var F = new SyntheticBoundNodeFactory(this, ContainingType.GetNonNullSyntaxNode(), compilationState, diagnostics);

            try
            {
                ImmutableArray <Symbol> printableMembers = ContainingType.GetMembers().WhereAsArray(m => isPrintable(m));

                if (ReturnType.IsErrorType() ||
                    printableMembers.Any(static m => m.GetTypeOrReturnType().Type.IsErrorType()))
        internal override void GenerateMethodBody(TypeCompilationState compilationState, BindingDiagnosticBag diagnostics)
        {
            var F = new SyntheticBoundNodeFactory(this, ContainingType.GetNonNullSyntaxNode(), compilationState, diagnostics);

            if (ParameterCount != _positionalMembers.Length)
            {
                // There is a mismatch, an error was reported elsewhere
                F.CloseMethod(F.ThrowNull());
                return;
            }

            var statementsBuilder = ArrayBuilder <BoundStatement> .GetInstance(_positionalMembers.Length + 1);

            for (int i = 0; i < _positionalMembers.Length; i++)
            {
                var parameter        = Parameters[i];
                var positionalMember = _positionalMembers[i];

                var type = positionalMember switch
                {
                    PropertySymbol property => property.Type,
                    FieldSymbol field => field.Type,
                    _ => throw ExceptionUtilities.Unreachable
                };

                if (!parameter.Type.Equals(type, TypeCompareKind.AllIgnoreOptions))
                {
                    // There is a mismatch, an error was reported elsewhere
                    statementsBuilder.Free();
                    F.CloseMethod(F.ThrowNull());
                    return;
                }

                switch (positionalMember)
                {
                case PropertySymbol property:
                    // parameter_i = property_i;
                    statementsBuilder.Add(F.Assignment(F.Parameter(parameter), F.Property(F.This(), property)));
                    break;

                case FieldSymbol field:
                    // parameter_i = field_i;
                    statementsBuilder.Add(F.Assignment(F.Parameter(parameter), F.Field(F.This(), field)));
                    break;
                }
            }

            statementsBuilder.Add(F.Return());
            F.CloseMethod(F.Block(statementsBuilder.ToImmutableAndFree()));
        }
    }
        internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics)
        {
            var F = new SyntheticBoundNodeFactory(this, ContainingType.GetNonNullSyntaxNode(), compilationState, diagnostics);

            try
            {
                // => !(r1 == r2);
                F.CloseMethod(F.Block(F.Return(F.Not(F.Call(receiver: null, ContainingType.GetMembers(WellKnownMemberNames.EqualityOperatorName).OfType <SynthesizedRecordEqualityOperator>().Single(),
                                                            F.Parameter(Parameters[0]), F.Parameter(Parameters[1]))))));
            }
            catch (SyntheticBoundNodeFactory.MissingPredefinedMember ex)
            {
                diagnostics.Add(ex.Diagnostic);
                F.CloseMethod(F.ThrowNull());
            }
        }
        internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics)
        {
            var F = new SyntheticBoundNodeFactory(this, ContainingType.GetNonNullSyntaxNode(), compilationState, diagnostics);

            try
            {
                MethodSymbol equalityComparer_GetHashCode = F.WellKnownMethod(WellKnownMember.System_Collections_Generic_EqualityComparer_T__GetHashCode, isOptional: false) !;
                MethodSymbol equalityComparer_get_Default = F.WellKnownMethod(WellKnownMember.System_Collections_Generic_EqualityComparer_T__get_Default, isOptional: false) !;

                BoundExpression currentHashValue;

                if (ContainingType.BaseTypeNoUseSiteDiagnostics.IsObjectType())
                {
                    // There are no base record types.
                    // Get hash code of the equality contract and combine it with hash codes for field values.
                    currentHashValue = MethodBodySynthesizer.GenerateGetHashCode(equalityComparer_GetHashCode, equalityComparer_get_Default, F.Property(F.This(), _equalityContract), F);
                }
                else
                {
                    // There are base record types.
                    // Get base.GetHashCode() and combine it with hash codes for field values.
                    var overridden = OverriddenMethod;
                    currentHashValue = F.Call(F.Base(overridden.ContainingType), overridden);
                }

                //  bound HASH_FACTOR
                BoundLiteral?boundHashFactor = null;

                foreach (var f in ContainingType.GetFieldsToEmit())
                {
                    if (!f.IsStatic)
                    {
                        currentHashValue = MethodBodySynthesizer.GenerateHashCombine(currentHashValue, equalityComparer_GetHashCode, equalityComparer_get_Default, ref boundHashFactor,
                                                                                     F.Field(F.This(), f),
                                                                                     F);
                    }
                }

                F.CloseMethod(F.Block(F.Return(currentHashValue)));
            }
            catch (SyntheticBoundNodeFactory.MissingPredefinedMember ex)
            {
                diagnostics.Add(ex.Diagnostic);
                F.CloseMethod(F.ThrowNull());
            }
        }
Пример #8
0
        internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics)
        {
            var F = new SyntheticBoundNodeFactory(this, ContainingType.GetNonNullSyntaxNode(), compilationState, diagnostics);

            var paramAccess = F.Parameter(Parameters[0]);

            BoundExpression expression;

            if (ContainingType.IsStructType())
            {
                throw ExceptionUtilities.Unreachable;
            }
            else
            {
                // For classes:
                //      return this.Equals(param as ContainingType);
                expression = F.Call(F.This(), _typedRecordEquals, F.As(paramAccess, ContainingType));
            }

            F.CloseMethod(F.Block(ImmutableArray.Create <BoundStatement>(F.Return(expression))));
        }
        public override ImmutableArray <Symbol> GetMembers()
        {
            // @t-mawind
            //   Not making this lazy results in new symbols being created every
            //   time we call GetMembers(), which is not only inefficient but
            //   breaks reference equality.
            if (_members.IsDefault)
            {
                var mb = ArrayBuilder <Symbol> .GetInstance();

                mb.AddRange(base.GetMembers());

                // @t-mawind
                //   This is slightly wrong, but we don't have any syntax to
                //   cling onto apart from this...
                var binder = DeclaringCompilation.GetBinder(ContainingType.GetNonNullSyntaxNode());

                var diagnostics = DiagnosticBag.GetInstance();

                var memberSyntax = _concept.GetConceptDefaultMethods();
                foreach (var m in memberSyntax)
                {
                    var ms = m.GetSyntax() as MethodDeclarationSyntax;
                    if (ms == null)
                    {
                        continue;
                    }

                    mb.Add(SourceMemberMethodSymbol.CreateMethodSymbol(this, binder, ms, diagnostics));
                }

                AddDeclarationDiagnostics(diagnostics);

                ImmutableInterlocked.InterlockedInitialize(ref _members, mb.ToImmutableAndFree());
            }
            return(_members);
        }
Пример #10
0
        internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics)
        {
            var             F     = new SyntheticBoundNodeFactory(this, ContainingType.GetNonNullSyntaxNode(), compilationState, diagnostics);
            var             other = F.Parameter(Parameters[0]);
            BoundExpression?retExpr;

            if (IsOverride)
            {
                // This method is an override of a strongly-typed Equals method from a base record type.
                // The definition of the method is as follows, and _otherEqualsMethod
                // is the method to delegate to (see B.Equals(A), C.Equals(A), C.Equals(B) above):
                //
                // override bool Equals(Base other) => Equals(other as Derived);
                retExpr = F.Call(
                    F.This(),
                    _otherEqualsMethod !,
                    F.As(other, ContainingType));
            }
            else
            {
                // This method is the strongly-typed Equals method where the parameter type is
                // the containing type.

                if (_otherEqualsMethod is null)
                {
                    // There are no base record types.
                    // The definition of the method is as follows (see A.Equals(A) above):
                    //
                    // virtual bool Equals(T other) =>
                    //     other != null &&
                    //     EqualityContract == other.EqualityContract &&
                    //     field1 == other.field1 && ... && fieldN == other.fieldN;

                    // other != null
                    Debug.Assert(!other.Type.IsStructType());
                    retExpr = F.ObjectNotEqual(other, F.Null(F.SpecialType(SpecialType.System_Object)));

                    // EqualityContract == other.EqualityContract
                    var contractsEqual = F.Binary(
                        BinaryOperatorKind.ObjectEqual,
                        F.SpecialType(SpecialType.System_Boolean),
                        F.Property(F.This(), _equalityContract),
                        F.Property(other, _equalityContract));

                    retExpr = retExpr is null ? contractsEqual : F.LogicalAnd(retExpr, contractsEqual);
                }
                else
                {
                    // There are base record types.
                    // The definition of the method is as follows, and _otherEqualsMethod
                    // is the corresponding method on the nearest base record type to
                    // delegate to (see B.Equals(B), C.Equals(C) above):
                    //
                    // virtual bool Equals(Derived other) =>
                    //     base.Equals((Base)other) &&
                    //     field1 == other.field1 && ... && fieldN == other.fieldN;
                    retExpr = F.Call(
                        F.Base(_otherEqualsMethod.ContainingType),
                        _otherEqualsMethod !,
                        F.Convert(_otherEqualsMethod.Parameters[0].Type, other));
                }

                // field1 == other.field1 && ... && fieldN == other.fieldN
                // https://github.com/dotnet/roslyn/issues/44895: Should compare fields from non-record base classes.
                var fields = ArrayBuilder <FieldSymbol> .GetInstance();

                foreach (var f in ContainingType.GetFieldsToEmit())
                {
                    if (!f.IsStatic)
                    {
                        fields.Add(f);
                    }
                }
                if (fields.Count > 0)
                {
                    retExpr = MethodBodySynthesizer.GenerateFieldEquals(
                        retExpr,
                        other,
                        fields,
                        F);
                }
                fields.Free();
            }

            F.CloseMethod(F.Block(F.Return(retExpr)));
        }
        internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics)
        {
            var F = new SyntheticBoundNodeFactory(this, ContainingType.GetNonNullSyntaxNode(), compilationState, diagnostics);

            try
            {
                var             other = F.Parameter(Parameters[0]);
                BoundExpression?retExpr;

                // This method is the strongly-typed Equals method where the parameter type is
                // the containing type.

                if (ContainingType.BaseTypeNoUseSiteDiagnostics.IsObjectType())
                {
                    if (_equalityContract.GetMethod is null)
                    {
                        // The equality contract isn't usable, an error was reported elsewhere
                        F.CloseMethod(F.ThrowNull());
                        return;
                    }

                    if (_equalityContract.IsStatic || !_equalityContract.Type.Equals(DeclaringCompilation.GetWellKnownType(WellKnownType.System_Type), TypeCompareKind.AllIgnoreOptions))
                    {
                        // There is a signature mismatch, an error was reported elsewhere
                        F.CloseMethod(F.ThrowNull());
                        return;
                    }

                    // There are no base record types.
                    // The definition of the method is as follows
                    //
                    // virtual bool Equals(T other) =>
                    //     other != null &&
                    //     EqualityContract == other.EqualityContract &&
                    //     field1 == other.field1 && ... && fieldN == other.fieldN;

                    // other != null
                    Debug.Assert(!other.Type.IsStructType());
                    retExpr = F.ObjectNotEqual(other, F.Null(F.SpecialType(SpecialType.System_Object)));

                    // EqualityContract == other.EqualityContract
                    var contractsEqual = F.Call(receiver: null, F.WellKnownMethod(WellKnownMember.System_Type__op_Equality),
                                                F.Property(F.This(), _equalityContract),
                                                F.Property(other, _equalityContract));

                    retExpr = F.LogicalAnd(retExpr, contractsEqual);
                }
                else
                {
                    MethodSymbol?baseEquals = ContainingType.GetMembersUnordered().OfType <SynthesizedRecordBaseEquals>().Single().OverriddenMethod;

                    if (baseEquals is null || !baseEquals.ContainingType.Equals(ContainingType.BaseTypeNoUseSiteDiagnostics, TypeCompareKind.AllIgnoreOptions) ||
                        baseEquals.ReturnType.SpecialType != SpecialType.System_Boolean)
                    {
                        // There was a problem with overriding of base equals, an error was reported elsewhere
                        F.CloseMethod(F.ThrowNull());
                        return;
                    }

                    // There are base record types.
                    // The definition of the method is as follows, and baseEquals
                    // is the corresponding method on the nearest base record type to
                    // delegate to:
                    //
                    // virtual bool Equals(Derived other) =>
                    //     base.Equals((Base)other) &&
                    //     field1 == other.field1 && ... && fieldN == other.fieldN;
                    retExpr = F.Call(
                        F.Base(baseEquals.ContainingType),
                        baseEquals,
                        F.Convert(baseEquals.Parameters[0].Type, other));
                }

                // field1 == other.field1 && ... && fieldN == other.fieldN
                var fields = ArrayBuilder <FieldSymbol> .GetInstance();

                bool foundBadField = false;
                foreach (var f in ContainingType.GetFieldsToEmit())
                {
                    if (!f.IsStatic)
                    {
                        fields.Add(f);

                        var parameterType = f.Type;
                        if (parameterType.IsUnsafe())
                        {
                            diagnostics.Add(ErrorCode.ERR_BadFieldTypeInRecord, f.Locations.FirstOrNone(), parameterType);
                            foundBadField = true;
                        }
                        else if (parameterType.IsRestrictedType())
                        {
                            // We'll have reported a diagnostic elsewhere (SourceMemberFieldSymbol.TypeChecks)
                            foundBadField = true;
                        }
                    }
                }
                if (fields.Count > 0 && !foundBadField)
                {
                    retExpr = MethodBodySynthesizer.GenerateFieldEquals(
                        retExpr,
                        other,
                        fields,
                        F);
                }

                fields.Free();

                F.CloseMethod(F.Block(F.Return(retExpr)));
            }
            catch (SyntheticBoundNodeFactory.MissingPredefinedMember ex)
            {
                diagnostics.Add(ex.Diagnostic);
                F.CloseMethod(F.ThrowNull());
            }
        }
        internal override void GenerateMethodBody(TypeCompilationState compilationState, BindingDiagnosticBag diagnostics)
        {
            var F = new SyntheticBoundNodeFactory(this, ContainingType.GetNonNullSyntaxNode(), compilationState, diagnostics);

            try
            {
                ImmutableArray <Symbol> printableMembers = ContainingType.GetMembers().WhereAsArray(m => isPrintable(m));

                if (ReturnType.IsErrorType() ||
                    printableMembers.Any(m => m.GetTypeOrReturnType().Type.IsErrorType()))
                {
                    F.CloseMethod(F.ThrowNull());
                    return;
                }

                ArrayBuilder <BoundStatement> block;
                BoundParameter builder = F.Parameter(this.Parameters[0]);
                if (ContainingType.BaseTypeNoUseSiteDiagnostics.IsObjectType() || ContainingType.IsRecordStruct)
                {
                    if (printableMembers.IsEmpty)
                    {
                        // return false;
                        F.CloseMethod(F.Return(F.Literal(false)));
                        return;
                    }
                    block = ArrayBuilder <BoundStatement> .GetInstance();

                    if (!ContainingType.IsRecordStruct)
                    {
                        var ensureStackMethod = F.WellKnownMethod(
                            WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__EnsureSufficientExecutionStack,
                            isOptional: true);
                        if (ensureStackMethod is not null)
                        {
                            block.Add(F.ExpressionStatement(
                                          F.Call(receiver: null, ensureStackMethod)));
                        }
                    }
                }
                else
                {
                    MethodSymbol?basePrintMethod = OverriddenMethod;
                    if (basePrintMethod is null ||
                        basePrintMethod.ReturnType.SpecialType != SpecialType.System_Boolean)
                    {
                        F.CloseMethod(F.ThrowNull()); // an error was reported in base checks already
                        return;
                    }

                    var basePrintCall = F.Call(receiver: F.Base(ContainingType.BaseTypeNoUseSiteDiagnostics), basePrintMethod, builder);
                    if (printableMembers.IsEmpty)
                    {
                        // return base.PrintMembers(builder);
                        F.CloseMethod(F.Return(basePrintCall));
                        return;
                    }
                    else
                    {
                        block = ArrayBuilder <BoundStatement> .GetInstance();

                        // if (base.PrintMembers(builder))
                        //     builder.Append(", ")
                        block.Add(F.If(basePrintCall, makeAppendString(F, builder, ", ")));
                    }
                }

                Debug.Assert(!printableMembers.IsEmpty);

                for (var i = 0; i < printableMembers.Length; i++)
                {
                    // builder.Append(", <name> = "); // if previous members exist
                    // builder.Append("<name> = "); // if it is the first member

                    // The only printable members are fields and properties,
                    // which cannot be generic so as to have variant names

                    var member       = printableMembers[i];
                    var memberHeader = $"{member.Name} = ";
                    if (i > 0)
                    {
                        memberHeader = ", " + memberHeader;
                    }

                    block.Add(makeAppendString(F, builder, memberHeader));

                    var value = member.Kind switch
                    {
                        SymbolKind.Field => F.Field(F.This(), (FieldSymbol)member),
                        SymbolKind.Property => F.Property(F.This(), (PropertySymbol)member),
                        _ => throw ExceptionUtilities.UnexpectedValue(member.Kind)
                    };

                    // builder.Append((object)<value>); OR builder.Append(<value>.ToString()); for value types

                    Debug.Assert(value.Type is not null);
                    if (value.Type.IsValueType)
                    {
                        block.Add(F.ExpressionStatement(
                                      F.Call(receiver: builder,
                                             F.WellKnownMethod(WellKnownMember.System_Text_StringBuilder__AppendString),
                                             F.Call(value, F.SpecialMethod(SpecialMember.System_Object__ToString)))));
                    }
                    else
                    {
                        block.Add(F.ExpressionStatement(
                                      F.Call(receiver: builder,
                                             F.WellKnownMethod(WellKnownMember.System_Text_StringBuilder__AppendObject),
                                             F.Convert(F.SpecialType(SpecialType.System_Object), value))));
                    }
                }

                block.Add(F.Return(F.Literal(true)));

                F.CloseMethod(F.Block(block.ToImmutableAndFree()));
            }
            catch (SyntheticBoundNodeFactory.MissingPredefinedMember ex)
            {
                diagnostics.Add(ex.Diagnostic);
                F.CloseMethod(F.ThrowNull());
            }