diff --git a/src/MLIR/Semantics/AttributeValue.cs b/src/MLIR/Semantics/AttributeValue.cs
index 65b6c7c..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, AttributeDefinition
}
///
- /// Gets the raw syntax text for the attribute value.
+ /// 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 8da58a9..53f9023 100644
--- a/src/MLIR/Semantics/Block.cs
+++ b/src/MLIR/Semantics/Block.cs
@@ -6,38 +6,61 @@ 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 from a concrete syntax node.
+ /// The block label reference is derived from the syntax node's label token.
///
- public BlockSyntax Syntax { get; } = syntax;
+ /// 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)
+ {
+ Syntax = syntax;
+ LabelReference = new BlockReference(syntax.LabelToken);
+ Arguments = arguments;
+ Operations = operations;
+ }
///
- /// Gets the semantic block arguments.
+ /// Initializes a new instance of the class as a synthetic block with no corresponding source text.
///
- public IReadOnlyList Arguments { get; } = arguments;
+ /// The semantic reference to the block label.
+ /// The semantic block arguments.
+ /// The operations contained in the block.
+ public Block(BlockReference labelReference, IReadOnlyList arguments, IReadOnlyList operations)
+ {
+ Syntax = null;
+ LabelReference = labelReference;
+ Arguments = arguments;
+ Operations = operations;
+ }
///
- /// Gets the operations contained in the block.
+ /// Gets the concrete syntax node for the block, or null if this is a synthetic block with no corresponding source text.
///
- public IReadOnlyList Operations { get; } = operations;
+ public BlockSyntax? Syntax { get; }
///
- /// Gets the block label, including the leading ^.
+ /// Gets the semantic reference to the block label.
+ ///
+ public BlockReference LabelReference { get; }
+
+ ///
+ /// Gets the semantic block arguments.
+ ///
+ public IReadOnlyList Arguments { get; }
+
+ ///
+ /// Gets the operations contained in the block.
///
- public string Label => Syntax.Label;
+ public IReadOnlyList Operations { get; }
///
- /// Gets the typed reference to the block label.
+ /// Gets the block label, including the leading ^.
///
- public BlockReference LabelReference { get; } = new BlockReference(syntax.LabelToken);
+ public string Label => LabelReference.Label;
///
/// Gets the source location of the block label, if known.
diff --git a/src/MLIR/Semantics/BlockReference.cs b/src/MLIR/Semantics/BlockReference.cs
index 62af382..9761f6d 100644
--- a/src/MLIR/Semantics/BlockReference.cs
+++ b/src/MLIR/Semantics/BlockReference.cs
@@ -18,9 +18,9 @@ public BlockReference(SyntaxToken token)
}
///
- /// Initializes a new instance of the struct from a label text.
+ /// Initializes a new instance of the struct for a synthetic reference with no corresponding source token.
///
- /// The block label text.
+ /// The block label text, including the leading ^.
public BlockReference(string label)
{
Token = null;
@@ -28,7 +28,7 @@ public BlockReference(string label)
}
///
- /// Gets the syntax token for the block 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; }
diff --git a/src/MLIR/Semantics/Operation.cs b/src/MLIR/Semantics/Operation.cs
index d96eb23..22c07ff 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;
@@ -15,7 +14,7 @@ public abstract class Operation
/// Initializes a new instance of the class.
///
protected Operation(
- OperationSyntax syntax,
+ OperationSyntax? syntax,
string name,
OperationDefinition? definition)
{
@@ -25,9 +24,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 +74,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 +108,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.
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/Semantics/UnknownAttributeValue.cs b/src/MLIR/Semantics/UnknownAttributeValue.cs
index f066907..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(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/Semantics/ValueReference.cs b/src/MLIR/Semantics/ValueReference.cs
index 708b71b..f462e83 100644
--- a/src/MLIR/Semantics/ValueReference.cs
+++ b/src/MLIR/Semantics/ValueReference.cs
@@ -18,7 +18,7 @@ public ValueReference(SyntaxToken token)
}
///
- /// Initializes a new instance of the struct from a value name.
+ /// Initializes a new instance of the struct for a synthetic value with no corresponding source token.
///
/// The SSA value name.
public ValueReference(string name)
@@ -28,7 +28,7 @@ public ValueReference(string name)
}
///
- /// Gets the syntax token for the SSA value.
+ /// 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; }
diff --git a/src/MLIR/Transforms/AssemblySyntaxBuilder.cs b/src/MLIR/Transforms/AssemblySyntaxBuilder.cs
index c700d83..6ba810c 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);
}
@@ -84,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);
@@ -98,7 +154,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)
@@ -109,11 +168,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, [], operations);
}
}
}
diff --git a/tests/MLIR.Tests/SemanticTests.cs b/tests/MLIR.Tests/SemanticTests.cs
index 69790ff..2ff3449 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(
@@ -214,12 +237,13 @@ 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);
- 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 +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.");
}
@@ -344,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);
}
@@ -608,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]
@@ -625,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]
@@ -756,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]
@@ -953,4 +977,44 @@ public void RegistryRejectsDuplicateAttributeAndTypeRegistrations()
Assert.Contains("already registered", attributeException.Message);
Assert.Contains("already registered", typeException.Message);
}
+
+ [Fact]
+ public void SyntheticBlockHasNullSyntaxAndUnknownLocation()
+ {
+ var syntheticBlock = new Block(new BlockReference("^entry"), [], []);
+
+ Assert.Null(syntheticBlock.Syntax);
+ Assert.Equal("^entry", syntheticBlock.Label);
+ 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);
+ }
}