Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions eng/targets/TargetFrameworks.props
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<NetVSCode>net9.0</NetVSCode>
<NetVSShared>net8.0</NetVSShared>
<NetRoslynBuildHostNetCoreVersion>net8.0</NetRoslynBuildHostNetCoreVersion>
<NetRoslynNext>net9.0</NetRoslynNext>
<NetRoslynNext>net10.0</NetRoslynNext>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only projects using this value are now the Emit3 unit tests, and BuildBoss.

</PropertyGroup>

<!--
Expand Down Expand Up @@ -71,4 +71,4 @@
</PropertyGroup>
</Otherwise>
</Choose>
</Project>
</Project>
20 changes: 20 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis.CSharp.Symbols;
Expand Down Expand Up @@ -1836,6 +1837,7 @@ internal static NamedTypeSymbol GetWellKnownType(CSharpCompilation compilation,
return GetWellKnownType(compilation, type, diagnostics, node.Location);
}

#nullable enable
internal static NamedTypeSymbol GetWellKnownType(CSharpCompilation compilation, WellKnownType type, BindingDiagnosticBag diagnostics, Location location)
{
NamedTypeSymbol typeSymbol = compilation.GetWellKnownType(type);
Expand All @@ -1844,6 +1846,24 @@ internal static NamedTypeSymbol GetWellKnownType(CSharpCompilation compilation,
return typeSymbol;
}

internal static bool TryGetOptionalWellKnownType(CSharpCompilation compilation, WellKnownType type, BindingDiagnosticBag diagnostics, Location location, [NotNullWhen(true)] out NamedTypeSymbol? typeSymbol)
{
typeSymbol = compilation.GetWellKnownType(type);
Debug.Assert((object)typeSymbol != null, "Expect an error type if well-known type isn't found");
var useSiteInfo = typeSymbol.GetUseSiteInfo();
if (useSiteInfo.DiagnosticInfo?.Severity == DiagnosticSeverity.Error)
{
typeSymbol = null;
return false;
}

// Ignore warnings
useSiteInfo = new UseSiteInfo<AssemblySymbol>(diagnosticInfo: null, useSiteInfo.PrimaryDependency, useSiteInfo.SecondaryDependencies);
diagnostics.Add(useSiteInfo, location);
return true;
}
#nullable disable

/// <summary>
/// This is a layer on top of the Compilation version that generates a diagnostic if the well-known
/// type isn't found.
Expand Down
16 changes: 14 additions & 2 deletions src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2092,13 +2092,25 @@ internal MethodSymbol EnsureInlineArrayAsSpanExists(SyntaxNode syntaxNode, Named
diagnostics);
}

internal NamedTypeSymbol EnsureInlineArrayTypeExists(SyntaxNode syntaxNode, SyntheticBoundNodeFactory factory, int arrayLength, DiagnosticBag diagnostics)
internal NamedTypeSymbol EnsureInlineArrayTypeExists(SyntaxNode syntaxNode, SyntheticBoundNodeFactory factory, int arrayLength, BindingDiagnosticBag diagnostics)
{
Debug.Assert(Compilation.Assembly.RuntimeSupportsInlineArrayTypes);
Debug.Assert(arrayLength > 0);
Debug.Assert(diagnostics.DiagnosticBag is not null);

if (arrayLength is >= 2 and <= 16)
{
// In .NET 10 and up, the runtime provides InlineArrayN<T> types for N from 2 to 16.
var arrayWellKnownType = WellKnownType.System_Runtime_CompilerServices_InlineArray2 + (arrayLength - 2);
Debug.Assert(arrayWellKnownType is >= WellKnownType.System_Runtime_CompilerServices_InlineArray2 and <= WellKnownType.System_Runtime_CompilerServices_InlineArray16);
if (Binder.TryGetOptionalWellKnownType(Compilation, arrayWellKnownType, diagnostics, syntaxNode.Location, out var existingType))
{
return existingType;
}
}

string typeName = GeneratedNames.MakeSynthesizedInlineArrayName(arrayLength, CurrentGenerationOrdinal);
var privateImplClass = GetPrivateImplClass(syntaxNode, diagnostics).PrivateImplementationDetails;
var privateImplClass = GetPrivateImplClass(syntaxNode, diagnostics.DiagnosticBag).PrivateImplementationDetails;
var typeAdapter = privateImplClass.GetSynthesizedType(typeName);

if (typeAdapter is null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ private BoundExpression CreateAndPopulateSpanFromInlineArray(
return _factory.Sequence([assignment], call);
}

var inlineArrayType = _factory.ModuleBuilderOpt.EnsureInlineArrayTypeExists(syntax, _factory, arrayLength, _diagnostics.DiagnosticBag).Construct(ImmutableArray.Create(elementType));
var inlineArrayType = _factory.ModuleBuilderOpt.EnsureInlineArrayTypeExists(syntax, _factory, arrayLength, _diagnostics).Construct(ImmutableArray.Create(elementType));
Debug.Assert(inlineArrayType.HasInlineArrayAttribute(out int inlineArrayLength) && inlineArrayLength == arrayLength);

var intType = _factory.SpecialType(SpecialType.System_Int32);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<PropertyGroup>
<OutputType>Library</OutputType>
<RootNamespace>Microsoft.CodeAnalysis.CSharp.UnitTests</RootNamespace>
<TargetFrameworks>$(NetRoslyn);net472</TargetFrameworks>
<TargetFrameworks>$(NetRoslynNext);net472</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup Label="Project References">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46887,5 +46887,232 @@ public static SpanCollection<Span<int>> Test(Span<int> span1, Span<int> span2)
comp.VerifyDiagnostics(
);
}

[Theory]
[CombinatorialData]
public void BuiltInInlineArrayTypes([CombinatorialRange(1, 17)] int arrayLength)
{
string sourceA = """
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
[CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))]
public struct MyCollection<T> : IEnumerable<T>
{
private readonly List<T> _list;
public MyCollection(List<T> list) { _list = list; }
public IEnumerator<T> GetEnumerator() => _list.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
public class MyCollectionBuilder
{
public static MyCollection<T> Create<T>(ReadOnlySpan<T> items)
{
return new MyCollection<T>(new List<T>(items.ToArray()));
}
}
""";

var sourceB = $$"""
MyCollection<object> m = [{{string.Join(", ", Enumerable.Range(1, arrayLength))}}];
m.Report();
""";

var expectedOutput = $"[{string.Join(", ", Enumerable.Range(1, arrayLength))}],";
var ilVerifyFailure = arrayLength == 1
? Verification.FailsPEVerify
: Verification.Fails with
{
ILVerifyMessage = "[InlineArrayAsReadOnlySpan]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x11 }"
};
var verifier = CompileAndVerify([sourceA, sourceB, s_collectionExtensionsWithSpan], expectedOutput: IncludeExpectedOutput(expectedOutput), targetFramework: TargetFramework.Net100, verify: ilVerifyFailure, symbolValidator: verifyResult(shouldHaveSynthesizedArrayType: arrayLength == 17, arrayLength));
verifier.VerifyDiagnostics();
if (arrayLength == 2)
{
verifier.VerifyIL("<top-level-statements-entry-point>", """
{
// Code size 63 (0x3f)
.maxstack 2
.locals init (System.Runtime.CompilerServices.InlineArray2<object> V_0)
IL_0000: ldloca.s V_0
IL_0002: initobj "System.Runtime.CompilerServices.InlineArray2<object>"
IL_0008: ldloca.s V_0
IL_000a: ldc.i4.0
IL_000b: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<System.Runtime.CompilerServices.InlineArray2<object>, object>(ref System.Runtime.CompilerServices.InlineArray2<object>, int)"
IL_0010: ldc.i4.1
IL_0011: box "int"
IL_0016: stind.ref
IL_0017: ldloca.s V_0
IL_0019: ldc.i4.1
IL_001a: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<System.Runtime.CompilerServices.InlineArray2<object>, object>(ref System.Runtime.CompilerServices.InlineArray2<object>, int)"
IL_001f: ldc.i4.2
IL_0020: box "int"
IL_0025: stind.ref
IL_0026: ldloca.s V_0
IL_0028: ldc.i4.2
IL_0029: call "System.ReadOnlySpan<object> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<System.Runtime.CompilerServices.InlineArray2<object>, object>(in System.Runtime.CompilerServices.InlineArray2<object>, int)"
IL_002e: call "MyCollection<object> MyCollectionBuilder.Create<object>(System.ReadOnlySpan<object>)"
IL_0033: box "MyCollection<object>"
IL_0038: ldc.i4.0
IL_0039: call "void CollectionExtensions.Report(object, bool)"
IL_003e: ret
}
""");
}

var comp = CreateCompilation([sourceA, sourceB, s_collectionExtensionsWithSpan], targetFramework: TargetFramework.Net100);
if (arrayLength is not (1 or 17))
{
comp.MakeTypeMissing(WellKnownType.System_Runtime_CompilerServices_InlineArray2 + (arrayLength - 2));
}

verifier = CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput(expectedOutput), verify: ilVerifyFailure, symbolValidator: verifyResult(shouldHaveSynthesizedArrayType: arrayLength != 1, arrayLength));
verifier.VerifyDiagnostics();
if (arrayLength == 2)
{
verifier.VerifyIL("<top-level-statements-entry-point>", """
{
// Code size 63 (0x3f)
.maxstack 2
.locals init (<>y__InlineArray2<object> V_0)
IL_0000: ldloca.s V_0
IL_0002: initobj "<>y__InlineArray2<object>"
IL_0008: ldloca.s V_0
IL_000a: ldc.i4.0
IL_000b: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray2<object>, object>(ref <>y__InlineArray2<object>, int)"
IL_0010: ldc.i4.1
IL_0011: box "int"
IL_0016: stind.ref
IL_0017: ldloca.s V_0
IL_0019: ldc.i4.1
IL_001a: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray2<object>, object>(ref <>y__InlineArray2<object>, int)"
IL_001f: ldc.i4.2
IL_0020: box "int"
IL_0025: stind.ref
IL_0026: ldloca.s V_0
IL_0028: ldc.i4.2
IL_0029: call "System.ReadOnlySpan<object> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<<>y__InlineArray2<object>, object>(in <>y__InlineArray2<object>, int)"
IL_002e: call "MyCollection<object> MyCollectionBuilder.Create<object>(System.ReadOnlySpan<object>)"
IL_0033: box "MyCollection<object>"
IL_0038: ldc.i4.0
IL_0039: call "void CollectionExtensions.Report(object, bool)"
IL_003e: ret
}
""");
}

static Action<ModuleSymbol> verifyResult(bool shouldHaveSynthesizedArrayType, int arrayLength)
{
return (moduleSymbol) =>
{
var expectedSythesizedName = GeneratedNames.MakeSynthesizedInlineArrayName(arrayLength, generation: 0);
if (shouldHaveSynthesizedArrayType)
{
Assert.True(moduleSymbol.TypeNames.Contains(expectedSythesizedName));
}
else
{
Assert.False(moduleSymbol.TypeNames.Contains(expectedSythesizedName));
}
};
}
}

[Fact]
public void BuiltInInlineArrayTypes_DependencyTracking()
{
var librarySource = """
namespace System.Runtime.CompilerServices
{
[InlineArray(2)]
public struct InlineArray2<T>
{
private T _element0;
}
}
""";

var libraryComp = CreateCompilation(librarySource, targetFramework: TargetFramework.Net80);
libraryComp.VerifyDiagnostics();
var libraryRef = libraryComp.ToMetadataReference();

string consumerSource = """
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

[CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))]
public struct MyCollection<T> : IEnumerable<T>
{
private readonly List<T> _list;
public MyCollection(List<T> list) { _list = list; }
public IEnumerator<T> GetEnumerator() => _list.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class MyCollectionBuilder
{
public static MyCollection<T> Create<T>(ReadOnlySpan<T> items)
{
return new MyCollection<T>(new List<T>(items.ToArray()));
}
}

public class Program
{
public static void Main()
{
int i = 1;
MyCollection<int> m = [i, i + 1];
m.Report();
}
}
""";

var consumerComp = CreateCompilation([consumerSource, s_collectionExtensionsWithSpan], options: TestOptions.ReleaseExe, references: [libraryRef], targetFramework: TargetFramework.Net80);
var verifier = CompileAndVerify(consumerComp, expectedOutput: IncludeExpectedOutput("[1, 2],"), verify: Verification.Fails with
{
ILVerifyMessage = "[InlineArrayAsReadOnlySpan]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x11 }"
}).VerifyDiagnostics();
verifier.VerifyIL("Program.Main()", """
{
// Code size 57 (0x39)
.maxstack 3
.locals init (int V_0, //i
System.Runtime.CompilerServices.InlineArray2<int> V_1)
IL_0000: ldc.i4.1
IL_0001: stloc.0
IL_0002: ldloca.s V_1
IL_0004: initobj "System.Runtime.CompilerServices.InlineArray2<int>"
IL_000a: ldloca.s V_1
IL_000c: ldc.i4.0
IL_000d: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<System.Runtime.CompilerServices.InlineArray2<int>, int>(ref System.Runtime.CompilerServices.InlineArray2<int>, int)"
IL_0012: ldloc.0
IL_0013: stind.i4
IL_0014: ldloca.s V_1
IL_0016: ldc.i4.1
IL_0017: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<System.Runtime.CompilerServices.InlineArray2<int>, int>(ref System.Runtime.CompilerServices.InlineArray2<int>, int)"
IL_001c: ldloc.0
IL_001d: ldc.i4.1
IL_001e: add
IL_001f: stind.i4
IL_0020: ldloca.s V_1
IL_0022: ldc.i4.2
IL_0023: call "System.ReadOnlySpan<int> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<System.Runtime.CompilerServices.InlineArray2<int>, int>(in System.Runtime.CompilerServices.InlineArray2<int>, int)"
IL_0028: call "MyCollection<int> MyCollectionBuilder.Create<int>(System.ReadOnlySpan<int>)"
IL_002d: box "MyCollection<int>"
IL_0032: ldc.i4.0
IL_0033: call "void CollectionExtensions.Report(object, bool)"
IL_0038: ret
}
""");

// Verify that the library reference is included in used assembly references
var usedRefs = consumerComp.GetUsedAssemblyReferences();
Assert.Contains(libraryRef, usedRefs);

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,21 @@ public void AllWellKnownTypes()
case WellKnownType.System_Runtime_CompilerServices_Unsafe:
case WellKnownType.System_Runtime_CompilerServices_ParamCollectionAttribute:
case WellKnownType.System_Runtime_CompilerServices_ExtensionMarkerAttribute:
case WellKnownType.System_Runtime_CompilerServices_InlineArray2:
case WellKnownType.System_Runtime_CompilerServices_InlineArray3:
case WellKnownType.System_Runtime_CompilerServices_InlineArray4:
case WellKnownType.System_Runtime_CompilerServices_InlineArray5:
case WellKnownType.System_Runtime_CompilerServices_InlineArray6:
case WellKnownType.System_Runtime_CompilerServices_InlineArray7:
case WellKnownType.System_Runtime_CompilerServices_InlineArray8:
case WellKnownType.System_Runtime_CompilerServices_InlineArray9:
case WellKnownType.System_Runtime_CompilerServices_InlineArray10:
case WellKnownType.System_Runtime_CompilerServices_InlineArray11:
case WellKnownType.System_Runtime_CompilerServices_InlineArray12:
case WellKnownType.System_Runtime_CompilerServices_InlineArray13:
case WellKnownType.System_Runtime_CompilerServices_InlineArray14:
case WellKnownType.System_Runtime_CompilerServices_InlineArray15:
case WellKnownType.System_Runtime_CompilerServices_InlineArray16:
// Not yet in the platform.
continue;
case WellKnownType.Microsoft_CodeAnalysis_Runtime_Instrumentation:
Expand Down
Loading