| Severity | Error |
| Reported by | Metalama.Framework |
| Message | Execution scope mismatch in the expression '<ParentExpression>': the sub-expression '<SubExpression>' is <SubExpressionScope>, but the other sub-expression '<OtherSubExpression>' is <OtherSubExpressionScope>. |
Cause
In a T# template, Metalama classifies every expression as either compile-time, which it evaluates while generating the code, or run-time, which it copies into the generated code. A sub-expression whose value is a compile-time object, such as an IProperty or an IMethod, can't become part of the generated code. A sub-expression whose value is run-time, such as meta.Proceed() or the Value of a declaration, can't be evaluated at compile time.
Metalama reports LAMA0241 when an expression combines two such sub-expressions, so that it can be neither compile-time nor run-time. The message gives the two sub-expressions and their scopes. This typically happens in a condition that joins a compile-time test and a run-time test with && or ||, in an array initializer, or in a cast of a compile-time value to a run-time type.
In the following example, the isLoggerAvailable condition checks at compile time that the target type has a Logger property, and at run time that the value of this property isn't null.
1using Metalama.Framework.Aspects;
2using System.Linq;
3
4namespace Doc.LAMA0241.Error;
5
6public class LogAttribute : OverrideMethodAspect
7{
8 public override dynamic? OverrideMethod()
9 {
10 var loggerProperty = meta.Target.Type.Properties.OfName( "Logger" ).SingleOrDefault();
11
12 // Error: the left operand is compile-time, but the right operand is run-time.
Error LAMA0241: Execution scope mismatch in the expression 'loggerProperty != null && loggerProperty.Value != null': the sub-expression 'loggerProperty != null' is compile-time, but the other sub-expression 'loggerProperty.Value != null' is run-time.
13 bool isLoggerAvailable = loggerProperty != null && loggerProperty.Value != null;
14
15 if ( isLoggerAvailable )
16 {
17 loggerProperty!.Value!.WriteLine( $"Executing {meta.Target.Method}." );
18 }
19
20 return meta.Proceed();
21 }
22}
23
1using System;
2using System.IO;
3
4namespace Doc.LAMA0241.Error;
5
6internal class OrderService
7{
8 public TextWriter? Logger { get; set; } = Console.Out;
9
10 [Log]
11 public void PlaceOrder( int quantity ) { }
12}
13
Resolution
Split the expression so that each part is entirely compile-time or entirely run-time. For a condition, test the compile-time part in a compile-time if statement and, inside it, test the run-time part in a run-time expression or if statement. The following example uses this fix.
1using Metalama.Framework.Aspects;
2using System.Linq;
3
4namespace Doc.LAMA0241.Fixed;
5
6public class LogAttribute : OverrideMethodAspect
7{
8 public override dynamic? OverrideMethod()
9 {
10 var loggerProperty = meta.Target.Type.Properties.OfName( "Logger" ).SingleOrDefault();
11
12 // Fixed: the compile-time test is in a compile-time if statement, and the run-time test is inside it.
13 if ( loggerProperty != null )
14 {
15 bool isLoggerAvailable = loggerProperty.Value != null;
16
17 if ( isLoggerAvailable )
18 {
19 loggerProperty.Value!.WriteLine( $"Executing {meta.Target.Method}." );
20 }
21 }
22
23 return meta.Proceed();
24 }
25}
26
1using System;
2using System.IO;
3
4namespace Doc.LAMA0241.Fixed;
5
6internal class OrderService
7{
8 public TextWriter? Logger { get; set; } = Console.Out;
9
10 [Log]
11 public void PlaceOrder( int quantity ) { }
12}
13
1using System;
2using System.IO;
3
4namespace Doc.LAMA0241.Fixed;
5
6internal class OrderService
7{
8 public TextWriter? Logger { get; set; } = Console.Out;
9
10 [Log]
11 public void PlaceOrder(int quantity)
12 {
13 bool isLoggerAvailable = Logger != null;
14 if (isLoggerAvailable)
15 {
16 Logger.WriteLine("Executing OrderService.PlaceOrder(int).");
17 }
18 }
19}
20