Skip to content

Commit e49c6c3

Browse files
committed
[NativeAOT] Fix Activator.CreateInstance for shared generic structs
The default constructor has to be invoked using fat pointer in shared generic structs.
1 parent 8c5bc36 commit e49c6c3

File tree

8 files changed

+69
-18
lines changed

8 files changed

+69
-18
lines changed

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Activator.NativeAot.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public static partial class Activator
6363
else
6464
{
6565
t = default!;
66-
RawCalliHelper.Call(defaultConstructor, ref Unsafe.As<T, byte>(ref t));
66+
((delegate* <ref byte, void>)defaultConstructor)(ref Unsafe.As<T, byte>(ref t));
6767

6868
// Debugger goo so that stepping in works. Only affects debug info generation.
6969
// The call gets optimized away.

src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,9 +337,19 @@ internal override IntPtr Create(TypeBuilder builder)
337337
{
338338
IntPtr result = TypeLoaderEnvironment.TryGetDefaultConstructorForType(Type);
339339

340+
if (result != IntPtr.Zero)
341+
{
342+
if (Type.IsValueType)
343+
Environment.FailFast("Here!");
340344

341-
if (result == IntPtr.Zero)
345+
// TODO
346+
// result = TypeLoaderEnvironment.Instance.ConvertUnboxingFunctionPointerToUnderlyingNonUnboxingPointer(result, new RuntimeTypeHandle(pElementEEType));
347+
}
348+
else
349+
{
342350
result = RuntimeAugments.GetFallbackDefaultConstructor();
351+
}
352+
343353
return result;
344354
}
345355
}

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ public ISymbolNode ComputeConstantLookup(ReadyToRunHelperId lookupKind, object t
326326
{
327327
var type = (TypeDesc)targetOfLookup;
328328
MethodDesc ctor = GetConstructorForCreateInstanceIntrinsic(type);
329-
return NodeFactory.CanonicalEntrypoint(ctor);
329+
return type.IsValueType ? NodeFactory.ExactCallableAddress(ctor) : NodeFactory.CanonicalEntrypoint(ctor);
330330
}
331331
case ReadyToRunHelperId.ObjectAllocator:
332332
{

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericLookupResult.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -781,7 +781,7 @@ public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultCo
781781
{
782782
TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation);
783783
MethodDesc defaultCtor = Compilation.GetConstructorForCreateInstanceIntrinsic(instantiatedType);
784-
return factory.CanonicalEntrypoint(defaultCtor);
784+
return instantiatedType.IsValueType ? factory.ExactCallableAddress(defaultCtor) : factory.CanonicalEntrypoint(defaultCtor);
785785
}
786786

787787
public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)

src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,8 +360,9 @@ private void ImportCall(ILOpcode opcode, int token)
360360
}
361361
else
362362
{
363-
MethodDesc ctor = Compilation.GetConstructorForCreateInstanceIntrinsic(method.Instantiation[0]);
364-
_dependencies.Add(_factory.CanonicalEntrypoint(ctor), reason);
363+
TypeDesc type = method.Instantiation[0];
364+
MethodDesc ctor = Compilation.GetConstructorForCreateInstanceIntrinsic(type);
365+
_dependencies.Add(type.IsValueType ? _factory.ExactCallableAddress(ctor) : _factory.CanonicalEntrypoint(ctor), reason);
365366
}
366367

367368
return;

src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ActivatorTests.Generic.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,22 @@ public void CreateInstanceT_StructWithoutDefaultConstructor_InvokesConstructor()
6161
public void CreateInstanceT_StructWithDefaultConstructorThatThrows_ThrowsTargetInvocationException() =>
6262
Assert.Throws<TargetInvocationException>(() => Activator.CreateInstance<StructWithDefaultConstructorThatThrows>());
6363

64+
[Fact]
65+
public void CreateInstanceT_GenericTypes()
66+
{
67+
TestGenericClassWithDefaultConstructor<string>();
68+
TestGenericClassWithDefaultConstructor<int>();
69+
70+
TestGenericStructWithDefaultConstructor<string>();
71+
TestGenericStructWithDefaultConstructor<int>();
72+
73+
void TestGenericClassWithDefaultConstructor<T>()
74+
=> Assert.Equal(typeof(T), Activator.CreateInstance<GenericClassWithDefaultConstructor<T>>().TypeOfT);
75+
76+
void TestGenericStructWithDefaultConstructor<T>()
77+
=> Assert.Equal(typeof(T), Activator.CreateInstance<GenericStructWithDefaultConstructor<T>>().TypeOfT);
78+
}
79+
6480
private interface IInterface
6581
{
6682
}
@@ -96,5 +112,27 @@ private class ClassWithDefaultConstructorThatThrows
96112
public ClassWithDefaultConstructorThatThrows() =>
97113
throw new Exception();
98114
}
115+
116+
public class GenericClassWithDefaultConstructor<T>
117+
{
118+
public GenericClassWithDefaultConstructor() =>
119+
TypeOfT = typeof(T);
120+
121+
public Type TypeOfT { get; }
122+
}
123+
124+
public struct StructWithDefaultConstructorThatThrows
125+
{
126+
public StructWithDefaultConstructorThatThrows() =>
127+
throw new Exception();
128+
}
129+
130+
public struct GenericStructWithDefaultConstructor<T>
131+
{
132+
public GenericStructWithDefaultConstructor() =>
133+
TypeOfT = typeof(T);
134+
135+
public Type TypeOfT { get; }
136+
}
99137
}
100138
}

src/libraries/System.Runtime/tests/System.Runtime.Tests/TestStructs/System.TestStructs.il

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,6 @@
5757
ret
5858
}
5959
}
60-
61-
.class public sequential sealed StructWithDefaultConstructorThatThrows
62-
extends [System.Runtime]System.ValueType
63-
{
64-
.size 1
65-
66-
.method public hidebysig specialname rtspecialname instance void .ctor () cil managed
67-
{
68-
newobj instance void [System.Runtime]System.Exception::.ctor()
69-
throw
70-
}
71-
}
7260
}
7361

7462
.class public sequential sealed '.GlobalStructStartingWithDot'

src/tests/nativeaot/SmokeTests/DynamicGenerics/activation.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,18 @@ public override string ToString()
131131
return memberVar;
132132
}
133133
}
134+
struct StructToString<T>
135+
{
136+
string memberVar;
137+
public StructToString()
138+
{
139+
memberVar = typeof(T).Name;
140+
}
141+
public override string ToString()
142+
{
143+
return memberVar;
144+
}
145+
}
134146
class SomeUnrealtedType<T>
135147
{
136148
string memberVar;
@@ -195,5 +207,7 @@ public static void TestDefaultCtorInLazyGenerics()
195207
AllocViaGVMBase typeWithGVM = AllocViaGVMDerived.Alloc();
196208
Assert.AreEqual("ToStringIsInteresting1", typeWithGVM.Alloc<ToStringIsInteresting1>().ToString());
197209
Assert.AreEqual("ToStringIsInteresting2", typeWithGVM.Alloc<ToStringIsInteresting2>().ToString());
210+
Assert.AreEqual("ToStringIsInteresting1", typeWithGVM.Alloc<StructToString<ToStringIsInteresting1>>().ToString());
211+
Assert.AreEqual("ToStringIsInteresting2", typeWithGVM.Alloc<StructToString<ToStringIsInteresting2>>().ToString());
198212
}
199213
}

0 commit comments

Comments
 (0)