From f847008dd772382369cb329d0378b0d653940ee3 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 31 Mar 2026 00:25:34 +0000
Subject: [PATCH 1/5] Initial plan
From c1d3e37d7cf4be27e57c7485b2a537d910040e70 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 31 Mar 2026 00:40:54 +0000
Subject: [PATCH 2/5] feat: allow null syntax nodes in Block, Region,
Operation, AttributeValue
Agent-Logs-Url: https://github.com/jonathanvdc/MLIR.NET/sessions/1834ea25-3ce4-42c5-923b-4feca90152ad
Co-authored-by: jonathanvdc <9839946+jonathanvdc@users.noreply.github.com>
---
src/MLIR/Semantics/AttributeValue.cs | 6 +-
src/MLIR/Semantics/Block.cs | 40 ++++++----
src/MLIR/Semantics/Operation.cs | 21 +++---
src/MLIR/Semantics/Region.cs | 8 +-
src/MLIR/Transforms/AssemblySyntaxBuilder.cs | 57 +++++++++++---
tests/MLIR.Tests/SemanticTests.cs | 78 ++++++++++++++++++--
6 files changed, 161 insertions(+), 49 deletions(-)
diff --git a/src/MLIR/Semantics/AttributeValue.cs b/src/MLIR/Semantics/AttributeValue.cs
index 65b6c7c..00f4727 100644
--- a/src/MLIR/Semantics/AttributeValue.cs
+++ b/src/MLIR/Semantics/AttributeValue.cs
@@ -11,7 +11,7 @@ public abstract class AttributeValue
///
/// Initializes a new instance of the class.
///
- protected AttributeValue(RawSyntaxText syntax, string? name, AttributeDefinition? definition, SourceLocation location)
+ protected AttributeValue(RawSyntaxText? syntax, string? name, AttributeDefinition? definition, SourceLocation location)
{
Syntax = syntax;
Name = name;
@@ -20,9 +20,9 @@ protected AttributeValue(RawSyntaxText syntax, string? name, AttributeDefinition
}
///
- /// Gets the raw syntax text for the attribute value.
+ /// Gets the raw syntax text for the attribute value, or null if this is a synthetic attribute value with no corresponding source text.
///
- public RawSyntaxText Syntax { get; }
+ public RawSyntaxText? Syntax { get; }
///
/// Gets the canonical attribute name, if one was recognized.
diff --git a/src/MLIR/Semantics/Block.cs b/src/MLIR/Semantics/Block.cs
index 8da58a9..846c23e 100644
--- a/src/MLIR/Semantics/Block.cs
+++ b/src/MLIR/Semantics/Block.cs
@@ -6,41 +6,49 @@ namespace MLIR.Semantics;
///
/// Represents a semantic block within a region.
///
-///
-/// Initializes a new instance of the class.
-///
-/// The concrete syntax node for the block.
-/// The semantic block arguments.
-/// The operations contained in the block.
-public sealed class Block(BlockSyntax syntax, IReadOnlyList arguments, IReadOnlyList operations)
+public sealed class Block
{
///
- /// Gets the concrete syntax node for the block.
+ /// Initializes a new instance of the class.
///
- public BlockSyntax Syntax { get; } = syntax;
+ /// The concrete syntax node for the block, or null for a synthetic block with no corresponding source text.
+ /// The semantic block arguments.
+ /// The operations contained in the block.
+ public Block(BlockSyntax? syntax, IReadOnlyList arguments, IReadOnlyList operations)
+ {
+ Syntax = syntax;
+ Arguments = arguments;
+ Operations = operations;
+ LabelReference = syntax != null ? new BlockReference(syntax.LabelToken) : null;
+ }
+
+ ///
+ /// Gets the concrete syntax node for the block, or null if this is a synthetic block with no corresponding source text.
+ ///
+ public BlockSyntax? Syntax { get; }
///
/// Gets the semantic block arguments.
///
- public IReadOnlyList Arguments { get; } = arguments;
+ public IReadOnlyList Arguments { get; }
///
/// Gets the operations contained in the block.
///
- public IReadOnlyList Operations { get; } = operations;
+ public IReadOnlyList Operations { get; }
///
- /// Gets the block label, including the leading ^.
+ /// Gets the block label, including the leading ^, or null if this is a synthetic block with no label.
///
- public string Label => Syntax.Label;
+ public string? Label => Syntax?.Label;
///
- /// Gets the typed reference to the block label.
+ /// Gets the typed reference to the block label, or null if this is a synthetic block with no label.
///
- public BlockReference LabelReference { get; } = new BlockReference(syntax.LabelToken);
+ public BlockReference? LabelReference { get; }
///
/// Gets the source location of the block label, if known.
///
- public SourceLocation Location => LabelReference.Location;
+ public SourceLocation Location => LabelReference?.Location ?? SourceLocation.Unknown;
}
diff --git a/src/MLIR/Semantics/Operation.cs b/src/MLIR/Semantics/Operation.cs
index 0ee5926..e0465ee 100644
--- a/src/MLIR/Semantics/Operation.cs
+++ b/src/MLIR/Semantics/Operation.cs
@@ -15,7 +15,7 @@ public abstract class Operation
/// Initializes a new instance of the class.
///
protected Operation(
- OperationSyntax syntax,
+ OperationSyntax? syntax,
string name,
OperationDefinition? definition)
{
@@ -25,9 +25,9 @@ protected Operation(
}
///
- /// Gets the concrete syntax node for the operation.
+ /// Gets the concrete syntax node for the operation, or null if this is a synthetic operation with no corresponding source text.
///
- public OperationSyntax Syntax { get; }
+ public OperationSyntax? Syntax { get; }
///
/// Gets the canonical operation name without MLIR string-literal quoting.
@@ -75,9 +75,9 @@ protected Operation(
public bool IsKnown => Definition != null;
///
- /// Gets the operation name exactly as written in the source.
+ /// Gets the operation name exactly as written in the source, or null if this is a synthetic operation with no corresponding source text.
///
- public string SyntaxName => Syntax.Name;
+ public string? SyntaxName => Syntax?.Name;
///
/// Gets the dialect namespace portion of the operation name, if present.
@@ -109,7 +109,7 @@ public string DialectName
///
/// Gets the source location of the operation name, if known.
///
- public SourceLocation Location => SourceLocation.FromToken(Syntax.NameToken);
+ public SourceLocation Location => Syntax != null ? SourceLocation.FromToken(Syntax.NameToken) : SourceLocation.Unknown;
///
/// Determines whether the operation has an attribute with the supplied name.
@@ -148,19 +148,22 @@ public NamedAttribute GetAttribute(string name)
///
public GenericOperationBodySyntax GetGenericBody()
{
- if (Syntax.Body is GenericOperationBodySyntax genericBody)
+ if (Syntax?.Body is GenericOperationBodySyntax genericBody)
{
return genericBody;
}
// TODO: preserve tokens, avoid stringifying and reparsing type signatures, etc.
+ // For synthetic regions and attributes (null Syntax), placeholder values are used:
+ // - Regions without syntax produce an empty region body; AssemblySyntaxBuilder replaces them with fully built syntax.
+ // - Attributes without syntax produce an empty value string, as there is no source text to round-trip.
return (GenericOperationBodySyntax)Factory.Op(
Name,
Results,
Operands,
Successors,
- Regions.Select(r => r.Syntax).ToList(),
- Attributes.Select(a => Factory.Attr(a.Name, a.Value.Syntax.Text)).ToList(),
+ Regions.Select(r => r.Syntax ?? new RegionSyntax([])).ToList(),
+ Attributes.Select(a => Factory.Attr(a.Name, a.Value.Syntax?.Text ?? string.Empty)).ToList(),
TypeSignatureReference != null ? TypeSignatureReference.Syntax : null
).Body;
}
diff --git a/src/MLIR/Semantics/Region.cs b/src/MLIR/Semantics/Region.cs
index 1406599..c2c574d 100644
--- a/src/MLIR/Semantics/Region.cs
+++ b/src/MLIR/Semantics/Region.cs
@@ -9,14 +9,14 @@ namespace MLIR.Semantics;
///
/// Initializes a new instance of the class.
///
-/// The concrete syntax node for the region.
+/// The concrete syntax node for the region, or null for a synthetic region with no corresponding source text.
/// The semantic blocks contained in the region.
-public sealed class Region(RegionSyntax syntax, IReadOnlyList blocks)
+public sealed class Region(RegionSyntax? syntax, IReadOnlyList blocks)
{
///
- /// Gets the concrete syntax node for the region.
+ /// Gets the concrete syntax node for the region, or null if this is a synthetic region with no corresponding source text.
///
- public RegionSyntax Syntax { get; } = syntax;
+ public RegionSyntax? Syntax { get; } = syntax;
///
/// Gets the semantic blocks contained in the region.
diff --git a/src/MLIR/Transforms/AssemblySyntaxBuilder.cs b/src/MLIR/Transforms/AssemblySyntaxBuilder.cs
index fec67b1..96190b0 100644
--- a/src/MLIR/Transforms/AssemblySyntaxBuilder.cs
+++ b/src/MLIR/Transforms/AssemblySyntaxBuilder.cs
@@ -1,5 +1,6 @@
namespace MLIR.Transforms;
+using System;
using System.Collections.Generic;
using MLIR.Construction;
using MLIR.Semantics;
@@ -45,11 +46,36 @@ public OperationSyntax WithBody(Operation operation, OperationBodySyntax body)
public OperationSyntax RewriteOperation(Operation operation, OperationBodySyntax body, SyntaxToken? nameToken = null)
{
+ if (operation.Syntax != null)
+ {
+ return new OperationSyntax(
+ operation.Syntax.ResultTokens,
+ operation.Syntax.ResultCommaTokens,
+ operation.Syntax.EqualsToken,
+ nameToken ?? operation.Syntax.NameToken,
+ body);
+ }
+
+ // Synthesize tokens for a synthetic operation with no source syntax.
+ var results = operation.Results;
+ var resultTokens = new List(results.Count);
+ foreach (var result in results)
+ {
+ resultTokens.Add(new SyntaxToken(result));
+ }
+
+ var resultCommaTokens = new List(Math.Max(0, results.Count - 1));
+ for (var i = 1; i < results.Count; i++)
+ {
+ resultCommaTokens.Add(new SyntaxToken(","));
+ }
+
+ var equalsToken = results.Count > 0 ? (SyntaxToken?)new SyntaxToken("=") : null;
return new OperationSyntax(
- operation.Syntax.ResultTokens,
- operation.Syntax.ResultCommaTokens,
- operation.Syntax.EqualsToken,
- nameToken ?? operation.Syntax.NameToken,
+ resultTokens,
+ resultCommaTokens,
+ equalsToken,
+ nameToken ?? new SyntaxToken(operation.Name),
body);
}
@@ -79,7 +105,10 @@ public RegionSyntax BuildRegion(Region region)
blocks.Add(BuildBlock(block));
}
- return new RegionSyntax(region.Syntax.OpenBraceToken, blocks, region.Syntax.CloseBraceToken);
+ return new RegionSyntax(
+ region.Syntax?.OpenBraceToken ?? new SyntaxToken("{"),
+ blocks,
+ region.Syntax?.CloseBraceToken ?? new SyntaxToken("}"));
}
public BlockSyntax BuildBlock(Block block)
@@ -90,11 +119,19 @@ public BlockSyntax BuildBlock(Block block)
operations.Add(BuildOperation(operation));
}
- return new BlockSyntax(
- block.Syntax.LabelToken,
- block.Syntax.Arguments,
- block.Syntax.ColonToken,
- operations);
+ if (block.Syntax != null)
+ {
+ return new BlockSyntax(
+ block.Syntax.LabelToken,
+ block.Syntax.Arguments,
+ block.Syntax.ColonToken,
+ operations);
+ }
+
+ // Synthesize a block syntax for a synthetic block with no source syntax.
+ // Use "^entry" as the fallback label: the parser uses this synthetic label for implicit
+ // entry blocks, and BlockSyntax.WriteTo omits it during printing when there are no arguments.
+ return new BlockSyntax(block.Label ?? "^entry", [], operations);
}
}
}
diff --git a/tests/MLIR.Tests/SemanticTests.cs b/tests/MLIR.Tests/SemanticTests.cs
index 69790ff..0e76c33 100644
--- a/tests/MLIR.Tests/SemanticTests.cs
+++ b/tests/MLIR.Tests/SemanticTests.cs
@@ -175,6 +175,29 @@ public GeneratedAddIOperation(OperationConstructionContext context)
public ValueReference ResultValue { get; }
}
+ private sealed class SyntheticOperation : Operation
+ {
+ public SyntheticOperation(string name)
+ : base(null, name, null)
+ {
+ }
+
+ public override IReadOnlyList Regions => [];
+ public override IReadOnlyList Attributes => [];
+ public override TypeReference? TypeSignatureReference => null;
+ public override IReadOnlyList ResultValues => [];
+ public override IReadOnlyList OperandValues => [];
+ public override IReadOnlyList SuccessorReferences => [];
+ }
+
+ private sealed class SyntheticAttributeValue : AttributeValue
+ {
+ public SyntheticAttributeValue(string name)
+ : base(null, name, null, SourceLocation.Unknown)
+ {
+ }
+ }
+
private sealed class PrefixConstantAssemblyFormat : IOperationAssemblyFormat
{
public bool TryParse(
@@ -215,11 +238,11 @@ public OperationSyntax Rewrite(Operation operation, OperationSyntaxTransformCont
{
var genericBody = context.TransformGenericBody(operation);
var body = new PrefixConstantBodySyntax(
- operation.HasAttribute("value") ? operation.GetAttribute("value").Value.Syntax : new RawSyntaxText(string.Empty),
+ operation.HasAttribute("value") ? operation.GetAttribute("value").Value.Syntax! : new RawSyntaxText(string.Empty),
genericBody.TypeSignatureColonToken ?? new SyntaxToken(":"),
genericBody.TypeSignatureSyntax ?? throw new InvalidOperationException("Expected a type signature in the generic body for rewriting."),
genericBody.Attributes);
- var sourceNameToken = operation.Syntax.NameToken;
+ var sourceNameToken = operation.Syntax!.NameToken;
var rewrittenNameToken = new SyntaxToken(operation.Name, sourceNameToken.LeadingTrivia, sourceNameToken.Line, sourceNameToken.Column);
return context.RewriteOperation(operation, body, rewrittenNameToken);
}
@@ -234,7 +257,7 @@ public void Bind(AttributeValue attribute, AttributeAssemblyBindingContext conte
denseAttribute.BindDense();
}
- if (!attribute.Syntax.Text.Contains("tensor<"))
+ if (!attribute.Syntax!.Text.Contains("tensor<"))
{
context.Report("dense attribute literals should mention a tensor type.");
}
@@ -344,7 +367,7 @@ public void BindsNestedRegionsBlocksArgumentsAndAttributes()
Assert.Equal("i32", block.Arguments[0].Type.Text);
Assert.Single(nestedOperation.Attributes);
Assert.Equal("value", nestedOperation.Attributes[0].Name);
- Assert.Equal("1 : i32", nestedOperation.Attributes[0].Value.Syntax.Text);
+ Assert.Equal("1 : i32", nestedOperation.Attributes[0].Value.Syntax!.Text);
Assert.Equal("%arg0", block.Arguments[0].Value.Name);
Assert.Equal("i32", block.Arguments[0].TypeReference.Name);
}
@@ -608,7 +631,7 @@ public void OperationCanRetrieveAttributesByName()
var attribute = module.Operations[0].GetAttribute("value");
Assert.Equal("value", attribute.Name);
- Assert.Equal("0 : i32", attribute.Value.Syntax.Text);
+ Assert.Equal("0 : i32", attribute.Value.Syntax!.Text);
}
[Fact]
@@ -625,7 +648,7 @@ public void OperationViewProvidesTypedWrapperOverSemanticOperation()
Assert.Equal("%0", view.Results[0]);
Assert.Equal("%0", view.ResultValue.Name);
- Assert.Equal("0 : i32", view.ValueAttribute.Value.Syntax.Text);
+ Assert.Equal("0 : i32", view.ValueAttribute.Value.Syntax!.Text);
}
[Fact]
@@ -756,7 +779,7 @@ public void DocumentCanParseRegisteredCustomAssemblyFormats()
var module = Binder.BindModule(document.Module, registry);
Assert.Equal("%0 = arith.constant 0 : i32", module.ToText());
- Assert.Equal("0", module.Operations[0].GetAttribute("value").Value.Syntax.Text);
+ Assert.Equal("0", module.Operations[0].GetAttribute("value").Value.Syntax!.Text);
}
[Fact]
@@ -953,4 +976,45 @@ public void RegistryRejectsDuplicateAttributeAndTypeRegistrations()
Assert.Contains("already registered", attributeException.Message);
Assert.Contains("already registered", typeException.Message);
}
+
+ [Fact]
+ public void SyntheticBlockHasNullSyntaxAndUnknownLocation()
+ {
+ var syntheticBlock = new Block(null, [], []);
+
+ Assert.Null(syntheticBlock.Syntax);
+ Assert.Null(syntheticBlock.Label);
+ Assert.Null(syntheticBlock.LabelReference);
+ Assert.False(syntheticBlock.Location.IsKnown);
+ }
+
+ [Fact]
+ public void SyntheticRegionHasNullSyntax()
+ {
+ var syntheticRegion = new Region(null, []);
+
+ Assert.Null(syntheticRegion.Syntax);
+ }
+
+ [Fact]
+ public void SyntheticOperationHasNullSyntaxAndUnknownLocation()
+ {
+ var syntheticOperation = new SyntheticOperation("test.synthetic");
+
+ Assert.Null(syntheticOperation.Syntax);
+ Assert.Null(syntheticOperation.SyntaxName);
+ Assert.False(syntheticOperation.Location.IsKnown);
+ Assert.Equal("test.synthetic", syntheticOperation.Name);
+ Assert.Equal("test", syntheticOperation.DialectName);
+ }
+
+ [Fact]
+ public void SyntheticAttributeValueHasNullSyntax()
+ {
+ var syntheticAttribute = new SyntheticAttributeValue("test");
+
+ Assert.Null(syntheticAttribute.Syntax);
+ Assert.Equal("test", syntheticAttribute.Name);
+ Assert.False(syntheticAttribute.Location.IsKnown);
+ }
}
From 48bdd53ef3037838de1cb269bfc190edffe05652 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 31 Mar 2026 00:57:33 +0000
Subject: [PATCH 3/5] feat: address review feedback - AttributeValueSyntax,
split Block constructors, remove null label fallback
Agent-Logs-Url: https://github.com/jonathanvdc/MLIR.NET/sessions/1a5fa164-7732-4774-8ff6-f3625f3f0a17
Co-authored-by: jonathanvdc <9839946+jonathanvdc@users.noreply.github.com>
---
src/MLIR/Semantics/AttributeValue.cs | 6 ++--
.../AttributeValueConstructionContext.cs | 6 ++--
src/MLIR/Semantics/Binder.cs | 4 +--
src/MLIR/Semantics/Block.cs | 31 ++++++++++++++-----
src/MLIR/Semantics/Operation.cs | 8 ++++-
src/MLIR/Semantics/UnknownAttributeValue.cs | 2 +-
src/MLIR/Transforms/AssemblySyntaxBuilder.cs | 2 +-
tests/MLIR.Tests/SemanticTests.cs | 17 +++++-----
8 files changed, 50 insertions(+), 26 deletions(-)
diff --git a/src/MLIR/Semantics/AttributeValue.cs b/src/MLIR/Semantics/AttributeValue.cs
index 00f4727..56c024d 100644
--- a/src/MLIR/Semantics/AttributeValue.cs
+++ b/src/MLIR/Semantics/AttributeValue.cs
@@ -11,7 +11,7 @@ public abstract class AttributeValue
///
/// Initializes a new instance of the class.
///
- protected AttributeValue(RawSyntaxText? syntax, string? name, AttributeDefinition? definition, SourceLocation location)
+ protected AttributeValue(AttributeValueSyntax? syntax, string? name, AttributeDefinition? definition, SourceLocation location)
{
Syntax = syntax;
Name = name;
@@ -20,9 +20,9 @@ protected AttributeValue(RawSyntaxText? syntax, string? name, AttributeDefinitio
}
///
- /// Gets the raw syntax text for the attribute value, or null if this is a synthetic attribute value with no corresponding source text.
+ /// Gets the syntax for the attribute value, or null if this is a synthetic attribute value with no corresponding source text.
///
- public RawSyntaxText? Syntax { get; }
+ public AttributeValueSyntax? Syntax { get; }
///
/// Gets the canonical attribute name, if one was recognized.
diff --git a/src/MLIR/Semantics/AttributeValueConstructionContext.cs b/src/MLIR/Semantics/AttributeValueConstructionContext.cs
index 2d81498..169e9ca 100644
--- a/src/MLIR/Semantics/AttributeValueConstructionContext.cs
+++ b/src/MLIR/Semantics/AttributeValueConstructionContext.cs
@@ -8,7 +8,7 @@ namespace MLIR.Semantics;
///
public sealed class AttributeValueConstructionContext
{
- internal AttributeValueConstructionContext(RawSyntaxText syntax, string? name, AttributeDefinition definition, SourceLocation location)
+ internal AttributeValueConstructionContext(AttributeValueSyntax syntax, string? name, AttributeDefinition definition, SourceLocation location)
{
Syntax = syntax;
Name = name;
@@ -17,9 +17,9 @@ internal AttributeValueConstructionContext(RawSyntaxText syntax, string? name, A
}
///
- /// Gets the raw syntax text for the attribute value.
+ /// Gets the syntax for the attribute value.
///
- public RawSyntaxText Syntax { get; }
+ public AttributeValueSyntax Syntax { get; }
///
/// Gets the canonical attribute name, if one was recognized.
diff --git a/src/MLIR/Semantics/Binder.cs b/src/MLIR/Semantics/Binder.cs
index b5d5733..268c542 100644
--- a/src/MLIR/Semantics/Binder.cs
+++ b/src/MLIR/Semantics/Binder.cs
@@ -319,12 +319,12 @@ public AttributeValue BindAttributeValue(RawSyntaxText syntax)
var location = syntax.Location;
if (definition != null)
{
- attribute = definition.Factory(new AttributeValueConstructionContext(syntax, canonicalName, definition, location));
+ attribute = definition.Factory(new AttributeValueConstructionContext(new RawAttributeValueSyntax(syntax), canonicalName, definition, location));
definition.AssemblyFormat?.Bind(attribute, new AttributeAssemblyBindingContext(attribute, diagnostics));
}
else
{
- attribute = new UnknownAttributeValue(syntax, canonicalName, definition, location);
+ attribute = new UnknownAttributeValue(new RawAttributeValueSyntax(syntax), canonicalName, definition, location);
}
return attribute;
diff --git a/src/MLIR/Semantics/Block.cs b/src/MLIR/Semantics/Block.cs
index 846c23e..e8fb09e 100644
--- a/src/MLIR/Semantics/Block.cs
+++ b/src/MLIR/Semantics/Block.cs
@@ -9,17 +9,34 @@ namespace MLIR.Semantics;
public sealed class Block
{
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class from a concrete syntax node.
+ /// The block label is taken from the syntax node.
///
- /// The concrete syntax node for the block, or null for a synthetic block with no corresponding source text.
+ /// The concrete syntax node for the block.
/// The semantic block arguments.
/// The operations contained in the block.
- public Block(BlockSyntax? syntax, IReadOnlyList arguments, IReadOnlyList operations)
+ public Block(BlockSyntax syntax, IReadOnlyList arguments, IReadOnlyList operations)
{
Syntax = syntax;
+ Label = syntax.Label;
Arguments = arguments;
Operations = operations;
- LabelReference = syntax != null ? new BlockReference(syntax.LabelToken) : null;
+ LabelReference = new BlockReference(syntax.LabelToken);
+ }
+
+ ///
+ /// Initializes a new instance of the class as a synthetic block with no corresponding source text.
+ ///
+ /// The block label, including the leading ^.
+ /// The semantic block arguments.
+ /// The operations contained in the block.
+ public Block(string label, IReadOnlyList arguments, IReadOnlyList operations)
+ {
+ Syntax = null;
+ Label = label;
+ Arguments = arguments;
+ Operations = operations;
+ LabelReference = null;
}
///
@@ -38,12 +55,12 @@ public Block(BlockSyntax? syntax, IReadOnlyList arguments, IReadO
public IReadOnlyList Operations { get; }
///
- /// Gets the block label, including the leading ^, or null if this is a synthetic block with no label.
+ /// Gets the block label, including the leading ^.
///
- public string? Label => Syntax?.Label;
+ public string Label { get; }
///
- /// Gets the typed reference to the block label, or null if this is a synthetic block with no label.
+ /// Gets the typed reference to the block label, or null if this is a synthetic block with no label token.
///
public BlockReference? LabelReference { get; }
diff --git a/src/MLIR/Semantics/Operation.cs b/src/MLIR/Semantics/Operation.cs
index e0465ee..00bb8fe 100644
--- a/src/MLIR/Semantics/Operation.cs
+++ b/src/MLIR/Semantics/Operation.cs
@@ -163,7 +163,13 @@ public GenericOperationBodySyntax GetGenericBody()
Operands,
Successors,
Regions.Select(r => r.Syntax ?? new RegionSyntax([])).ToList(),
- Attributes.Select(a => Factory.Attr(a.Name, a.Value.Syntax?.Text ?? string.Empty)).ToList(),
+ Attributes.Select(a =>
+ {
+ var attrText = a.Value.Syntax != null && a.Value.Syntax.TryGetRawText(out var rawText)
+ ? rawText!.Text
+ : string.Empty;
+ return Factory.Attr(a.Name, attrText);
+ }).ToList(),
TypeSignatureReference != null ? TypeSignatureReference.Syntax : null
).Body;
}
diff --git a/src/MLIR/Semantics/UnknownAttributeValue.cs b/src/MLIR/Semantics/UnknownAttributeValue.cs
index f066907..647fe73 100644
--- a/src/MLIR/Semantics/UnknownAttributeValue.cs
+++ b/src/MLIR/Semantics/UnknownAttributeValue.cs
@@ -11,7 +11,7 @@ public sealed class UnknownAttributeValue : AttributeValue
///
/// Initializes a new instance of the class.
///
- public UnknownAttributeValue(RawSyntaxText syntax, string? name, AttributeDefinition? definition, SourceLocation location)
+ public UnknownAttributeValue(AttributeValueSyntax? syntax, string? name, AttributeDefinition? definition, SourceLocation location)
: base(syntax, name, definition, location)
{
}
diff --git a/src/MLIR/Transforms/AssemblySyntaxBuilder.cs b/src/MLIR/Transforms/AssemblySyntaxBuilder.cs
index 96190b0..077c4c2 100644
--- a/src/MLIR/Transforms/AssemblySyntaxBuilder.cs
+++ b/src/MLIR/Transforms/AssemblySyntaxBuilder.cs
@@ -131,7 +131,7 @@ public BlockSyntax BuildBlock(Block block)
// Synthesize a block syntax for a synthetic block with no source syntax.
// Use "^entry" as the fallback label: the parser uses this synthetic label for implicit
// entry blocks, and BlockSyntax.WriteTo omits it during printing when there are no arguments.
- return new BlockSyntax(block.Label ?? "^entry", [], operations);
+ return new BlockSyntax(block.Label, [], operations);
}
}
}
diff --git a/tests/MLIR.Tests/SemanticTests.cs b/tests/MLIR.Tests/SemanticTests.cs
index 0e76c33..a3ba107 100644
--- a/tests/MLIR.Tests/SemanticTests.cs
+++ b/tests/MLIR.Tests/SemanticTests.cs
@@ -237,8 +237,9 @@ public Operation Bind(OperationSyntax syntax, OperationDefinition definition, Bi
public OperationSyntax Rewrite(Operation operation, OperationSyntaxTransformContext context)
{
var genericBody = context.TransformGenericBody(operation);
+ var valueAttr = operation.Attributes.FirstOrDefault(a => a.Name == "value");
var body = new PrefixConstantBodySyntax(
- operation.HasAttribute("value") ? operation.GetAttribute("value").Value.Syntax! : new RawSyntaxText(string.Empty),
+ valueAttr != null && valueAttr.Value.Syntax != null ? valueAttr.Value.Syntax.GetRawText() : new RawSyntaxText(string.Empty),
genericBody.TypeSignatureColonToken ?? new SyntaxToken(":"),
genericBody.TypeSignatureSyntax ?? throw new InvalidOperationException("Expected a type signature in the generic body for rewriting."),
genericBody.Attributes);
@@ -257,7 +258,7 @@ public void Bind(AttributeValue attribute, AttributeAssemblyBindingContext conte
denseAttribute.BindDense();
}
- if (!attribute.Syntax!.Text.Contains("tensor<"))
+ if (!attribute.Syntax!.GetRawText().Text.Contains("tensor<"))
{
context.Report("dense attribute literals should mention a tensor type.");
}
@@ -367,7 +368,7 @@ public void BindsNestedRegionsBlocksArgumentsAndAttributes()
Assert.Equal("i32", block.Arguments[0].Type.Text);
Assert.Single(nestedOperation.Attributes);
Assert.Equal("value", nestedOperation.Attributes[0].Name);
- Assert.Equal("1 : i32", nestedOperation.Attributes[0].Value.Syntax!.Text);
+ Assert.Equal("1 : i32", nestedOperation.Attributes[0].Value.Syntax!.GetRawText().Text);
Assert.Equal("%arg0", block.Arguments[0].Value.Name);
Assert.Equal("i32", block.Arguments[0].TypeReference.Name);
}
@@ -631,7 +632,7 @@ public void OperationCanRetrieveAttributesByName()
var attribute = module.Operations[0].GetAttribute("value");
Assert.Equal("value", attribute.Name);
- Assert.Equal("0 : i32", attribute.Value.Syntax!.Text);
+ Assert.Equal("0 : i32", attribute.Value.Syntax!.GetRawText().Text);
}
[Fact]
@@ -648,7 +649,7 @@ public void OperationViewProvidesTypedWrapperOverSemanticOperation()
Assert.Equal("%0", view.Results[0]);
Assert.Equal("%0", view.ResultValue.Name);
- Assert.Equal("0 : i32", view.ValueAttribute.Value.Syntax!.Text);
+ Assert.Equal("0 : i32", view.ValueAttribute.Value.Syntax!.GetRawText().Text);
}
[Fact]
@@ -779,7 +780,7 @@ public void DocumentCanParseRegisteredCustomAssemblyFormats()
var module = Binder.BindModule(document.Module, registry);
Assert.Equal("%0 = arith.constant 0 : i32", module.ToText());
- Assert.Equal("0", module.Operations[0].GetAttribute("value").Value.Syntax!.Text);
+ Assert.Equal("0", module.Operations[0].GetAttribute("value").Value.Syntax!.GetRawText().Text);
}
[Fact]
@@ -980,10 +981,10 @@ public void RegistryRejectsDuplicateAttributeAndTypeRegistrations()
[Fact]
public void SyntheticBlockHasNullSyntaxAndUnknownLocation()
{
- var syntheticBlock = new Block(null, [], []);
+ var syntheticBlock = new Block("^entry", [], []);
Assert.Null(syntheticBlock.Syntax);
- Assert.Null(syntheticBlock.Label);
+ Assert.Equal("^entry", syntheticBlock.Label);
Assert.Null(syntheticBlock.LabelReference);
Assert.False(syntheticBlock.Location.IsKnown);
}
From 9022650a830a05d5ecf815ee4282eedbe7bd5e98 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 31 Mar 2026 01:08:20 +0000
Subject: [PATCH 4/5] Changes before error encountered
Agent-Logs-Url: https://github.com/jonathanvdc/MLIR.NET/sessions/b8741ebf-34a1-45a3-b6cf-be3beee8e913
Co-authored-by: jonathanvdc <9839946+jonathanvdc@users.noreply.github.com>
---
src/MLIR/Semantics/Block.cs | 26 ++++++++++-----------
src/MLIR/Semantics/BlockReference.cs | 34 ++++++++++++++++++++--------
src/MLIR/Semantics/Operation.cs | 30 +++---------------------
src/MLIR/Semantics/ValueReference.cs | 34 ++++++++++++++++++++--------
4 files changed, 65 insertions(+), 59 deletions(-)
diff --git a/src/MLIR/Semantics/Block.cs b/src/MLIR/Semantics/Block.cs
index e8fb09e..53f9023 100644
--- a/src/MLIR/Semantics/Block.cs
+++ b/src/MLIR/Semantics/Block.cs
@@ -10,7 +10,7 @@ public sealed class Block
{
///
/// Initializes a new instance of the class from a concrete syntax node.
- /// The block label is taken from the syntax node.
+ /// The block label reference is derived from the syntax node's label token.
///
/// The concrete syntax node for the block.
/// The semantic block arguments.
@@ -18,25 +18,23 @@ public sealed class Block
public Block(BlockSyntax syntax, IReadOnlyList arguments, IReadOnlyList operations)
{
Syntax = syntax;
- Label = syntax.Label;
+ LabelReference = new BlockReference(syntax.LabelToken);
Arguments = arguments;
Operations = operations;
- LabelReference = new BlockReference(syntax.LabelToken);
}
///
/// Initializes a new instance of the class as a synthetic block with no corresponding source text.
///
- /// The block label, including the leading ^.
+ /// The semantic reference to the block label.
/// The semantic block arguments.
/// The operations contained in the block.
- public Block(string label, IReadOnlyList arguments, IReadOnlyList operations)
+ public Block(BlockReference labelReference, IReadOnlyList arguments, IReadOnlyList operations)
{
Syntax = null;
- Label = label;
+ LabelReference = labelReference;
Arguments = arguments;
Operations = operations;
- LabelReference = null;
}
///
@@ -44,6 +42,11 @@ public Block(string label, IReadOnlyList arguments, IReadOnlyList
///
public BlockSyntax? Syntax { get; }
+ ///
+ /// Gets the semantic reference to the block label.
+ ///
+ public BlockReference LabelReference { get; }
+
///
/// Gets the semantic block arguments.
///
@@ -57,15 +60,10 @@ public Block(string label, IReadOnlyList arguments, IReadOnlyList
///
/// Gets the block label, including the leading ^.
///
- public string Label { get; }
-
- ///
- /// Gets the typed reference to the block label, or null if this is a synthetic block with no label token.
- ///
- public BlockReference? LabelReference { get; }
+ public string Label => LabelReference.Label;
///
/// Gets the source location of the block label, if known.
///
- public SourceLocation Location => LabelReference?.Location ?? SourceLocation.Unknown;
+ public SourceLocation Location => LabelReference.Location;
}
diff --git a/src/MLIR/Semantics/BlockReference.cs b/src/MLIR/Semantics/BlockReference.cs
index 2b0543d..9761f6d 100644
--- a/src/MLIR/Semantics/BlockReference.cs
+++ b/src/MLIR/Semantics/BlockReference.cs
@@ -5,24 +5,40 @@ namespace MLIR.Semantics;
///
/// Represents a typed reference to a block label in the semantic layer.
///
-///
-/// Initializes a new instance of the struct.
-///
-/// The syntax token for the block label.
-public readonly struct BlockReference(SyntaxToken token)
+public readonly struct BlockReference
{
///
- /// Gets the syntax token for the block label.
+ /// Initializes a new instance of the struct from a syntax token.
///
- public SyntaxToken Token { get; } = token;
+ /// The syntax token for the block label.
+ public BlockReference(SyntaxToken token)
+ {
+ Token = token;
+ Label = token.Text;
+ }
+
+ ///
+ /// Initializes a new instance of the struct for a synthetic reference with no corresponding source token.
+ ///
+ /// The block label text, including the leading ^.
+ public BlockReference(string label)
+ {
+ Token = null;
+ Label = label;
+ }
+
+ ///
+ /// Gets the syntax token for the block label, or null if this is a synthetic reference with no corresponding source token.
+ ///
+ public SyntaxToken? Token { get; }
///
/// Gets the block label text.
///
- public string Label => Token.Text;
+ public string Label { get; }
///
/// Gets the source location of the block label, if known.
///
- public SourceLocation Location => SourceLocation.FromToken(Token);
+ public SourceLocation Location => Token.HasValue ? SourceLocation.FromToken(Token.Value) : SourceLocation.Unknown;
}
diff --git a/src/MLIR/Semantics/Operation.cs b/src/MLIR/Semantics/Operation.cs
index 00bb8fe..83a9e96 100644
--- a/src/MLIR/Semantics/Operation.cs
+++ b/src/MLIR/Semantics/Operation.cs
@@ -2,7 +2,6 @@ namespace MLIR.Semantics;
using System;
using System.Collections.Generic;
-using MLIR.Construction;
using MLIR.Dialects;
using MLIR.Syntax;
@@ -144,34 +143,11 @@ public NamedAttribute GetAttribute(string name)
}
///
- /// Gets the operation body as a generic operation body syntax node.
+ /// Gets the operation body as a generic operation body syntax node, or null if the operation body is not in generic form.
///
- public GenericOperationBodySyntax GetGenericBody()
+ public GenericOperationBodySyntax? GetGenericBody()
{
- if (Syntax?.Body is GenericOperationBodySyntax genericBody)
- {
- return genericBody;
- }
-
- // TODO: preserve tokens, avoid stringifying and reparsing type signatures, etc.
- // For synthetic regions and attributes (null Syntax), placeholder values are used:
- // - Regions without syntax produce an empty region body; AssemblySyntaxBuilder replaces them with fully built syntax.
- // - Attributes without syntax produce an empty value string, as there is no source text to round-trip.
- return (GenericOperationBodySyntax)Factory.Op(
- Name,
- Results,
- Operands,
- Successors,
- Regions.Select(r => r.Syntax ?? new RegionSyntax([])).ToList(),
- Attributes.Select(a =>
- {
- var attrText = a.Value.Syntax != null && a.Value.Syntax.TryGetRawText(out var rawText)
- ? rawText!.Text
- : string.Empty;
- return Factory.Attr(a.Name, attrText);
- }).ToList(),
- TypeSignatureReference != null ? TypeSignatureReference.Syntax : null
- ).Body;
+ return Syntax?.Body as GenericOperationBodySyntax;
}
private static IReadOnlyList GetNames(IReadOnlyList values)
diff --git a/src/MLIR/Semantics/ValueReference.cs b/src/MLIR/Semantics/ValueReference.cs
index 3ea0e68..f462e83 100644
--- a/src/MLIR/Semantics/ValueReference.cs
+++ b/src/MLIR/Semantics/ValueReference.cs
@@ -5,24 +5,40 @@ namespace MLIR.Semantics;
///
/// Represents a typed reference to an SSA value in the semantic layer.
///
-///
-/// Initializes a new instance of the struct.
-///
-/// The syntax token for the SSA value.
-public readonly struct ValueReference(SyntaxToken token)
+public readonly struct ValueReference
{
///
- /// Gets the syntax token for the SSA value.
+ /// Initializes a new instance of the struct from a syntax token.
///
- public SyntaxToken Token { get; } = token;
+ /// The syntax token for the SSA value.
+ public ValueReference(SyntaxToken token)
+ {
+ Token = token;
+ Name = token.Text;
+ }
+
+ ///
+ /// Initializes a new instance of the struct for a synthetic value with no corresponding source token.
+ ///
+ /// The SSA value name.
+ public ValueReference(string name)
+ {
+ Token = null;
+ Name = name;
+ }
+
+ ///
+ /// Gets the syntax token for the SSA value, or null if this is a synthetic reference with no corresponding source token.
+ ///
+ public SyntaxToken? Token { get; }
///
/// Gets the SSA value name.
///
- public string Name => Token.Text;
+ public string Name { get; }
///
/// Gets the source location of the SSA value, if known.
///
- public SourceLocation Location => SourceLocation.FromToken(Token);
+ public SourceLocation Location => Token.HasValue ? SourceLocation.FromToken(Token.Value) : SourceLocation.Unknown;
}
From 433e862f3f40c87bdd429282e575af99d6e85632 Mon Sep 17 00:00:00 2001
From: jonathanvdc
Date: Mon, 30 Mar 2026 21:34:19 -0400
Subject: [PATCH 5/5] fix: update UnknownAttributeValue constructor to require
non-null syntax; enhance attribute building logic in AssemblySyntaxBuilder
---
src/MLIR/Semantics/UnknownAttributeValue.cs | 2 +-
src/MLIR/Transforms/AssemblySyntaxBuilder.cs | 34 ++++++++++++++++++--
tests/MLIR.Tests/SemanticTests.cs | 3 +-
3 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/src/MLIR/Semantics/UnknownAttributeValue.cs b/src/MLIR/Semantics/UnknownAttributeValue.cs
index 647fe73..fcfa498 100644
--- a/src/MLIR/Semantics/UnknownAttributeValue.cs
+++ b/src/MLIR/Semantics/UnknownAttributeValue.cs
@@ -11,7 +11,7 @@ public sealed class UnknownAttributeValue : AttributeValue
///
/// Initializes a new instance of the class.
///
- public UnknownAttributeValue(AttributeValueSyntax? syntax, string? name, AttributeDefinition? definition, SourceLocation location)
+ public UnknownAttributeValue(AttributeValueSyntax syntax, string? name, AttributeDefinition? definition, SourceLocation location)
: base(syntax, name, definition, location)
{
}
diff --git a/src/MLIR/Transforms/AssemblySyntaxBuilder.cs b/src/MLIR/Transforms/AssemblySyntaxBuilder.cs
index f785ad7..6ba810c 100644
--- a/src/MLIR/Transforms/AssemblySyntaxBuilder.cs
+++ b/src/MLIR/Transforms/AssemblySyntaxBuilder.cs
@@ -110,12 +110,42 @@ private GenericOperationBodySyntax GetGenericBody(Operation operation)
operation.Results,
operation.Operands,
operation.Successors,
- operation.Regions.Select(r => r.Syntax).ToList(),
- operation.Attributes.Select(a => Factory.Attr(a.Name, a.Value.Syntax.Text)).ToList(),
+ operation.Regions.Select(BuildRegion).ToList(),
+ operation.Attributes.Select(BuildNamedAttribute).ToList(),
operation.TypeSignatureReference != null ? operation.TypeSignatureReference.Syntax : null
).Body;
}
+ public NamedAttributeSyntax BuildNamedAttribute(NamedAttribute attribute)
+ {
+ if (attribute.Syntax != null)
+ {
+ return attribute.Syntax;
+ }
+
+ // Synthesize an attribute syntax for a synthetic attribute with no source syntax.
+ return new NamedAttributeSyntax(
+ new SyntaxToken(attribute.Name),
+ new SyntaxToken("="),
+ BuildAttributeValue(attribute.Value));
+ }
+
+ public AttributeValueSyntax BuildAttributeValue(AttributeValue attributeValue)
+ {
+ if (attributeValue.Syntax != null)
+ {
+ return attributeValue.Syntax;
+ }
+
+ if (attributeValue is UnknownAttributeValue unknownAttributeValue)
+ {
+ // For unknown attribute values, we want to preserve the original syntax if possible, even if it was not recognized as a valid attribute value.
+ return unknownAttributeValue.Syntax!;
+ }
+
+ throw new InvalidOperationException($"Cannot build syntax for unrecognized attribute value of type {attributeValue.GetType().FullName}.");
+ }
+
public RegionSyntax BuildRegion(Region region)
{
var blocks = new List(region.Blocks.Count);
diff --git a/tests/MLIR.Tests/SemanticTests.cs b/tests/MLIR.Tests/SemanticTests.cs
index a3ba107..2ff3449 100644
--- a/tests/MLIR.Tests/SemanticTests.cs
+++ b/tests/MLIR.Tests/SemanticTests.cs
@@ -981,11 +981,10 @@ public void RegistryRejectsDuplicateAttributeAndTypeRegistrations()
[Fact]
public void SyntheticBlockHasNullSyntaxAndUnknownLocation()
{
- var syntheticBlock = new Block("^entry", [], []);
+ var syntheticBlock = new Block(new BlockReference("^entry"), [], []);
Assert.Null(syntheticBlock.Syntax);
Assert.Equal("^entry", syntheticBlock.Label);
- Assert.Null(syntheticBlock.LabelReference);
Assert.False(syntheticBlock.Location.IsKnown);
}