diff --git a/mlir/docs/Interfaces.md b/mlir/docs/Interfaces.md --- a/mlir/docs/Interfaces.md +++ b/mlir/docs/Interfaces.md @@ -379,6 +379,9 @@ * C++ Class Name (Provided via template parameter) - The name of the C++ interface class. +* Interface Base Classes + - A set of interfaces that the interface class should derived from. See + [Interface Inheritance](#interface-inheritance) below for more details. * Description (`description`) - A string description of the interface, its invariants, example usages, etc. @@ -415,6 +418,8 @@ - The structure of this code block corresponds 1-1 with the structure of a [`Trait::verifyTrait`](Traits.md) method. +##### Interface Methods + There are two types of methods that can be used with an interface, `InterfaceMethod` and `StaticInterfaceMethod`. They are both comprised of the same core components, with the distinction that `StaticInterfaceMethod` models a @@ -634,6 +639,43 @@ [DeclareOpInterfaceMethods]> { ... } ``` +##### Interface Inheritance + +Interfaces also support a limited form of inheritance, which allows for +building upon pre-existing interfaces in a way similar to that of classes in +programming languages like C++. This more easily allows for building modular +interfaces, without suffering from the pain of lots of explicit casting. To +enable inheritance, an interface simply needs to provide the desired set of +base classes in its definition. For example: + +```tablegen +def MyBaseInterface : OpInterface<"MyBaseInterface"> { + ... +} + +def MyInterface : OpInterface<"MyInterface", [MyBaseInterface]> { + ... +} +``` + +This will result in `MyInterface` inheriting various components from +`MyBaseInterface`, namely its interface methods and extra class declarations. +`MyInterface` will also implicitly inherit any base classes defined on +`MyBaseInterface` as well. It's important to note, however, that there is only +ever one instance of each interface for a given attribute, operation, or type. +"Inheriting" an interface simply forwards to the implementation defined on +the base interface, and instills a few other utilities, like implicit conversion +operators, to provide an API similar to traditional inheritance (such as that in +C++). This produces a simpler system overall, and also removes any potential +problems surrounding "diamond inheritance". + +When adding an interface with inheritance to an attribute, operation, or type, +all of the base interfaces are also implicitly added as well. The user may still +manually specify the base interfaces if they desire, such as for use with the +`DeclareInterfaceMethods` helper classes. + +##### Generation + Once the interfaces have been defined, the C++ header and source files can be generated using the `--gen--interface-decls` and `--gen--interface-defs` options with mlir-tblgen. Note that when diff --git a/mlir/include/mlir/IR/OpBase.td b/mlir/include/mlir/IR/OpBase.td --- a/mlir/include/mlir/IR/OpBase.td +++ b/mlir/include/mlir/IR/OpBase.td @@ -1948,7 +1948,7 @@ def NoRegionArguments : NativeOpTrait<"NoRegionArguments">, StructuralOpTrait; //===----------------------------------------------------------------------===// -// OpInterface definitions +// Interface definitions //===----------------------------------------------------------------------===// // Marker used to identify the argument list for an op or interface method. @@ -2026,7 +2026,7 @@ defaultImplementation>; // Interface represents a base interface. -class Interface { +class Interface baseInterfacesArg> { // A human-readable description of what this interface does. string description = ""; @@ -2055,28 +2055,36 @@ // implements the interface if some dynamic characteristic holds. `$_self` // may be used to refer to an instance of this interface being checked. code extraClassOf = ""; + + // An optional set of base interfaces that this interface + // "derives" from. + list baseInterfaces = baseInterfacesArg; } // AttrInterface represents an interface registered to an attribute. -class AttrInterface : Interface, InterfaceTrait, - Attr()">, - name # " instance"> -{ +class AttrInterface baseInterfaces = []> + : Interface, InterfaceTrait, + Attr()">, + name # " instance" + > { let storageType = !if(!empty(cppNamespace), "", cppNamespace # "::") # name; let returnType = storageType; let convertFromStorage = "$_self"; } // OpInterface represents an interface registered to an operation. -class OpInterface : Interface, OpInterfaceTrait; +class OpInterface baseInterfaces = []> + : Interface, OpInterfaceTrait; // TypeInterface represents an interface registered to a type. -class TypeInterface : Interface, InterfaceTrait, - Type()">, +class TypeInterface baseInterfaces = []> + : Interface, InterfaceTrait, + Type()">, name # " instance", - !if(!empty(cppNamespace),"", cppNamespace # "::") # name>; + !if(!empty(cppNamespace),"", cppNamespace # "::") # name + >; // Whether to declare the interface methods in the user entity's header. This // class simply wraps an Interface but is used to indicate that the method @@ -2092,29 +2100,32 @@ class DeclareAttrInterfaceMethods overridenMethods = []> : DeclareInterfaceMethods, - AttrInterface { + AttrInterface { let description = interface.description; let cppInterfaceName = interface.cppInterfaceName; let cppNamespace = interface.cppNamespace; let methods = interface.methods; + let baseInterfaces = interface.baseInterfaces; } class DeclareOpInterfaceMethods overridenMethods = []> : DeclareInterfaceMethods, - OpInterface { + OpInterface { let description = interface.description; let cppInterfaceName = interface.cppInterfaceName; let cppNamespace = interface.cppNamespace; let methods = interface.methods; + let baseInterfaces = interface.baseInterfaces; } class DeclareTypeInterfaceMethods overridenMethods = []> : DeclareInterfaceMethods, - TypeInterface { + TypeInterface { let description = interface.description; let cppInterfaceName = interface.cppInterfaceName; let cppNamespace = interface.cppNamespace; let methods = interface.methods; + let baseInterfaces = interface.baseInterfaces; } //===----------------------------------------------------------------------===// diff --git a/mlir/include/mlir/IR/OperationSupport.h b/mlir/include/mlir/IR/OperationSupport.h --- a/mlir/include/mlir/IR/OperationSupport.h +++ b/mlir/include/mlir/IR/OperationSupport.h @@ -87,8 +87,7 @@ /// operation. If the operation is not registered, some of these components /// may not be populated. struct Impl { - Impl(StringAttr name) - : name(name), dialect(nullptr), interfaceMap(std::nullopt) {} + Impl(StringAttr name) : name(name), dialect(nullptr) {} /// The name of the operation. StringAttr name; @@ -345,7 +344,7 @@ /// for the concrete operation. template void attachInterface() { - impl->interfaceMap.insert(); + impl->interfaceMap.insertModels(); } /// Returns true if the operation has a particular trait. diff --git a/mlir/include/mlir/IR/StorageUniquerSupport.h b/mlir/include/mlir/IR/StorageUniquerSupport.h --- a/mlir/include/mlir/IR/StorageUniquerSupport.h +++ b/mlir/include/mlir/IR/StorageUniquerSupport.h @@ -140,7 +140,7 @@ "that is not itself registered."); (checkInterfaceTarget(), ...); - abstract->interfaceMap.template insert(); + abstract->interfaceMap.template insertModels(); } /// Get or create a new ConcreteT instance within the ctx. This diff --git a/mlir/include/mlir/Support/InterfaceSupport.h b/mlir/include/mlir/Support/InterfaceSupport.h --- a/mlir/include/mlir/Support/InterfaceSupport.h +++ b/mlir/include/mlir/Support/InterfaceSupport.h @@ -111,8 +111,8 @@ } /// Constructor for a known concept. - Interface(ValueT t, Concept *conceptImpl) - : BaseType(t), conceptImpl(conceptImpl) { + Interface(ValueT t, const Concept *conceptImpl) + : BaseType(t), conceptImpl(const_cast(conceptImpl)) { assert(!t || ConcreteType::getInterfaceFor(t) == conceptImpl); } @@ -152,25 +152,6 @@ template