| Severity | Error |
| Reported by | Metalama.Framework |
| Message | The compile-time '<LoopKind>' loop is not allowed here because it is part of a block whose execution depends on the run-time condition '<RunTimeCondition>'. Move the loop out of the run-time-conditional block or use a compile-time 'foreach' loop instead. |
Cause
In a T# template, a while or do loop whose condition is a compile-time expression runs at compile time. Such a loop needs a compile-time variable that changes from one iteration to the next. However, the body of a run-time if, else, for, foreach, while, switch, catch, or finally executes only when the program runs, and Metalama doesn't allow modifying compile-time variables from such a block (see LAMA0108).
For this reason, Metalama reports LAMA0110 when a compile-time while or do loop appears inside a run-time-conditional block. The first placeholder of the message is the kind of loop, and the second one is the statement that introduces the run-time condition. The check doesn't apply to foreach loops because their iteration variable can't be modified explicitly.
In the following example, the aspect uses a compile-time while loop to iterate over the parameters of the target method, inside an if statement whose condition, result == null, is a run-time expression.
1using Metalama.Framework.Aspects;
2using System;
3
4namespace Doc.LAMA0110.Error;
5
6public class LogNullResultAttribute : OverrideMethodAspect
7{
8 public override dynamic? OverrideMethod()
9 {
10 var result = meta.Proceed();
11
12 if ( result == null )
13 {
14 var i = meta.CompileTime( 0 );
15
16 // Error: a compile-time while loop is used under a run-time condition.
Error LAMA0110: The compile-time 'while' loop is not allowed here because it is part of a block whose execution depends on the run-time condition 'if ( result == null )'. Move the loop out of the run-time-conditional block or use a compile-time 'foreach' loop instead.
17 while ( i < meta.Target.Parameters.Count )
18 {
19 Console.WriteLine( $"{meta.Target.Method.Name} returned null for {meta.Target.Parameters[i].Name} = {meta.Target.Parameters[i].Value}." );
20 i++;
21 }
22 }
23
24 return result;
25 }
26}
27
1namespace Doc.LAMA0110.Error;
2
3internal class CustomerRepository
4{
5 [LogNullResult]
6 public string? FindCustomerId( string name, string city ) => null;
7}
8
Resolution
Depending on your intent, do one of the following:
- Replace the
whileordoloop with a compile-timeforeachloop. To iterate over a range of numbers, iterate overmeta.CompileTime( Enumerable.Range( 0, n ) ). The following example uses aforeachloop. - Move the loop out of the run-time-conditional block, for instance to compute a compile-time value before the block.
- Move the logic to a compile-time helper method that returns a collection, and iterate over that collection with
foreach.
1using Metalama.Framework.Aspects;
2using System;
3
4namespace Doc.LAMA0110.Fixed;
5
6public class LogNullResultAttribute : OverrideMethodAspect
7{
8 public override dynamic? OverrideMethod()
9 {
10 var result = meta.Proceed();
11
12 if ( result == null )
13 {
14 // Fixed: a compile-time foreach loop is allowed under a run-time condition.
15 foreach ( var parameter in meta.Target.Parameters )
16 {
17 Console.WriteLine( $"{meta.Target.Method.Name} returned null for {parameter.Name} = {parameter.Value}." );
18 }
19 }
20
21 return result;
22 }
23}
24
1namespace Doc.LAMA0110.Fixed;
2
3internal class CustomerRepository
4{
5 [LogNullResult]
6 public string? FindCustomerId( string name, string city ) => null;
7}
8
1using System;
2
3namespace Doc.LAMA0110.Fixed;
4
5internal class CustomerRepository
6{
7 [LogNullResult]
8 public string? FindCustomerId(string name, string city)
9 {
10 string? result;
11 result = null;
12 if (result == null)
13 {
14 Console.WriteLine($"FindCustomerId returned null for name = {name}.");
15 Console.WriteLine($"FindCustomerId returned null for city = {city}.");
16 }
17
18 return result;
19 }
20}
21