Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
ce62431
Add well-known members
DoctorKrolic Jan 11, 2024
ed3da0e
Better null-annotate `TryGetWellKnownTypeMember`
DoctorKrolic Jan 11, 2024
6958b61
Optimize for 2 arguments
DoctorKrolic Jan 11, 2024
ba7663d
Optimize for 3 arguments
DoctorKrolic Jan 11, 2024
9e10fe6
Optimize for 4 arguments
DoctorKrolic Jan 11, 2024
db98d9f
Test concat of 5 chars
DoctorKrolic Jan 12, 2024
bfb7388
Add missing grouping cases for concat of 4
DoctorKrolic Jan 12, 2024
ce042d0
Add missing tests for concat 2 arguments case
DoctorKrolic Jan 12, 2024
05f8250
Extract span-based rewrite into common helper and try to merge with u…
DoctorKrolic Jan 13, 2024
95d901f
Merge branch 'dotnet:main' into concat-string-char-v2
DoctorKrolic Jan 24, 2024
a288d21
Handle missing `object.ToString`
DoctorKrolic Jan 24, 2024
176f3e6
Handle `constantChar.ToString()` case
DoctorKrolic Jan 24, 2024
e1766d3
Fix tests on .NET Framework
DoctorKrolic Jan 24, 2024
e4e26f1
Move assert to avoid nullability warning
DoctorKrolic Jan 24, 2024
2c4bf14
Rename
DoctorKrolic Jan 29, 2024
70656d5
Use array builder
DoctorKrolic Jan 30, 2024
da90a6b
Use block body
DoctorKrolic Jan 30, 2024
f5f7efe
Revert naming
DoctorKrolic Jan 30, 2024
e2ea475
Feedback
DoctorKrolic Jan 30, 2024
404d7fc
Get member from compilation
DoctorKrolic Jan 30, 2024
b6bf346
Improve error location of missing `object.ToString()` for concatenati…
DoctorKrolic Jan 31, 2024
083c380
Don't bother about user-made concat calls and leave them as is
DoctorKrolic Feb 3, 2024
1118dcf
Handle case when `char` doesn't override `object.ToString()`
DoctorKrolic Feb 3, 2024
2b11728
Fix
DoctorKrolic Feb 4, 2024
6c3423e
Track sequence locals
DoctorKrolic Feb 6, 2024
54765fa
Early return
DoctorKrolic Feb 6, 2024
199862e
Still produce span-based concat when `char` doesn't directly override…
DoctorKrolic Feb 6, 2024
91888a3
Throw unreachable exception
DoctorKrolic Feb 6, 2024
50bf4e2
Simplify
DoctorKrolic Feb 6, 2024
0f6302d
Drop
DoctorKrolic Feb 6, 2024
db25bda
Simplify
DoctorKrolic Feb 6, 2024
53c529f
Inline helper back
DoctorKrolic Feb 6, 2024
201fd04
Use pooled hash set
DoctorKrolic Feb 7, 2024
49a865f
Use switch statement
DoctorKrolic Feb 7, 2024
a3a58a6
Use containing type
DoctorKrolic Feb 7, 2024
1d3447c
Assert
DoctorKrolic Feb 7, 2024
3634b2f
Convert to `foreach`
DoctorKrolic Feb 8, 2024
0b048b8
Move into `if`
DoctorKrolic Feb 8, 2024
a384b97
Free pooled object
DoctorKrolic Feb 8, 2024
05a2e3c
Remove assert
DoctorKrolic Feb 8, 2024
71ea57d
Free
DoctorKrolic Feb 8, 2024
0cc529d
Handle ROS constructor ref kinds other than `ref readonly` or `in`
DoctorKrolic Feb 8, 2024
8d3a40b
Support `await` scenarios
DoctorKrolic Feb 12, 2024
04046af
Pass ref kind
DoctorKrolic Feb 12, 2024
323b50a
Feedback
DoctorKrolic Feb 12, 2024
14fb8c1
Extra safe
DoctorKrolic Feb 13, 2024
5df710b
Unify handling of constant and not constant chars during string concat
AlekseyTs Feb 13, 2024
3e841f7
Fixup
AlekseyTs Feb 13, 2024
5ddb579
Reduce amount of scenarios affected by unification
AlekseyTs Feb 15, 2024
c09ad71
Add `ReadOnlySpan<T>` to special types
DoctorKrolic Feb 18, 2024
0b650ee
Move to special members
DoctorKrolic Feb 18, 2024
19ed7b6
Add test cases
DoctorKrolic Feb 18, 2024
26bce1e
Align
DoctorKrolic Feb 18, 2024
ef5350c
Merge branch 'main' into concat-string-char-v2
DoctorKrolic Feb 18, 2024
a3b12ec
Fixes
DoctorKrolic Feb 18, 2024
facd5b1
Fix
DoctorKrolic Feb 19, 2024
5448edb
Doc
DoctorKrolic Feb 19, 2024
d23446f
System
DoctorKrolic Feb 20, 2024
91a4730
Adjust
DoctorKrolic Feb 20, 2024
ff510e7
System?
DoctorKrolic Feb 20, 2024
92de034
Add tests with `null` concatenation argument
DoctorKrolic Feb 27, 2024
9154924
Fix leaks
DoctorKrolic Feb 27, 2024
3d4e273
Add `ReadOnlySpan` field loading test for C#
DoctorKrolic Feb 28, 2024
f61c656
Fix and test VB
DoctorKrolic Feb 28, 2024
71ddb7b
WorkItem
DoctorKrolic Feb 28, 2024
3a8a619
Avoid comparisons to `SpecialType.None` due to extensibility issues
DoctorKrolic Feb 28, 2024
c62c748
Use `GetSpecialType` API in tests
DoctorKrolic Feb 28, 2024
82101b9
Comment
DoctorKrolic Feb 28, 2024
1a2907b
Update comment
DoctorKrolic Feb 28, 2024
e99de5d
Feedback
DoctorKrolic Feb 29, 2024
608c310
Typo
DoctorKrolic Feb 29, 2024
3b96ce6
Extract helper method
DoctorKrolic Mar 17, 2024
9132328
Merge branch 'dotnet:main' into concat-string-char-v2
DoctorKrolic Mar 18, 2024
482bcaa
Fix
DoctorKrolic Mar 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2294,9 +2294,12 @@ internal static bool MayUseCallForStructMethod(MethodSymbol method)
}

var containingType = method.ContainingType;
// overrides in structs that are special types can be called directly.
// we can assume that special types will not be removing overrides
return containingType.SpecialType != SpecialType.None;
// Overrides in structs of some special types can be called directly.
// We can assume that these special types will not be removing overrides.
// This pattern can probably be applied to all special types,
// but that would introduce a silent change every time a new special type is added,
// so we constrain the check to a fixed range of types
return containingType.SpecialType.CanOptimizeBehavior();
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#define REFERENCE_STATE
#endif

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
Expand Down Expand Up @@ -362,7 +361,7 @@ protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
if ((object)methodThisParameter != null)
{
EnterParameter(methodThisParameter);
if (methodThisParameter.Type.SpecialType != SpecialType.None)
if (methodThisParameter.Type.SpecialType.CanOptimizeBehavior())
{
int slot = GetOrCreateSlot(methodThisParameter);
SetSlotState(slot, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,10 @@

#nullable disable

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using System.Threading;

namespace Microsoft.CodeAnalysis.CSharp
{
Expand Down Expand Up @@ -131,7 +125,7 @@ public static bool IsTrackableStructType(TypeSymbol type)
if ((object)type == null) return false;
var nts = type.OriginalDefinition as NamedTypeSymbol;
if ((object)nts == null) return false;
return nts.IsStructType() && nts.SpecialType == SpecialType.None && !nts.KnownCircularStruct;
return nts.IsStructType() && !nts.SpecialType.CanOptimizeBehavior() && !nts.KnownCircularStruct;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4322,7 +4322,7 @@ protected override bool IsEmptyStructType(TypeSymbol type)
return false;
}

if (type.SpecialType != SpecialType.None)
if (type.SpecialType.CanOptimizeBehavior())
{
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public override BoundNode VisitArrayAccess(BoundArrayAccess node)
{
if (_inExpressionLambda &&
node.Indices.Length == 1 &&
node.Indices[0].Type!.SpecialType == SpecialType.None)
!node.Indices[0].Type!.SpecialType.CanOptimizeBehavior())
{
Error(ErrorCode.ERR_ExpressionTreeContainsPatternImplicitIndexer, node);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -593,11 +593,11 @@ private static BoundExpression BadExpression(SyntaxNode syntax, TypeSymbol resul
return new BoundBadExpression(syntax, LookupResultKind.NotReferencable, ImmutableArray<Symbol?>.Empty, children, resultType);
}

private bool TryGetWellKnownTypeMember<TSymbol>(SyntaxNode? syntax, WellKnownMember member, out TSymbol symbol, bool isOptional = false, Location? location = null) where TSymbol : Symbol
private bool TryGetWellKnownTypeMember<TSymbol>(SyntaxNode? syntax, WellKnownMember member, [NotNullWhen(true)] out TSymbol? symbol, bool isOptional = false, Location? location = null) where TSymbol : Symbol
{
Debug.Assert((syntax != null) ^ (location != null));

symbol = (TSymbol)Binder.GetWellKnownTypeMember(_compilation, member, _diagnostics, syntax: syntax, isOptional: isOptional, location: location);
symbol = (TSymbol?)Binder.GetWellKnownTypeMember(_compilation, member, _diagnostics, syntax: syntax, isOptional: isOptional, location: location);
return symbol is { };
}

Expand Down Expand Up @@ -652,7 +652,7 @@ public override BoundNode VisitTypeOfOperator(BoundTypeOfOperator node)
var type = this.VisitType(node.Type);

// Emit needs this helper
MethodSymbol getTypeFromHandle;
MethodSymbol? getTypeFromHandle;
if (!TryGetWellKnownTypeMember(node.Syntax, WellKnownMember.System_Type__GetTypeFromHandle, out getTypeFromHandle))
{
return new BoundTypeOfOperator(node.Syntax, sourceType, null, type, hasErrors: true);
Expand All @@ -669,7 +669,7 @@ public override BoundNode VisitRefTypeOperator(BoundRefTypeOperator node)
var type = this.VisitType(node.Type);

// Emit needs this helper
MethodSymbol getTypeFromHandle;
MethodSymbol? getTypeFromHandle;
if (!TryGetWellKnownTypeMember(node.Syntax, WellKnownMember.System_Type__GetTypeFromHandle, out getTypeFromHandle))
{
return new BoundRefTypeOperator(node.Syntax, operand, null, type, hasErrors: true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ private BoundExpression MakeUtf8Span(BoundExpression node, IReadOnlyList<byte>?
BadExpression(node.Syntax, byteArray, ImmutableArray<BoundExpression>.Empty) :
MakeUnderlyingArrayForUtf8Span(node.Syntax, byteArray, bytes, out length);

if (!TryGetWellKnownTypeMember<MethodSymbol>(node.Syntax, WellKnownMember.System_ReadOnlySpan_T__ctor_Array_Start_Length, out MethodSymbol ctor))
if (!TryGetWellKnownTypeMember<MethodSymbol>(node.Syntax, WellKnownMember.System_ReadOnlySpan_T__ctor_Array_Start_Length, out MethodSymbol? ctor))
{
result = BadExpression(node.Syntax, node.Type, ImmutableArray<BoundExpression>.Empty);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ private BoundExpression RewriteWindowsRuntimeEventAssignmentOperator(SyntaxNode
BoundExpression? clearCall = null;
if (kind == EventAssignmentKind.Assignment)
{
MethodSymbol clearMethod;
MethodSymbol? clearMethod;
if (TryGetWellKnownTypeMember(syntax, WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_WindowsRuntimeMarshal__RemoveAllEventHandlers, out clearMethod))
{
clearCall = MakeCall(
Expand Down Expand Up @@ -154,7 +154,7 @@ private BoundExpression RewriteWindowsRuntimeEventAssignmentOperator(SyntaxNode

BoundExpression marshalCall;

MethodSymbol marshalMethod;
MethodSymbol? marshalMethod;
if (TryGetWellKnownTypeMember(syntax, helper, out marshalMethod))
{
marshalMethod = marshalMethod.Construct(eventType);
Expand Down Expand Up @@ -248,7 +248,7 @@ private BoundExpression MakeEventAccess(

BoundExpression getOrCreateCall;

MethodSymbol getOrCreateMethod;
MethodSymbol? getOrCreateMethod;
if (TryGetWellKnownTypeMember(syntax, WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationTokenTable_T__GetOrCreateEventRegistrationTokenTable, out getOrCreateMethod))
{
getOrCreateMethod = getOrCreateMethod.AsMember(fieldType);
Expand All @@ -266,7 +266,7 @@ private BoundExpression MakeEventAccess(
getOrCreateCall = new BoundBadExpression(syntax, LookupResultKind.NotInvocable, ImmutableArray<Symbol?>.Empty, ImmutableArray.Create<BoundExpression>(fieldAccess), ErrorTypeSymbol.UnknownResultType);
}

PropertySymbol invocationListProperty;
PropertySymbol? invocationListProperty;
if (TryGetWellKnownTypeMember(syntax, WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationTokenTable_T__InvocationList, out invocationListProperty))
{
MethodSymbol invocationListAccessor = invocationListProperty.GetMethod;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ private BoundStatement InitializeFixedStatementStringLocal(
BoundExpression notNullCheck = _factory.MakeNullCheck(factory.Syntax, factory.Local(localSymbol), BinaryOperatorKind.NotEqual);
BoundExpression helperCall;

MethodSymbol offsetMethod;
MethodSymbol? offsetMethod;
if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__get_OffsetToStringData, out offsetMethod))
{
helperCall = factory.Call(receiver: null, method: offsetMethod);
Expand Down Expand Up @@ -534,7 +534,7 @@ private BoundStatement InitializeFixedStatementArrayLocal(
}
else
{
MethodSymbol lengthMethod;
MethodSymbol? lengthMethod;
if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Array__get_Length, out lengthMethod))
{
lengthCall = factory.Call(factory.Local(pinnedTemp), lengthMethod);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public override BoundNode VisitLockStatement(BoundLockStatement node)
BoundStatement boundLockTempInit = new BoundExpressionStatement(lockSyntax, assignmentToLockTemp);
BoundExpression exitCallExpr;

MethodSymbol exitMethod;
MethodSymbol? exitMethod;
if (TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Exit, out exitMethod))
{
exitCallExpr = BoundCall.Synthesized(
Expand All @@ -117,7 +117,7 @@ public override BoundNode VisitLockStatement(BoundLockStatement node)

BoundStatement exitCall = new BoundExpressionStatement(lockSyntax, exitCallExpr);

MethodSymbol enterMethod;
MethodSymbol? enterMethod;

if ((TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter2, out enterMethod, isOptional: true) ||
TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter, out enterMethod)) && // If we didn't find the overload introduced in .NET 4.0, then use the older one.
Expand Down Expand Up @@ -195,7 +195,7 @@ public override BoundNode VisitLockStatement(BoundLockStatement node)

BoundExpression enterCallExpr;

if ((object)enterMethod != null)
if ((object?)enterMethod != null)
{
Debug.Assert(enterMethod.ParameterCount == 1);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ private BoundExpression MakeNewT(SyntaxNode syntax, TypeParameterSymbol typePara
// if struct defines one.
// Since we cannot know if T has a parameterless constructor statically,
// we must call Activator.CreateInstance unconditionally.
MethodSymbol method;
MethodSymbol? method;

if (!this.TryGetWellKnownTypeMember(syntax, WellKnownMember.System_Activator__CreateInstance_T, out method))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ private BoundNode VisitStackAllocArrayCreationBase(BoundStackAllocArrayCreationB
stackAllocNode.Syntax, elementType, stackSize, initializerOpt, _compilation.CreatePointerTypeSymbol(elementType));

BoundExpression constructorCall;
if (TryGetWellKnownTypeMember(stackAllocNode.Syntax, WellKnownMember.System_Span_T__ctor_Pointer, out MethodSymbol spanConstructor))
if (TryGetWellKnownTypeMember(stackAllocNode.Syntax, WellKnownMember.System_Span_T__ctor_Pointer, out MethodSymbol? spanConstructor))
{
constructorCall = _factory.New((MethodSymbol)spanConstructor.SymbolAsMember(spanType), stackAllocNode, countTemp);
}
Expand Down
Loading