Open sandboxFocusImprove this doc

LAMA0236: Compile-time code references a run-time-only declaration

Severity Error
Reported by Metalama.Framework
Message Cannot reference '<ReferencedSymbol>' in '<DeclaringSymbol>' because '<ReferencedSymbol>' is run-time-only but '<DeclaringSymbol>' is <DeclaringSymbolScope>. Run-time-only declarations can be referenced only in templates and in run-time code.

Cause

Metalama classifies every declaration as compile-time, run-time, or run-time-or-compile-time. Compile-time code, and run-time-or-compile-time code such as aspect classes, is compiled into a separate assembly that Metalama executes during the build. This assembly doesn't contain the run-time-only types of your project, such as the classes of your application, so compile-time code can't reference them.

Metalama reports LAMA0236 when code that isn't run-time-only references a run-time-only declaration, outside of a template and outside of a nameof or typeof expression. This typically happens in the following situations:

  • A non-template member of an aspect, such as a field, a property, or a helper method, uses a run-time-only type.
  • A member of a class marked with CompileTimeAttribute uses a run-time-only type.

In the message, <ReferencedSymbol> is the run-time-only declaration, <DeclaringSymbol> is the declaration that references it, and <DeclaringSymbolScope> is the scope of <DeclaringSymbol>. When the offending member has a run-time-only type, as a field in the example below, <DeclaringSymbol> is the containing type instead of the member.

In the following example, the Log aspect stores an instance of the run-time Logger class in a field of the aspect.

1using Metalama.Framework.Aspects;
2
3namespace Doc.LAMA0236.Error;
4
5public class LogAttribute : OverrideMethodAspect
6{
7    // Error: the aspect class cannot hold an instance of the run-time-only Logger class.
    Error LAMA0236: Cannot reference 'Logger' in 'LogAttribute' because 'Logger' is run-time-only but 'LogAttribute' is run-time-or-compile-time. Run-time-only declarations can be referenced only in templates and in run-time code.

8    private readonly Logger _logger = new Logger();
9
10    public override dynamic? OverrideMethod()
11    {
12        this._logger.Write( $"Entering {meta.Target.Method}." );
13
14        return meta.Proceed();
15    }
16}
17
1using System;
2
3namespace Doc.LAMA0236.Error;
4
5public class Logger
6{
7    public void Write( string message ) => Console.WriteLine( message );
8}
9
10internal partial class OrderService
11{
12    [Log]
13    public void PlaceOrder( int orderId ) { }
14}
15

Resolution

Depending on your intent, do one of the following:

  • If the run-time object belongs to the target code, introduce the member into the target type by marking it with IntroduceAttribute. Introduced members are templates, so they can use run-time types. The following example uses this fix.
  • If the code only needs the run-time object while the program runs, move it into a template, for instance by creating the object in the body of OverrideMethod.
  • If the code only needs to identify the type, use typeof or nameof, which are allowed, or use the code model, for instance meta.Target.Type, at compile time.
  • If the referenced type must be available at compile time, mark it with RunTimeOrCompileTimeAttribute. This is only possible if all its dependencies are also available at compile time.

1using Metalama.Framework.Aspects;
2
3namespace Doc.LAMA0236.Fixed;
4
5public class LogAttribute : OverrideMethodAspect
6{
7    // Fixed: the field is introduced into the target type, where Logger is available at run time.
8    [Introduce]
9    private readonly Logger _logger = new Logger();
10
11    public override dynamic? OverrideMethod()
12    {
13        this._logger.Write( $"Entering {meta.Target.Method}." );
14
15        return meta.Proceed();
16    }
17}
18
Source Code
1using System;
2
3namespace Doc.LAMA0236.Fixed;
4
5public class Logger
6{
7    public void Write( string message ) => Console.WriteLine( message );
8}
9
10internal partial class OrderService
11{
12    [Log]
13    public void PlaceOrder( int orderId ) { }
14}
15
Transformed Code
1using System;
2
3namespace Doc.LAMA0236.Fixed;
4
5public class Logger
6{
7    public void Write(string message) => Console.WriteLine(message);
8}
9
10internal partial class OrderService
11{
12    [Log]
13    public void PlaceOrder(int orderId)
14    {
15        _logger.Write("Entering OrderService.PlaceOrder(int).");
16    }
17
18    private readonly Logger _logger = new Logger();
19}
20

See also