Skip to content

Commit 466c5a9

Browse files
authored
Add first-class runtime type constraint definitions and separate type-constraint registration (#144)
1 parent f93e902 commit 466c5a9

6 files changed

Lines changed: 116 additions & 9 deletions

File tree

src/MLIR/Dialects/Dialect.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ namespace MLIR.Dialects;
1515
/// <param name="types">The type definitions exported by the dialect.</param>
1616
/// <param name="attributeConstraints">The context-directed attribute constraint definitions exported by the dialect.</param>
1717
/// <param name="dependencies">Dialect registrations that must be registered before this one.</param>
18+
/// <param name="typeConstraints">The context-directed type constraint definitions exported by the dialect.</param>
1819
public sealed class Dialect(
1920
string name,
2021
IReadOnlyList<OperationDefinition> operations,
2122
IReadOnlyList<AttributeDefinition>? attributes = null,
2223
IReadOnlyList<TypeDefinition>? types = null,
2324
IReadOnlyList<AttributeConstraintDefinition>? attributeConstraints = null,
24-
IReadOnlyList<Func<Dialect>>? dependencies = null)
25+
IReadOnlyList<Func<Dialect>>? dependencies = null,
26+
IReadOnlyList<TypeConstraintDefinition>? typeConstraints = null)
2527
{
2628
/// <summary>
2729
/// Creates a dialect from a fluent builder callback.
@@ -62,6 +64,11 @@ public static Dialect Create(string name, Action<DialectBuilder> configure, para
6264
/// </summary>
6365
public IReadOnlyList<TypeDefinition> Types { get; } = types ?? EmptyTypes;
6466

67+
/// <summary>
68+
/// Gets the context-directed type constraint definitions exported by the dialect.
69+
/// </summary>
70+
public IReadOnlyList<TypeConstraintDefinition> TypeConstraints { get; } = typeConstraints ?? EmptyTypeConstraints;
71+
6572
/// <summary>
6673
/// Gets the dialect registrations that must be registered before this dialect.
6774
/// </summary>
@@ -70,5 +77,6 @@ public static Dialect Create(string name, Action<DialectBuilder> configure, para
7077
private static readonly IReadOnlyList<AttributeDefinition> EmptyAttributes = new AttributeDefinition[0];
7178
private static readonly IReadOnlyList<AttributeConstraintDefinition> EmptyAttributeConstraints = new AttributeConstraintDefinition[0];
7279
private static readonly IReadOnlyList<TypeDefinition> EmptyTypes = new TypeDefinition[0];
80+
private static readonly IReadOnlyList<TypeConstraintDefinition> EmptyTypeConstraints = new TypeConstraintDefinition[0];
7381
private static readonly IReadOnlyList<Func<Dialect>> EmptyDependencies = new Func<Dialect>[0];
7482
}

src/MLIR/Dialects/DialectBuilder.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public sealed class DialectBuilder
1313
private readonly List<AttributeDefinition> attributes = new List<AttributeDefinition>();
1414
private readonly List<AttributeConstraintDefinition> attributeConstraints = new List<AttributeConstraintDefinition>();
1515
private readonly List<TypeDefinition> types = new List<TypeDefinition>();
16+
private readonly List<TypeConstraintDefinition> typeConstraints = new List<TypeConstraintDefinition>();
1617
private readonly List<Func<Dialect>> dependencies = new List<Func<Dialect>>();
1718

1819
/// <summary>
@@ -71,6 +72,15 @@ public DialectBuilder AddType(TypeDefinition type)
7172
return this;
7273
}
7374

75+
/// <summary>
76+
/// Adds a type constraint definition.
77+
/// </summary>
78+
public DialectBuilder AddTypeConstraint(TypeConstraintDefinition typeConstraint)
79+
{
80+
typeConstraints.Add(typeConstraint);
81+
return this;
82+
}
83+
7484
/// <summary>
7585
/// Adds a dialect registration dependency.
7686
/// </summary>
@@ -85,6 +95,6 @@ public DialectBuilder AddDependency(Func<Dialect> dependency)
8595
/// </summary>
8696
public Dialect Build(IReadOnlyList<Func<Dialect>>? dependencies = null)
8797
{
88-
return new Dialect(name, operations, attributes, types, attributeConstraints, dependencies ?? this.dependencies);
98+
return new Dialect(name, operations, attributes, types, attributeConstraints, dependencies ?? this.dependencies, typeConstraints);
8999
}
90100
}

src/MLIR/Dialects/DialectRegistry.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public sealed class DialectRegistry
1414
private readonly Dictionary<string, AttributeDefinition> attributesByParserName = new Dictionary<string, AttributeDefinition>(StringComparer.Ordinal);
1515
private readonly Dictionary<string, AttributeConstraintDefinition> attributeConstraintsByName = new Dictionary<string, AttributeConstraintDefinition>(StringComparer.Ordinal);
1616
private readonly Dictionary<string, TypeDefinition> typesByName = new Dictionary<string, TypeDefinition>(StringComparer.Ordinal);
17+
private readonly Dictionary<string, TypeConstraintDefinition> typeConstraintsByName = new Dictionary<string, TypeConstraintDefinition>(StringComparer.Ordinal);
1718

1819
/// <summary>
1920
/// Gets the dialects currently registered in the registry.
@@ -90,6 +91,25 @@ private void RegisterDialect(Dialect dialect, bool isDependency)
9091
{
9192
throw new ArgumentException($"The type '{type.Name}' is already registered.", nameof(dialect));
9293
}
94+
95+
if (typeConstraintsByName.ContainsKey(type.Name))
96+
{
97+
throw new ArgumentException($"The type constraint '{type.Name}' is already registered.", nameof(dialect));
98+
}
99+
}
100+
101+
foreach (var typeConstraint in dialect.TypeConstraints)
102+
{
103+
var constraintName = typeConstraint.Name;
104+
if (constraintName != null && typeConstraintsByName.ContainsKey(constraintName))
105+
{
106+
throw new ArgumentException($"The type constraint '{constraintName}' is already registered.", nameof(dialect));
107+
}
108+
109+
if (constraintName != null && typesByName.ContainsKey(constraintName))
110+
{
111+
throw new ArgumentException($"The type '{constraintName}' is already registered.", nameof(dialect));
112+
}
93113
}
94114

95115
dialectsByName.Add(dialect.Name, dialect);
@@ -120,6 +140,16 @@ private void RegisterDialect(Dialect dialect, bool isDependency)
120140
foreach (var type in dialect.Types)
121141
{
122142
typesByName.Add(type.Name, type);
143+
typeConstraintsByName.Add(type.Name, type);
144+
}
145+
146+
foreach (var typeConstraint in dialect.TypeConstraints)
147+
{
148+
var constraintName = typeConstraint.Name;
149+
if (constraintName != null)
150+
{
151+
typeConstraintsByName.Add(constraintName, typeConstraint);
152+
}
123153
}
124154
}
125155

@@ -201,4 +231,12 @@ public bool TryGetType(string name, out TypeDefinition type)
201231
{
202232
return typesByName.TryGetValue(name, out type!);
203233
}
234+
235+
/// <summary>
236+
/// Tries to resolve a type constraint definition by name.
237+
/// </summary>
238+
public bool TryResolveTypeConstraint(string name, out TypeConstraintDefinition typeConstraint)
239+
{
240+
return typeConstraintsByName.TryGetValue(name, out typeConstraint!);
241+
}
204242
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
namespace MLIR.Dialects;
2+
3+
using MLIR.Semantics;
4+
5+
/// <summary>
6+
/// Describes a context-directed type constraint (<c>Type</c> in ODS) that can parse and bind type references.
7+
/// </summary>
8+
/// <remarks>
9+
/// Initializes a new instance of the <see cref="TypeConstraintDefinition"/> class.
10+
/// </remarks>
11+
/// <param name="name">The logical constraint name, if one is known.</param>
12+
/// <param name="assemblyFormat">The optional custom assembly interpretation hook.</param>
13+
public class TypeConstraintDefinition(
14+
string? name = null,
15+
ITypeAssemblyFormat? assemblyFormat = null)
16+
{
17+
/// <summary>
18+
/// Gets the logical constraint name, if one is known.
19+
/// </summary>
20+
public string? Name { get; } = name;
21+
22+
/// <summary>
23+
/// Gets the custom assembly interpretation hook, if one is registered.
24+
/// </summary>
25+
public ITypeAssemblyFormat? AssemblyFormat { get; } = assemblyFormat;
26+
}

src/MLIR/Dialects/TypeDefinition.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@ namespace MLIR.Dialects;
33
using MLIR.Semantics;
44

55
/// <summary>
6-
/// Describes a dialect-defined type.
6+
/// Describes a concrete dialect-defined type (<c>TypeDef</c> in ODS).
77
/// </summary>
88
/// <remarks>
9+
/// Concrete type definitions are also valid type constraints, so this class derives from
10+
/// <see cref="TypeConstraintDefinition"/>.
11+
/// </remarks>
12+
/// <remarks>
913
/// Initializes a new instance of the <see cref="TypeDefinition"/> class.
1014
/// </remarks>
1115
/// <param name="name">The canonical type name.</param>
@@ -15,16 +19,16 @@ public sealed class TypeDefinition(
1519
string name,
1620
ITypeAssemblyFormat? assemblyFormat = null,
1721
System.Func<TypeReferenceConstructionContext, TypeReference>? factory = null)
22+
: TypeConstraintDefinition(name, assemblyFormat)
1823
{
1924
/// <summary>
2025
/// Gets the canonical type name.
2126
/// </summary>
22-
public string Name { get; } = name;
23-
24-
/// <summary>
25-
/// Gets the custom assembly interpretation hook, if one is registered.
26-
/// </summary>
27-
public ITypeAssemblyFormat? AssemblyFormat { get; } = assemblyFormat;
27+
/// <remarks>
28+
/// This narrows <see cref="TypeConstraintDefinition.Name"/> back to a non-null contract for
29+
/// concrete <c>TypeDef</c> registrations.
30+
/// </remarks>
31+
public new string Name { get; } = name;
2832

2933
/// <summary>
3034
/// Gets the typed type-reference factory.

tests/MLIR.Tests/SemanticRegistryTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,25 @@ public void RegistryRejectsDuplicateAttributeAndTypeRegistrations()
3131
Assert.Contains("already registered", attributeException.Message);
3232
Assert.Contains("already registered", typeException.Message);
3333
}
34+
35+
[Fact]
36+
public void RegistryResolvesStandaloneAndConcreteTypeConstraints()
37+
{
38+
var registry = new DialectRegistry();
39+
var standaloneConstraint = new TypeConstraintDefinition("AnyType");
40+
var concreteType = new TypeDefinition("i32");
41+
registry.RegisterDialect(new Dialect("builtin", [], [], [concreteType], [], [], [standaloneConstraint]));
42+
43+
Assert.True(registry.TryResolveTypeConstraint("AnyType", out var resolvedStandaloneConstraint));
44+
Assert.Same(standaloneConstraint, resolvedStandaloneConstraint);
45+
46+
Assert.True(registry.TryResolveTypeConstraint("i32", out var resolvedConcreteConstraint));
47+
Assert.Same(concreteType, resolvedConcreteConstraint);
48+
Assert.IsType<TypeDefinition>(resolvedConcreteConstraint);
49+
50+
Assert.True(registry.TryGetType("i32", out var resolvedConcreteType));
51+
Assert.Same(concreteType, resolvedConcreteType);
52+
53+
Assert.False(registry.TryGetType("AnyType", out _));
54+
}
3455
}

0 commit comments

Comments
 (0)