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); }