Open sandboxFocusImprove this doc

LAMA0259: The null-conditional operator mixes compile-time and run-time parts

Severity Error
Reported by Metalama.Framework
Message The null-conditional operator cannot be used in the expression '<Expression>', because '<CompileTimeReceiver>' is compile-time, but '<RunTimeAccess>' is run-time. Consider using a separate null-checking 'if' statement instead of the null-conditional operator.

Cause

In a T# template, the null-conditional operator (?.) must be evaluated entirely at compile time or entirely at run time. When the expression on the left of ?. is compile-time, for instance a field of the code model, but the member on the right returns a run-time value, for instance Value, the null check would happen at compile time while the access happens at run time. Metalama can't generate code for such an expression and reports LAMA0259.

In the message, <CompileTimeReceiver> is the compile-time receiver and <RunTimeAccess> is the run-time access.

In the following example, the aspect looks for a _log field in the target type and uses ?. to access the field's value only when the field exists.

1using Metalama.Framework.Aspects;
2using System.Linq;
3
4namespace Doc.LAMA0259.Error;
5
6public class LogAttribute : OverrideMethodAspect
7{
8    public override dynamic? OverrideMethod()
9    {
10        var logField = meta.Target.Type.Fields.OfName( "_log" ).SingleOrDefault();
11
12        // Error: logField is compile-time, but logField.Value is run-time.
        Error LAMA0259: The null-conditional operator cannot be used in the expression 'logField?.Value', because 'logField' is compile-time, but 'logField.Value' is run-time. Consider using a separate null-checking 'if' statement instead of the null-conditional operator.

13        var log = logField?.Value;
14        log?.WriteLine( $"Executing {meta.Target.Method}." );
15
16        return meta.Proceed();
17    }
18}
19
1using System;
2using System.IO;
3
4namespace Doc.LAMA0259.Error;
5
6internal class OrderService
7{
8    private readonly TextWriter _log = Console.Out;
9
10    [Log]
11    public void PlaceOrder( string product ) => Console.WriteLine( $"Ordered {product}." );
12}
13

Resolution

Check the compile-time expression for null in a separate if statement. Because the condition is compile-time, Metalama evaluates it while expanding the template and generates the run-time code only when the condition is true, as shown in the following example.

1using Metalama.Framework.Aspects;
2using System.Linq;
3
4namespace Doc.LAMA0259.Fixed;
5
6public class LogAttribute : OverrideMethodAspect
7{
8    public override dynamic? OverrideMethod()
9    {
10        var logField = meta.Target.Type.Fields.OfName( "_log" ).SingleOrDefault();
11
12        // Fixed: a compile-time 'if' statement checks logField for null.
13        if ( logField != null )
14        {
15            var log = logField.Value;
16            log?.WriteLine( $"Executing {meta.Target.Method}." );
17        }
18
19        return meta.Proceed();
20    }
21}
22
Source Code
1using System;
2using System.IO;
3
4namespace Doc.LAMA0259.Fixed;
5
6internal class OrderService
7{
8    private readonly TextWriter _log = Console.Out;
9
10    [Log]
11    public void PlaceOrder( string product ) => Console.WriteLine( $"Ordered {product}." );
12}
13
Transformed Code
1using System;
2using System.IO;
3
4namespace Doc.LAMA0259.Fixed;
5
6internal class OrderService
7{
8    private readonly TextWriter _log = Console.Out;
9
10    [Log]
11    public void PlaceOrder(string product)
12    {
13        var log = _log;
14        log?.WriteLine("Executing OrderService.PlaceOrder(string).");
15        Console.WriteLine($"Ordered {product}.");
16    }
17}
18

See also