Skip to content

Commit

Permalink
Document RequiresDynamicCode APIs that are intrinsically recognized (#…
Browse files Browse the repository at this point in the history
…42546)

These are useful to know when building AOT safe apps/libraries. Their existence is contractual.
  • Loading branch information
MichalStrehovsky authored Sep 11, 2024
1 parent 17d020b commit 286128a
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
title: Intrinsic APIs marked RequiresDynamicCode
description: Learn how the tooling recognizes certain patterns in calls to APIs annotated with RequiresDynamicCode.
author: MichalStrehovsky
ms.author: michals
ms.date: 09/09/2024
---

# Intrinsic APIs marked RequiresDynamicCode

Under normal circumstances, calling APIs annotated with <xref:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute> in an app published with native AOT triggers warning [IL3050 (Avoid calling members annotated with 'RequiresDynamicCodeAttribute' when publishing as native AOT)](warnings/il3050.md). APIs that trigger the warning might not behave correctly after AOT compilation.

Some APIs annotated RequiresDynamicCode can still be used without triggering the warning when called in a specific pattern. When used as part of a pattern, the call to the API can be statically analyzed by the compiler, does not generate a warning, and behaves as expected at run time.

## Enum.GetValues(Type) Method

Calls to this API don't trigger a warning if the concrete enum type is statically visible in the calling method body. For example, `Enum.GetValues(typeof(AttributeTargets))` does not trigger a warning, but `Enum.GetValues(typeof(T))` and `Enum.GetValues(someType)` do.

## Marshal.DestroyStructure(IntPtr, Type) Method

Calls to this API don't trigger a warning if the concrete type is statically visible in the calling method body. For example, `Marshal.DestroyStructure(offs, typeof(bool))` does not trigger a warning, but `Marshal.DestroyStructure(offs, typeof(T))` and `Marshal.DestroyStructure(offs, someType)` do.

## Marshal.GetDelegateForFunctionPointer(IntPtr, Type) Method

Calls to this API don't trigger a warning if the concrete type is statically visible in the calling method body. For example, `Marshal.GetDelegateForFunctionPointer(ptr, typeof(bool))` does not trigger a warning, but `Marshal.GetDelegateForFunctionPointer(ptr, typeof(T))` and `Marshal.GetDelegateForFunctionPointer(ptr, someType)` do.

## Marshal.OffsetOf(Type, String) Method

Calls to this API don't trigger a warning if the concrete type is statically visible in the calling method body. For example, `Marshal.OffsetOf(typeof(Point), someField)` does not trigger a warning, but `Marshal.OffsetOf(typeof(T), someField)` and `Marshal.OffsetOf(someType, someField)` do.

## Marshal.PtrToStructure(IntPtr, Type) Method

Calls to this API don't trigger a warning if the concrete type is statically visible in the calling method body. For example, `Marshal.PtrToStructure(offs, typeof(bool))` does not trigger a warning, but `Marshal.PtrToStructure(offs, typeof(T))` and `Marshal.PtrToStructure(offs, someType)` do.

## Marshal.SizeOf(Type) Method

Calls to this API don't trigger a warning if the concrete type is statically visible in the calling method body. For example, `Marshal.SizeOf(typeof(bool))` does not trigger a warning, but `Marshal.SizeOf(typeof(T))` and `Marshal.SizeOf(someType)` do.

## MethodInfo.MakeGenericMethod(Type[]) Method (.NET 9+)

Calls to this API don't trigger a warning if both the generic method definition and the instantiation arguments are statically visible within the calling method body. For example, `typeof(SomeType).GetMethod("GenericMethod").MakeGenericMethod(typeof(int))`. It's also possible to use a generic parameter as the argument: `typeof(SomeType).GetMethod("GenericMethod").MakeGenericMethod(typeof(T))` also doesn't warn.

If the generic type definition is statically visible within the calling method body and all the generic parameters of it are constrained to be a class, the call also doesn't trigger the IL3050 warning. In this case, the arguments don't have to be statically visible. For example:

```csharp
// No IL3050 warning on MakeGenericMethod because T is constrained to be class
typeof(SomeType).GetMethod("GenericMethod").MakeGenericMethod(Type.GetType(Console.ReadLine()));
class SomeType
{
public void GenericMethod<T>() where T : class { }
}
```

All the other cases, such as `someMethod.MakeGenericMethod(typeof(int))` or `typeof(SomeType).GetMethod("GenericMethod").MakeGenericMethod(someType)` where `someType` has an unknown value, trigger a warning.

## Type.MakeGenericType(Type[]) Method (.NET 9+)

Calls to this API don't trigger a warning if both the generic type definition and the instantiation arguments are statically visible within the calling method body. For example, `typeof(List<>).MakeGenericType(typeof(int))`. It's also possible to use a generic parameter as the argument: `typeof(List<>).MakeGenericType(typeof(T))` also doesn't warn.

If the generic type definition is statically visible within the calling method body and all the generic parameters of it are constrained to be a class, the call also doesn't trigger the IL3050 warning. In this case, the arguments don't have to be statically visible. For example:

```csharp
// No IL3050 warning on MakeGenericType because T is constrained to be class
typeof(Generic<>).MakeGenericType(Type.GetType(Console.ReadLine()));
class Generic<T> where T : class { }
```

All the other cases, such as `someType.MakeGenericType(typeof(int))` or `typeof(List<>).MakeGenericType(someType)` where `someType` has an unknown value, trigger a warning.
4 changes: 3 additions & 1 deletion docs/core/deploying/native-aot/warnings/il3050.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ When you publish an app as Native AOT (by setting the `PublishAot` property to `
// AOT analysis warning IL3050: Program.<Main>$(String[]): Using member 'System.Type.MakeGenericType(Type[])'
// which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for
// this instantiation might not be available at runtime.
typeof(Generic<>).MakeGenericType(typeof(SomeStruct));
typeof(Generic<>).MakeGenericType(unknownType);

class Generic<T> { }

Expand All @@ -33,3 +33,5 @@ struct SomeStruct { }
## How to fix violations

Members annotated with the `RequiresDynamicCodeAttribute` attribute have a message that provides useful information to users who are publishing as Native AOT. Consider adapting existing code to the attribute's message or removing the violating call.

Some APIs annotated with `RequiresDynamicCodeAttribute` don't trigger a warning when called in a specific pattern. For more information, see [Intrinsic APIs marked RequiresDynamicCode](../intrinsic-requiresdynamiccode-apis.md).
2 changes: 2 additions & 0 deletions docs/navigate/devops-testing/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,8 @@ items:
href: ../../core/deploying/native-aot/cross-compile.md
- name: Intro to AOT warnings
href: ../../core/deploying/native-aot/fixing-warnings.md
- name: Intrinsic APIs marked RequiresDynamicCode
href: ../../core/deploying/native-aot/intrinsic-requiresdynamiccode-apis.md
- name: AOT warnings
items:
- name: IL3050
Expand Down

0 comments on commit 286128a

Please sign in to comment.