| Severity | Error |
| Reported by | Metalama.Framework |
| Message | The template '<Template>' cannot be called with the type argument '<TypeArgument>' because this type argument contains a run-time type parameter of the calling template. |
Cause
An auxiliary template can have type parameters, but they must be compile-time type parameters, marked with [CompileTime]. Metalama replaces them with concrete types when it expands the template, so the type arguments of the call must be known at that moment.
A type parameter of the calling template that isn't marked with [CompileTime] is a run-time type parameter. This is the case, for example, of the type parameter of a generic method introduced with [Introduce]: it's copied to the generated method, and its value is only known when the program calls the method. Metalama reports LAMA0277 when a type argument of the call to an auxiliary template is such a type parameter, or contains it, like T[] or List<T>. The first placeholder of the message is the called template, and the second one is the faulty type argument.
In the following example, the introduced generic method LogType<T> calls the auxiliary template WriteTypeName with its own type parameter T.
1using Metalama.Framework.Aspects;
2using System;
3
4namespace Doc.LAMA0277.Error;
5
6public class TypeLoggerAttribute : TypeAspect
7{
8 [Introduce]
9 public void LogType<T>()
10 {
11 // Error: T is a type parameter of the introduced method, so it is only known at run time.
Error LAMA0277: The template 'TypeLoggerAttribute.WriteTypeName<T>()' cannot be called with the type argument 'T' because this type argument contains a run-time type parameter of the calling template.
12 WriteTypeName<T>();
13 }
14
15 [Template]
16 private void WriteTypeName<[CompileTime] T>()
17 {
18 Console.WriteLine( $"Type: {typeof(T).Name}" );
19 }
20}
21
1namespace Doc.LAMA0277.Error;
2
3[TypeLogger]
4internal partial class Calculator
5{
6 public int Add( int a, int b ) => a + b;
7}
8
Resolution
Depending on your intent, do one of the following:
- Pass the type as a run-time value instead of a type argument: replace the type parameter of the auxiliary template with a regular parameter of type Type and pass
typeof(T). The following example uses this fix. - Move the code of the auxiliary template into the calling template, where
Tcan be used directly. - If the type is known at compile time, pass a concrete type or a compile-time type parameter of the calling template.
1using Metalama.Framework.Aspects;
2using System;
3
4namespace Doc.LAMA0277.Fixed;
5
6public class TypeLoggerAttribute : TypeAspect
7{
8 [Introduce]
9 public void LogType<T>()
10 {
11 // Fixed: the type is passed as a run-time value instead of a compile-time type argument.
12 WriteTypeName( typeof(T) );
13 }
14
15 [Template]
16 private void WriteTypeName( Type type )
17 {
18 Console.WriteLine( $"Type: {type.Name}" );
19 }
20}
21
1namespace Doc.LAMA0277.Fixed;
2
3[TypeLogger]
4internal partial class Calculator
5{
6 public int Add( int a, int b ) => a + b;
7}
8
1using System;
2
3namespace Doc.LAMA0277.Fixed;
4
5[TypeLogger]
6internal partial class Calculator
7{
8 public int Add(int a, int b) => a + b;
9
10 public void LogType<T>()
11 {
12 Console.WriteLine($"Type: {typeof(T).Name}");
13 }
14}
15