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