// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
+using System.Collections.Generic;
using Internal.TypeSystem;
using Debug = System.Diagnostics.Debug;
// in unpredictable spots.
public partial class CompilerTypeSystemContext
{
+ [ThreadStatic]
+ private static List<TypeLoadabilityCheckInProgress> t_typeLoadCheckInProgressStack;
+ private static List<TypeDesc> EmptyList = new List<TypeDesc>();
+ private readonly ValidTypeHashTable _validTypes = new ValidTypeHashTable();
+
/// <summary>
/// Ensures that the type can be fully loaded. The method will throw one of the type system
/// exceptions if the type is not loadable.
/// </summary>
public void EnsureLoadableType(TypeDesc type)
{
- _validTypes.GetOrCreateValue(type);
+ if (type == null)
+ return;
+
+ if (_validTypes.Contains(type))
+ return;
+
+ // Use a scheme where we push a stack of types in the process of loading
+ // When the stack pops, without throwing an exception, the type will be marked as being detected as successfully loadable.
+ // We need this complex scheme, as types can have circular dependencies. In addition, due to circular references, we can
+ // be forced to move when a type is successfully marked as loaded up the stack to an earlier call to EnsureLoadableType
+ //
+ // For example, consider the following case:
+ // interface IInterface<T> {}
+ // interface IPassThruInterface<T> : IInterface<T> {}
+ // interface ISimpleInterface {}
+ // class A<T> : IInterface<A<T>>, IPassThruInterface<A<T>>, ISimpleInterface {}
+ // class B : A<B> {}
+ //
+ // We call EnsureLoadableType on B
+ //
+ // This will generate the following interesting stacks of calls to EnsureLoadableType
+ //
+ // B -> A<B> -> B
+ // This stack indicates that A<B> can only be considered loadable if B is considered loadable, so we must defer marking
+ // A<B> as loadable until we finish processing B.
+ //
+ // B -> A<B> -> ISimpleInterface
+ // Since examining ISimpleInterface does not have any dependency on B or A<B>, it can be marked as loadable as soon
+ // as we finish processing it.
+ //
+ // B -> A<B> -> IInterface<A<B>> -> A<B>
+ // This stack indicates that IInterface<A<B>> can be considered loadable if A<B> is considered loadable. We must defer
+ // marking IInterface<A<B>> as loadable until we are able to mark A<B> as loadable. Based on the stack above, that can
+ // only happen once B is considered loadable.
+ //
+ // B -> A<B> -> IPassthruInterface<A<B>> -> IInterface<A<B>>
+ // This stack indicates that IPassthruInterface<A<B>> can be considered loadable if IInterface<A<B>> is considered
+ // loadable. If this happens after the IInterface<A<B>> is marked as being loadable once B is considered loadable
+ // then we will push the loadibility marking to the B level at this point. OR we will continue to recurse and the logic
+ // will note that IInterface<A<B>> needs A<B> needs B which will move the marking up at that point.
+
+ if (PushTypeLoadInProgress(type))
+ return;
+
+ bool threwException = true;
+ try
+ {
+ EnsureLoadableTypeUncached(type);
+ threwException = false;
+ }
+ finally
+ {
+ PopTypeLoadabilityCheckInProgress(threwException);
+ }
+ }
+
+ private sealed class TypeLoadabilityCheckInProgress
+ {
+ public TypeDesc TypeInLoadabilityCheck;
+ public bool MarkTypeAsSuccessfullyLoadedIfNoExceptionThrown;
+ public List<TypeDesc> OtherTypesToMarkAsSuccessfullyLoaded;
+
+ public void AddToOtherTypesToMarkAsSuccessfullyLoaded(TypeDesc type)
+ {
+ if (OtherTypesToMarkAsSuccessfullyLoaded == EmptyList)
+ {
+ OtherTypesToMarkAsSuccessfullyLoaded = new List<TypeDesc>();
+ }
+
+ Debug.Assert(!OtherTypesToMarkAsSuccessfullyLoaded.Contains(type));
+ OtherTypesToMarkAsSuccessfullyLoaded.Add(type);
+ }
+ }
+
+ // Returns true to indicate the type should be considered to be loadable (although it might not be, actually safety may require more code to execute)
+ private static bool PushTypeLoadInProgress(TypeDesc type)
+ {
+ t_typeLoadCheckInProgressStack ??= new List<TypeLoadabilityCheckInProgress>();
+
+ // Walk stack to see if the specified type is already in the process of being type checked.
+ int typeLoadCheckInProgressStackOffset = -1;
+ bool checkingMode = false; // Checking for match on TypeLoadabilityCheck field or in OtherTypesToMarkAsSuccessfullyLoaded. (true for OtherTypesToMarkAsSuccessfullyLoaded)
+ for (int typeCheckDepth = t_typeLoadCheckInProgressStack.Count - 1; typeCheckDepth >= 0; typeCheckDepth--)
+ {
+ if (t_typeLoadCheckInProgressStack[typeCheckDepth].TypeInLoadabilityCheck == type)
+ {
+ // The stack contains the interesting type.
+ if (t_typeLoadCheckInProgressStack[typeCheckDepth].MarkTypeAsSuccessfullyLoadedIfNoExceptionThrown)
+ {
+ // And this is the level where the type is known to be successfully loaded.
+ typeLoadCheckInProgressStackOffset = typeCheckDepth;
+ break;
+ }
+ }
+ else if (t_typeLoadCheckInProgressStack[typeCheckDepth].OtherTypesToMarkAsSuccessfullyLoaded.Contains(type))
+ {
+ // We've found where the type will be marked as successfully loaded.
+ typeLoadCheckInProgressStackOffset = typeCheckDepth;
+ break;
+ }
+ }
+
+ if (checkingMode)
+ {
+ // If we enabled checkingMode we should always have found the type
+ Debug.Assert(typeLoadCheckInProgressStackOffset != -1);
+ }
+
+ if (typeLoadCheckInProgressStackOffset == -1)
+ {
+ // The type is not already in the process of being checked for loadability, so return false to indicate that normal load checking should begin
+ TypeLoadabilityCheckInProgress typeCheckInProgress = new TypeLoadabilityCheckInProgress();
+ typeCheckInProgress.TypeInLoadabilityCheck = type;
+ typeCheckInProgress.OtherTypesToMarkAsSuccessfullyLoaded = EmptyList;
+ typeCheckInProgress.MarkTypeAsSuccessfullyLoadedIfNoExceptionThrown = true;
+ t_typeLoadCheckInProgressStack.Add(typeCheckInProgress);
+ return false;
+ }
+
+ // Move timing of when types are considered loaded back to the point at which we mark this type as loaded
+ var typeLoadCheckToAddTo = t_typeLoadCheckInProgressStack[typeLoadCheckInProgressStackOffset];
+ for (int typeCheckDepth = t_typeLoadCheckInProgressStack.Count - 1; typeCheckDepth > typeLoadCheckInProgressStackOffset; typeCheckDepth--)
+ {
+ if (t_typeLoadCheckInProgressStack[typeCheckDepth].MarkTypeAsSuccessfullyLoadedIfNoExceptionThrown)
+ {
+ typeLoadCheckToAddTo.AddToOtherTypesToMarkAsSuccessfullyLoaded(t_typeLoadCheckInProgressStack[typeCheckDepth].TypeInLoadabilityCheck);
+ t_typeLoadCheckInProgressStack[typeCheckDepth].MarkTypeAsSuccessfullyLoadedIfNoExceptionThrown = false;
+ }
+
+ foreach (var typeToMove in t_typeLoadCheckInProgressStack[typeCheckDepth].OtherTypesToMarkAsSuccessfullyLoaded)
+ {
+ typeLoadCheckToAddTo.AddToOtherTypesToMarkAsSuccessfullyLoaded(typeToMove);
+ }
+
+ t_typeLoadCheckInProgressStack[typeCheckDepth].OtherTypesToMarkAsSuccessfullyLoaded = EmptyList;
+ }
+
+ // We are going to report that the type should be considered to be loadable at this stage
+ return true;
+ }
+
+ private void PopTypeLoadabilityCheckInProgress(bool exceptionThrown)
+ {
+ Debug.Assert(EmptyList.Count == 0);
+ var typeLoadabilityCheck = t_typeLoadCheckInProgressStack[t_typeLoadCheckInProgressStack.Count - 1];
+ t_typeLoadCheckInProgressStack.RemoveAt(t_typeLoadCheckInProgressStack.Count - 1);
+
+ if (!exceptionThrown)
+ {
+ if (!typeLoadabilityCheck.MarkTypeAsSuccessfullyLoadedIfNoExceptionThrown)
+ {
+ Debug.Assert(typeLoadabilityCheck.OtherTypesToMarkAsSuccessfullyLoaded.Count == 0);
+ }
+
+ if (typeLoadabilityCheck.MarkTypeAsSuccessfullyLoadedIfNoExceptionThrown)
+ {
+ _validTypes.GetOrCreateValue(typeLoadabilityCheck.TypeInLoadabilityCheck);
+ foreach (var type in typeLoadabilityCheck.OtherTypesToMarkAsSuccessfullyLoaded)
+ {
+ _validTypes.GetOrCreateValue(type);
+ }
+ }
+ }
}
public void EnsureLoadableMethod(MethodDesc method)
{
protected override bool CompareKeyToValue(TypeDesc key, TypeDesc value) => key == value;
protected override bool CompareValueToValue(TypeDesc value1, TypeDesc value2) => value1 == value2;
- protected override TypeDesc CreateValueFromKey(TypeDesc key) => EnsureLoadableTypeUncached(key);
+ protected override TypeDesc CreateValueFromKey(TypeDesc key) => key;
protected override int GetKeyHashCode(TypeDesc key) => key.GetHashCode();
protected override int GetValueHashCode(TypeDesc value) => value.GetHashCode();
}
- private readonly ValidTypeHashTable _validTypes = new ValidTypeHashTable();
private static TypeDesc EnsureLoadableTypeUncached(TypeDesc type)
{
}
}
- if (type.IsParameterizedType)
+ if (type.IsGenericParameter)
+ {
+ // Generic parameters don't need validation
+ }
+ else if (type.IsParameterizedType)
{
// Validate parameterized types
var parameterizedType = (ParameterizedType)type;
{
ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
}
+
+ ((CompilerTypeSystemContext)type.Context).EnsureLoadableType(typeArg);
}
- // Don't validate constraints with crossgen2 - the type system is not set up correctly
- // and doesn't see generic interfaces on arrays.
-#if !READYTORUN
if (!defType.IsCanonicalSubtype(CanonicalFormKind.Any) && !defType.CheckConstraints())
{
ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
}
-#endif
// Check the type doesn't have bogus MethodImpls or overrides and we can get the finalizer.
defType.GetFinalizer();
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// This test checks that R2R binaries with non-findable dependencies can still build correctly.
+
+// The only difference between this file and GitHub_89337.cs, is that references to the System.Xml.ReaderWriter
+// assembly have been renamed to references to NonexistentAssembly
+
+// .NET IL Disassembler. Version 8.0.0-dev
+
+
+
+// Metadata version: v4.0.30319
+.assembly extern System.Runtime
+{
+ .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:
+ .ver 6:0:0:0
+}
+.assembly extern NonexistentAssembly
+{
+ .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:
+ .ver 6:0:0:0
+}
+.assembly GitHub_89337
+{
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
+ 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
+
+ // --- The following custom attribute is added automatically, do not uncomment -------
+ // .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 )
+
+ .custom instance void [System.Runtime]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 18 2E 4E 45 54 43 6F 72 65 41 70 70 2C 56 // ....NETCoreApp,V
+ 65 72 73 69 6F 6E 3D 76 36 2E 30 01 00 54 0E 14 // ersion=v6.0..T..
+ 46 72 61 6D 65 77 6F 72 6B 44 69 73 70 6C 61 79 // FrameworkDisplay
+ 4E 61 6D 65 08 2E 4E 45 54 20 36 2E 30 ) // Name..NET 6.0
+ .custom instance void [System.Runtime]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 0C 47 69 74 48 75 62 5F 38 39 33 33 37 00 // ...GitHub_89337.
+ 00 )
+ .custom instance void [System.Runtime]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 05 44 65 62 75 67 00 00 ) // ...Debug..
+ .custom instance void [System.Runtime]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 ) // ...1.0.0.0..
+ .custom instance void [System.Runtime]System.Reflection.AssemblyInformationalVersionAttribute::.ctor(string) = ( 01 00 05 31 2E 30 2E 30 00 00 ) // ...1.0.0..
+ .custom instance void [System.Runtime]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 0C 47 69 74 48 75 62 5F 38 39 33 33 37 00 // ...GitHub_89337.
+ 00 )
+ .custom instance void [System.Runtime]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 0C 47 69 74 48 75 62 5F 38 39 33 33 37 00 // ...GitHub_89337.
+ 00 )
+ .hash algorithm 0x00008004
+ .ver 1:0:0:0
+}
+.module GitHub_89337.dll
+// MVID: {9cd3bf31-7da9-45e9-a188-4b1492ff96cd}
+.imagebase 0x00400000
+.file alignment 0x00000200
+.stackreserve 0x00100000
+.subsystem 0x0003 // WINDOWS_CUI
+.corflags 0x00000001 // ILONLY
+// Image base: 0x00000271F1C10000
+
+
+// =============== CLASS MEMBERS DECLARATION ===================
+
+.class private auto ansi sealed beforefieldinit Microsoft.CodeAnalysis.EmbeddedAttribute
+ extends [System.Runtime]System.Attribute
+{
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
+ .custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = ( 01 00 00 00 )
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor() cil managed
+ {
+ // Code size 8 (0x8)
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: call instance void [System.Runtime]System.Attribute::.ctor()
+ IL_0006: nop
+ IL_0007: ret
+ } // end of method EmbeddedAttribute::.ctor
+
+} // end of class Microsoft.CodeAnalysis.EmbeddedAttribute
+
+.class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.NullableAttribute
+ extends [System.Runtime]System.Attribute
+{
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
+ .custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = ( 01 00 00 00 )
+ .custom instance void [System.Runtime]System.AttributeUsageAttribute::.ctor(valuetype [System.Runtime]System.AttributeTargets) = ( 01 00 84 6B 00 00 02 00 54 02 0D 41 6C 6C 6F 77 // ...k....T..Allow
+ 4D 75 6C 74 69 70 6C 65 00 54 02 09 49 6E 68 65 // Multiple.T..Inhe
+ 72 69 74 65 64 00 ) // rited.
+ .field public initonly uint8[] NullableFlags
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor(uint8 A_1) cil managed
+ {
+ // Code size 24 (0x18)
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: call instance void [System.Runtime]System.Attribute::.ctor()
+ IL_0006: nop
+ IL_0007: ldarg.0
+ IL_0008: ldc.i4.1
+ IL_0009: newarr [System.Runtime]System.Byte
+ IL_000e: dup
+ IL_000f: ldc.i4.0
+ IL_0010: ldarg.1
+ IL_0011: stelem.i1
+ IL_0012: stfld uint8[] System.Runtime.CompilerServices.NullableAttribute::NullableFlags
+ IL_0017: ret
+ } // end of method NullableAttribute::.ctor
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor(uint8[] A_1) cil managed
+ {
+ // Code size 15 (0xf)
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: call instance void [System.Runtime]System.Attribute::.ctor()
+ IL_0006: nop
+ IL_0007: ldarg.0
+ IL_0008: ldarg.1
+ IL_0009: stfld uint8[] System.Runtime.CompilerServices.NullableAttribute::NullableFlags
+ IL_000e: ret
+ } // end of method NullableAttribute::.ctor
+
+} // end of class System.Runtime.CompilerServices.NullableAttribute
+
+.class private auto ansi beforefieldinit GitHub_89337
+ extends [System.Runtime]System.Object
+{
+ .class auto ansi nested private beforefieldinit Generic`1<T>
+ extends [System.Runtime]System.Object
+ {
+ .param type T
+ .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 )
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor() cil managed
+ {
+ // Code size 8 (0x8)
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: call instance void [System.Runtime]System.Object::.ctor()
+ IL_0006: nop
+ IL_0007: ret
+ } // end of method Generic`1::.ctor
+
+ } // end of class Generic`1
+
+ .class auto ansi nested private beforefieldinit Derived
+ extends [NonexistentAssembly]System.Xml.NameTable
+ {
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor() cil managed
+ {
+ // Code size 8 (0x8)
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: call instance void [NonexistentAssembly]System.Xml.NameTable::.ctor()
+ IL_0006: nop
+ IL_0007: ret
+ } // end of method Derived::.ctor
+
+ } // end of class Derived
+
+ .method private hidebysig static class GitHub_89337/Generic`1<class GitHub_89337/Derived>
+ Passthru(class GitHub_89337/Generic`1<class GitHub_89337/Derived> param) cil managed
+ {
+ .param [0]
+ .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8[]) = ( 01 00 02 00 00 00 02 01 00 00 )
+ .param [1]
+ .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8[]) = ( 01 00 02 00 00 00 02 01 00 00 )
+ // Code size 7 (0x7)
+ .maxstack 1
+ .locals init (class GitHub_89337/Generic`1<class GitHub_89337/Derived> V_0)
+ IL_0000: nop
+ IL_0001: ldarg.0
+ IL_0002: stloc.0
+ IL_0003: br.s IL_0005
+
+ IL_0005: ldloc.0
+ IL_0006: ret
+ } // end of method GitHub_89337::Passthru
+
+ .method private hidebysig static int32
+ Main() cil managed
+ {
+ .entrypoint
+ // Code size 8 (0x8)
+ .maxstack 1
+ .locals init (int32 V_0)
+ IL_0000: nop
+ IL_0001: ldc.i4.s 100
+ IL_0003: stloc.0
+ IL_0004: br.s IL_0006
+
+ IL_0006: ldloc.0
+ IL_0007: ret
+ } // end of method GitHub_89337::Main
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor() cil managed
+ {
+ // Code size 8 (0x8)
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: call instance void [System.Runtime]System.Object::.ctor()
+ IL_0006: nop
+ IL_0007: ret
+ } // end of method GitHub_89337::.ctor
+
+} // end of class GitHub_89337
\ No newline at end of file