Open sandboxFocusImprove this doc

LAMA0277: A called template can't receive a run-time type parameter as type argument

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 T can 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
Source Code
1namespace Doc.LAMA0277.Fixed;
2


3[TypeLogger]
4internal partial class Calculator
5{
6    public int Add( int a, int b ) => a + b;
7}
8
Transformed Code
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

See also