Open sandboxFocusImprove this doc

LAMA0288: A compile-time call with side effects can't depend on a run-time condition

Severity Error
Reported by Metalama.Framework
Message The compile-time expression '<Expression>' cannot be used as a statement here because the statement is in a block whose execution depends on a run-time condition ('<RunTimeCondition>'). A compile-time expression used as a statement is assumed to have side effects, and these side effects cannot depend on a run-time condition. Move the statement out of this block, or make the condition compile-time.

Cause

Metalama executes the compile-time parts of a template once, when it generates the code, but the run-time parts execute later, when the program runs. When a block depends on a run-time condition, such as an if statement whose condition is a run-time expression, Metalama doesn't know at compile time whether the block will execute. Compile-time code in that block must not change compile-time state, because that change would depend on a condition that's unknown at compile time.

Metalama reports LAMA0288 when a block that depends on a run-time condition contains a compile-time expression, used as a statement, that Metalama assumes to have side effects. Typically, it's a call to a method of a compile-time variable declared outside the block, or of a compile-time field or property, for instance stringBuilder.Append( ", " ) or list.Add( item ). Calls on compile-time variables declared inside the block, auxiliary template calls, and methods like meta.InsertStatement aren't affected. The <RunTimeCondition> placeholder shows the run-time condition, or the construct that makes the block conditional, for instance catch, finally, or a local function.

A common cause is a variable that you intend to be compile-time but that Metalama infers to be run-time. In the following example, the aspect builds a string with a compile-time StringBuilder, but the counter i is initialized with 0, so it's a run-time variable, and if ( i > 0 ) is a run-time condition.

1using Metalama.Framework.Aspects;
2using System.Linq;
3using System.Text;
4
5namespace Doc.LAMA0288.Error;
6
7public class ToStringAttribute : TypeAspect
8{
9    [Introduce( WhenExists = OverrideStrategy.Override, Name = "ToString" )]
10    public string IntroducedToString()
11    {
12        var stringBuilder = meta.CompileTime( new StringBuilder() );
13        stringBuilder.Append( meta.Target.Type.Name );
14        stringBuilder.Append( ": " );
15
16        var properties = meta.Target.Type.Properties.Where( p => !p.IsStatic ).ToList();
17
18        // 'i' is a run-time variable, so 'if ( i > 0 )' is a run-time condition.
19        var i = 0;
20
21        foreach ( var property in properties )
22        {
23            if ( i > 0 )
24            {
25                // Error: a compile-time method call with side effects under a run-time condition.
                Error LAMA0288: The compile-time expression 'stringBuilder.Append( ", " )' cannot be used as a statement here because the statement is in a block whose execution depends on a run-time condition ('if ( i > 0 )'). A compile-time expression used as a statement is assumed to have side effects, and these side effects cannot depend on a run-time condition. Move the statement out of this block, or make the condition compile-time.

26                stringBuilder.Append( ", " );
27            }
28
29            stringBuilder.Append( property.Name );
30
31            i++;
32        }
33
34        return stringBuilder.ToString();
35    }
36}
37
1namespace Doc.LAMA0288.Error;
2
3[ToString]
4internal partial class Point
5{
6    public int X { get; set; }
7
8    public int Y { get; set; }
9}
10

Resolution

Depending on your intent, do one of the following:

  • If the condition should be evaluated at compile time, make it compile-time, for instance by initializing the variables it uses with meta.CompileTime. The following example uses this approach.
  • Move the compile-time statement out of the block.
  • If the state is only needed inside the block, declare the compile-time variable inside the block.

1using Metalama.Framework.Aspects;
2using System.Linq;
3using System.Text;
4
5namespace Doc.LAMA0288.Fixed;
6
7public class ToStringAttribute : TypeAspect
8{
9    [Introduce( WhenExists = OverrideStrategy.Override, Name = "ToString" )]
10    public string IntroducedToString()
11    {
12        var stringBuilder = meta.CompileTime( new StringBuilder() );
13        stringBuilder.Append( meta.Target.Type.Name );
14        stringBuilder.Append( ": " );
15
16        var properties = meta.Target.Type.Properties.Where( p => !p.IsStatic ).ToList();
17
18        // Fixed: 'i' is a compile-time variable, so 'if ( i > 0 )' is a compile-time condition.
19        var i = meta.CompileTime( 0 );
20
21        foreach ( var property in properties )
22        {
23            if ( i > 0 )
24            {
25                stringBuilder.Append( ", " );
26            }
27
28            stringBuilder.Append( property.Name );
29
30            i++;
31        }
32
33        return stringBuilder.ToString();
34    }
35}
36
Source Code
1namespace Doc.LAMA0288.Fixed;
2

3[ToString]
4internal partial class Point
5{
6    public int X { get; set; }
7
8    public int Y { get; set; }
9}
10
Transformed Code
1namespace Doc.LAMA0288.Fixed;
2
3[ToString]
4internal partial class Point
5{
6    public int X { get; set; }
7
8    public int Y { get; set; }
9
10    public override string ToString()
11    {
12        return "Point: X, Y";
13    }
14}
15

See also