| Severity | Error |
| Reported by | Metalama.Framework |
| Message | '<Declaration>' is invalid because it combines run-time and compile-time elements. |
Cause
Metalama assigns an execution scope, compile-time or run-time, to every symbol used in a template or in compile-time code. The scope of a generic method or type instance depends on the scopes of its type arguments. When these scopes are incompatible, the symbol has no valid scope.
Usually, Metalama can name the run-time element and the compile-time element that conflict, and reports LAMA0226. LAMA0245 is the fallback when Metalama can't identify the two elements. The placeholder of the message is the invalid symbol.
A known case is a call to meta.CompileTime with a value whose type is a compile-time type parameter of a template. Such a type parameter stands for a type of the target code, so a value of this type, like default(T), is a run-time value, while meta.CompileTime is a compile-time method. In the following example, the introduced method tries to print the default value of its type parameter at compile time.
1using Metalama.Framework.Advising;
2using Metalama.Framework.Aspects;
3using Metalama.Framework.Code;
4using System;
5
6namespace Doc.LAMA0245.Error;
7
8public class PrintDefaultValueAttribute : TypeAspect
9{
10 public override void BuildAspect( IAspectBuilder<INamedType> builder )
11 {
12 builder.IntroduceMethod( nameof(this.PrintDefaultValue), args: new { T = typeof(int) } );
13 }
14
15 [Template]
16 public void PrintDefaultValue<[CompileTime] T>()
17 {
18 // Error: T stands for a run-time type, so default(T) cannot be evaluated by meta.CompileTime.
Error LAMA0245: 'meta.CompileTime<T>(T?)' is invalid because it combines run-time and compile-time elements.
19 Console.WriteLine( meta.CompileTime( default(T) ) );
20 }
21}
22
1namespace Doc.LAMA0245.Error;
2
3[PrintDefaultValue]
4internal partial class Counter
5{
6 public int Value { get; set; }
7}
8
Resolution
Decide whether the expression must be evaluated at compile time or at run time, and remove the elements of the other scope:
- If the value belongs to the target code, as with
default(T), use it in run-time code, without meta.CompileTime, as in the following example. Metalama replacesTwith the actual type in the generated code. - If you need a compile-time value, compute it from compile-time information, like the IType of the target declaration, instead of a run-time type.
1using Metalama.Framework.Advising;
2using Metalama.Framework.Aspects;
3using Metalama.Framework.Code;
4using System;
5
6namespace Doc.LAMA0245.Fixed;
7
8public class PrintDefaultValueAttribute : TypeAspect
9{
10 public override void BuildAspect( IAspectBuilder<INamedType> builder )
11 {
12 builder.IntroduceMethod( nameof(this.PrintDefaultValue), args: new { T = typeof(int) } );
13 }
14
15 [Template]
16 public void PrintDefaultValue<[CompileTime] T>()
17 {
18 // Fixed: default(T) is emitted as run-time code, where T is replaced by the actual type.
19 Console.WriteLine( default(T) );
20 }
21}
22
1namespace Doc.LAMA0245.Fixed;
2
3[PrintDefaultValue]
4internal partial class Counter
5{
6 public int Value { get; set; }
7}
8
1using System;
2
3namespace Doc.LAMA0245.Fixed;
4
5[PrintDefaultValue]
6internal partial class Counter
7{
8 public int Value { get; set; }
9
10 public void PrintDefaultValue()
11 {
12 Console.WriteLine(default(int));
13 }
14}
15