Open sandboxFocusImprove this doc

LAMA0110: A compile-time loop can't be used under a run-time condition

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 while or do loop with a compile-time foreach loop. To iterate over a range of numbers, iterate over meta.CompileTime( Enumerable.Range( 0, n ) ). The following example uses a foreach loop.
  • 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
Source Code
1namespace Doc.LAMA0110.Fixed;
2


3internal class CustomerRepository
4{
5    [LogNullResult]
6    public string? FindCustomerId( string name, string city ) => null;
7}
8
Transformed Code
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

See also