Open sandboxFocusImprove this doc

LAMA0699: The implementation of a constructor or indexer can't be inlined

Severity Error
Reported by Metalama.Framework
Message The implementation of '<TargetDeclaration>' provided by '<AspectType>' cannot be inlined, but Metalama can only generate inlined code for constructors and indexers. Call meta.Proceed() at most once in the template, in a simple statement such as 'meta.Proceed();' or 'return meta.Proceed();'.

Cause

When an aspect overrides a member, meta.Proceed stands for the previous implementation of the member: the source code or the override of a previous aspect. Metalama prefers to inline this implementation, that is, to copy its code in place of the meta.Proceed() call. When inlining isn't possible, Metalama moves the implementation to a separate, private member and calls this member instead.

Constructors and indexers can't be moved to a separate member, so their implementations must always be inlined. Metalama reports LAMA0699 when an implementation of a constructor or an indexer can't be inlined. An implementation is inlined only when the next template references it once, in a supported statement:

  • For a constructor, a single meta.Proceed(); statement.
  • For an indexer accessor, a single meta.Proceed() call in a statement such as return meta.Proceed();, var value = meta.Proceed();, or meta.Proceed();.

In the message, <TargetDeclaration> is the overridden constructor or indexer, and <AspectType> is the aspect that provides the implementation that can't be inlined, or source code for the original implementation.

In the following example, the LogConstructorErrors aspect retries the constructor by calling meta.Proceed() a second time in a catch block.

1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using System;
4
5namespace Doc.LAMA0699.Error;
6
7public class LogConstructorErrorsAttribute : TypeAspect
8{
9    public override void BuildAspect( IAspectBuilder<INamedType> builder )
10    {
11        foreach ( var constructor in builder.Target.Constructors )
12        {
13            builder.With( constructor ).Override( nameof(this.OverrideConstructor) );
14        }
15    }
16
17    [Template]
18    public void OverrideConstructor()
19    {
20        try
21        {
22            meta.Proceed();
23        }
24        catch ( Exception e )
25        {
26            Console.WriteLine( $"{meta.Target.Constructor} failed: {e.Message}" );
27
28            // Error: retrying with a second call to meta.Proceed() makes the constructor impossible to inline.
29            meta.Proceed();
30        }
31    }
32}
33
1using System;
2
3namespace Doc.LAMA0699.Error;
4
5[LogConstructorErrors]
6public class DatabaseConnection
7{
    Error LAMA0699: The implementation of 'DatabaseConnection.DatabaseConnection(string)' provided by 'source code' cannot be inlined, but Metalama can only generate inlined code for constructors and indexers. Call meta.Proceed() at most once in the template, in a simple statement such as 'meta.Proceed();' or 'return meta.Proceed();'.

8    public DatabaseConnection( string connectionString )
9    {
10        Console.WriteLine( $"Connecting to '{connectionString}'." );
11    }
12}
13

Resolution

Call meta.Proceed() only once in templates that override constructors or indexers, and use it in a simple statement. If you need to execute some logic several times, move it to a method, which can be called any number of times, and override this method instead.

In the following example, the aspect calls meta.Proceed() once and rethrows the exception instead of retrying.

1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using System;
4
5namespace Doc.LAMA0699.Fixed;
6
7public class LogConstructorErrorsAttribute : TypeAspect
8{
9    public override void BuildAspect( IAspectBuilder<INamedType> builder )
10    {
11        foreach ( var constructor in builder.Target.Constructors )
12        {
13            builder.With( constructor ).Override( nameof(this.OverrideConstructor) );
14        }
15    }
16
17    [Template]
18    public void OverrideConstructor()
19    {
20        try
21        {
22            meta.Proceed();
23        }
24        catch ( Exception e )
25        {
26            Console.WriteLine( $"{meta.Target.Constructor} failed: {e.Message}" );
27
28            // Fixed: meta.Proceed() is called only once, and the exception is rethrown.
29            throw;
30        }
31    }
32}
33
Source Code
1using System;
2
3namespace Doc.LAMA0699.Fixed;
4
5[LogConstructorErrors]
6public class DatabaseConnection
7{
8    public DatabaseConnection( string connectionString )
9    {
10        Console.WriteLine( $"Connecting to '{connectionString}'." );
11    }






12}


13
Transformed Code
1using System;
2
3namespace Doc.LAMA0699.Fixed;
4
5[LogConstructorErrors]
6public class DatabaseConnection
7{
8    public DatabaseConnection(string connectionString)
9    {
10        try
11        {
12            Console.WriteLine($"Connecting to '{connectionString}'.");
13        }
14        catch (Exception e)
15        {
16            Console.WriteLine($"DatabaseConnection.DatabaseConnection(string) failed: {e.Message}");
17            throw;
18        }
19    }
20}
21

See also