xref: /llvm-project/mlir/unittests/IR/InterfaceAttachmentTest.cpp (revision c24ce324d56328e4b91c8797ea4935545084303e)
19b2a1bcfSAlex Zinenko //===- InterfaceAttachmentTest.cpp - Test attaching interfaces ------------===//
29b2a1bcfSAlex Zinenko //
39b2a1bcfSAlex Zinenko // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
49b2a1bcfSAlex Zinenko // See https://llvm.org/LICENSE.txt for license information.
59b2a1bcfSAlex Zinenko // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
69b2a1bcfSAlex Zinenko //
79b2a1bcfSAlex Zinenko //===----------------------------------------------------------------------===//
89b2a1bcfSAlex Zinenko //
99b2a1bcfSAlex Zinenko // This implements the tests for attaching interfaces to attributes and types
109b2a1bcfSAlex Zinenko // without having to specify them on the attribute or type class directly.
119b2a1bcfSAlex Zinenko //
129b2a1bcfSAlex Zinenko //===----------------------------------------------------------------------===//
139b2a1bcfSAlex Zinenko 
149b2a1bcfSAlex Zinenko #include "mlir/IR/BuiltinAttributes.h"
15d7e89121SAlex Zinenko #include "mlir/IR/BuiltinDialect.h"
1623cdf7b6SAlex Zinenko #include "mlir/IR/BuiltinOps.h"
179b2a1bcfSAlex Zinenko #include "mlir/IR/BuiltinTypes.h"
189b2a1bcfSAlex Zinenko #include "gtest/gtest.h"
199b2a1bcfSAlex Zinenko 
209b2a1bcfSAlex Zinenko #include "../../test/lib/Dialect/Test/TestAttributes.h"
2123cdf7b6SAlex Zinenko #include "../../test/lib/Dialect/Test/TestDialect.h"
22e95e94adSJeff Niu #include "../../test/lib/Dialect/Test/TestOps.h"
239b2a1bcfSAlex Zinenko #include "../../test/lib/Dialect/Test/TestTypes.h"
24db79f4a2SMehdi Amini #include "mlir/IR/OwningOpRef.h"
259b2a1bcfSAlex Zinenko 
269b2a1bcfSAlex Zinenko using namespace mlir;
277776b19eSStephen Neuendorffer using namespace test;
289b2a1bcfSAlex Zinenko 
299b2a1bcfSAlex Zinenko namespace {
309b2a1bcfSAlex Zinenko 
319b2a1bcfSAlex Zinenko /// External interface model for the integer type. Only provides non-default
329b2a1bcfSAlex Zinenko /// methods.
339b2a1bcfSAlex Zinenko struct Model
349b2a1bcfSAlex Zinenko     : public TestExternalTypeInterface::ExternalModel<Model, IntegerType> {
359b2a1bcfSAlex Zinenko   unsigned getBitwidthPlusArg(Type type, unsigned arg) const {
369b2a1bcfSAlex Zinenko     return type.getIntOrFloatBitWidth() + arg;
379b2a1bcfSAlex Zinenko   }
389b2a1bcfSAlex Zinenko 
399b2a1bcfSAlex Zinenko   static unsigned staticGetSomeValuePlusArg(unsigned arg) { return 42 + arg; }
409b2a1bcfSAlex Zinenko };
419b2a1bcfSAlex Zinenko 
429b2a1bcfSAlex Zinenko /// External interface model for the float type. Provides non-deafult and
439b2a1bcfSAlex Zinenko /// overrides default methods.
449b2a1bcfSAlex Zinenko struct OverridingModel
459b2a1bcfSAlex Zinenko     : public TestExternalTypeInterface::ExternalModel<OverridingModel,
46*c24ce324SMatthias Springer                                                       Float32Type> {
479b2a1bcfSAlex Zinenko   unsigned getBitwidthPlusArg(Type type, unsigned arg) const {
489b2a1bcfSAlex Zinenko     return type.getIntOrFloatBitWidth() + arg;
499b2a1bcfSAlex Zinenko   }
509b2a1bcfSAlex Zinenko 
519b2a1bcfSAlex Zinenko   static unsigned staticGetSomeValuePlusArg(unsigned arg) { return 42 + arg; }
529b2a1bcfSAlex Zinenko 
539b2a1bcfSAlex Zinenko   unsigned getBitwidthPlusDoubleArgument(Type type, unsigned arg) const {
549b2a1bcfSAlex Zinenko     return 128;
559b2a1bcfSAlex Zinenko   }
569b2a1bcfSAlex Zinenko 
579b2a1bcfSAlex Zinenko   static unsigned staticGetArgument(unsigned arg) { return 420; }
589b2a1bcfSAlex Zinenko };
599b2a1bcfSAlex Zinenko 
609b2a1bcfSAlex Zinenko TEST(InterfaceAttachment, Type) {
619b2a1bcfSAlex Zinenko   MLIRContext context;
629b2a1bcfSAlex Zinenko 
639b2a1bcfSAlex Zinenko   // Check that the type has no interface.
649b2a1bcfSAlex Zinenko   IntegerType i8 = IntegerType::get(&context, 8);
655550c821STres Popp   ASSERT_FALSE(isa<TestExternalTypeInterface>(i8));
669b2a1bcfSAlex Zinenko 
679b2a1bcfSAlex Zinenko   // Attach an interface and check that the type now has the interface.
689b2a1bcfSAlex Zinenko   IntegerType::attachInterface<Model>(context);
695550c821STres Popp   TestExternalTypeInterface iface = dyn_cast<TestExternalTypeInterface>(i8);
709b2a1bcfSAlex Zinenko   ASSERT_TRUE(iface != nullptr);
719b2a1bcfSAlex Zinenko   EXPECT_EQ(iface.getBitwidthPlusArg(10), 18u);
729b2a1bcfSAlex Zinenko   EXPECT_EQ(iface.staticGetSomeValuePlusArg(0), 42u);
739b2a1bcfSAlex Zinenko   EXPECT_EQ(iface.getBitwidthPlusDoubleArgument(2), 12u);
749b2a1bcfSAlex Zinenko   EXPECT_EQ(iface.staticGetArgument(17), 17u);
759b2a1bcfSAlex Zinenko 
769b2a1bcfSAlex Zinenko   // Same, but with the default implementation overridden.
779b2a1bcfSAlex Zinenko   FloatType flt = Float32Type::get(&context);
785550c821STres Popp   ASSERT_FALSE(isa<TestExternalTypeInterface>(flt));
799b2a1bcfSAlex Zinenko   Float32Type::attachInterface<OverridingModel>(context);
805550c821STres Popp   iface = dyn_cast<TestExternalTypeInterface>(flt);
819b2a1bcfSAlex Zinenko   ASSERT_TRUE(iface != nullptr);
829b2a1bcfSAlex Zinenko   EXPECT_EQ(iface.getBitwidthPlusArg(10), 42u);
839b2a1bcfSAlex Zinenko   EXPECT_EQ(iface.staticGetSomeValuePlusArg(10), 52u);
849b2a1bcfSAlex Zinenko   EXPECT_EQ(iface.getBitwidthPlusDoubleArgument(3), 128u);
859b2a1bcfSAlex Zinenko   EXPECT_EQ(iface.staticGetArgument(17), 420u);
869b2a1bcfSAlex Zinenko 
879b2a1bcfSAlex Zinenko   // Other contexts shouldn't have the attribute attached.
889b2a1bcfSAlex Zinenko   MLIRContext other;
899b2a1bcfSAlex Zinenko   IntegerType i8other = IntegerType::get(&other, 8);
905550c821STres Popp   EXPECT_FALSE(isa<TestExternalTypeInterface>(i8other));
919b2a1bcfSAlex Zinenko }
929b2a1bcfSAlex Zinenko 
93d7e89121SAlex Zinenko /// External interface model for the test type from the test dialect.
94d7e89121SAlex Zinenko struct TestTypeModel
95d7e89121SAlex Zinenko     : public TestExternalTypeInterface::ExternalModel<TestTypeModel,
96d7e89121SAlex Zinenko                                                       test::TestType> {
97d7e89121SAlex Zinenko   unsigned getBitwidthPlusArg(Type type, unsigned arg) const { return arg; }
98d7e89121SAlex Zinenko 
99d7e89121SAlex Zinenko   static unsigned staticGetSomeValuePlusArg(unsigned arg) { return 10 + arg; }
100d7e89121SAlex Zinenko };
101d7e89121SAlex Zinenko 
102d7e89121SAlex Zinenko TEST(InterfaceAttachment, TypeDelayedContextConstruct) {
103d7e89121SAlex Zinenko   // Put the interface in the registry.
104d7e89121SAlex Zinenko   DialectRegistry registry;
105d7e89121SAlex Zinenko   registry.insert<test::TestDialect>();
10677eee579SRiver Riddle   registry.addExtension(+[](MLIRContext *ctx, test::TestDialect *dialect) {
10777eee579SRiver Riddle     test::TestType::attachInterface<TestTypeModel>(*ctx);
10877eee579SRiver Riddle   });
109d7e89121SAlex Zinenko 
110d7e89121SAlex Zinenko   // Check that when a context is constructed with the given registry, the type
111d7e89121SAlex Zinenko   // interface gets registered.
112d7e89121SAlex Zinenko   MLIRContext context(registry);
113d7e89121SAlex Zinenko   context.loadDialect<test::TestDialect>();
114d7e89121SAlex Zinenko   test::TestType testType = test::TestType::get(&context);
1155550c821STres Popp   auto iface = dyn_cast<TestExternalTypeInterface>(testType);
116d7e89121SAlex Zinenko   ASSERT_TRUE(iface != nullptr);
117d7e89121SAlex Zinenko   EXPECT_EQ(iface.getBitwidthPlusArg(42), 42u);
118d7e89121SAlex Zinenko   EXPECT_EQ(iface.staticGetSomeValuePlusArg(10), 20u);
119d7e89121SAlex Zinenko }
120d7e89121SAlex Zinenko 
121d7e89121SAlex Zinenko TEST(InterfaceAttachment, TypeDelayedContextAppend) {
122d7e89121SAlex Zinenko   // Put the interface in the registry.
123d7e89121SAlex Zinenko   DialectRegistry registry;
124d7e89121SAlex Zinenko   registry.insert<test::TestDialect>();
12577eee579SRiver Riddle   registry.addExtension(+[](MLIRContext *ctx, test::TestDialect *dialect) {
12677eee579SRiver Riddle     test::TestType::attachInterface<TestTypeModel>(*ctx);
12777eee579SRiver Riddle   });
128d7e89121SAlex Zinenko 
129d7e89121SAlex Zinenko   // Check that when the registry gets appended to the context, the interface
130d7e89121SAlex Zinenko   // becomes available for objects in loaded dialects.
131d7e89121SAlex Zinenko   MLIRContext context;
132d7e89121SAlex Zinenko   context.loadDialect<test::TestDialect>();
133d7e89121SAlex Zinenko   test::TestType testType = test::TestType::get(&context);
1345550c821STres Popp   EXPECT_FALSE(isa<TestExternalTypeInterface>(testType));
135d7e89121SAlex Zinenko   context.appendDialectRegistry(registry);
1365550c821STres Popp   EXPECT_TRUE(isa<TestExternalTypeInterface>(testType));
137d7e89121SAlex Zinenko }
138d7e89121SAlex Zinenko 
139d7e89121SAlex Zinenko TEST(InterfaceAttachment, RepeatedRegistration) {
140d7e89121SAlex Zinenko   DialectRegistry registry;
14177eee579SRiver Riddle   registry.addExtension(+[](MLIRContext *ctx, BuiltinDialect *dialect) {
14277eee579SRiver Riddle     IntegerType::attachInterface<Model>(*ctx);
14377eee579SRiver Riddle   });
144d7e89121SAlex Zinenko   MLIRContext context(registry);
145d7e89121SAlex Zinenko 
146d7e89121SAlex Zinenko   // Should't fail on repeated registration through the dialect registry.
147d7e89121SAlex Zinenko   context.appendDialectRegistry(registry);
148d7e89121SAlex Zinenko }
149d7e89121SAlex Zinenko 
150d7e89121SAlex Zinenko TEST(InterfaceAttachment, TypeBuiltinDelayed) {
151d7e89121SAlex Zinenko   // Builtin dialect needs to registration or loading, but delayed interface
152d7e89121SAlex Zinenko   // registration must still work.
153d7e89121SAlex Zinenko   DialectRegistry registry;
15477eee579SRiver Riddle   registry.addExtension(+[](MLIRContext *ctx, BuiltinDialect *dialect) {
15577eee579SRiver Riddle     IntegerType::attachInterface<Model>(*ctx);
15677eee579SRiver Riddle   });
157d7e89121SAlex Zinenko 
158d7e89121SAlex Zinenko   MLIRContext context(registry);
159d7e89121SAlex Zinenko   IntegerType i16 = IntegerType::get(&context, 16);
1605550c821STres Popp   EXPECT_TRUE(isa<TestExternalTypeInterface>(i16));
161d7e89121SAlex Zinenko 
162d7e89121SAlex Zinenko   MLIRContext initiallyEmpty;
163d7e89121SAlex Zinenko   IntegerType i32 = IntegerType::get(&initiallyEmpty, 32);
1645550c821STres Popp   EXPECT_FALSE(isa<TestExternalTypeInterface>(i32));
165d7e89121SAlex Zinenko   initiallyEmpty.appendDialectRegistry(registry);
1665550c821STres Popp   EXPECT_TRUE(isa<TestExternalTypeInterface>(i32));
167d7e89121SAlex Zinenko }
168d7e89121SAlex Zinenko 
1699b2a1bcfSAlex Zinenko /// The interface provides a default implementation that expects
1709b2a1bcfSAlex Zinenko /// ConcreteType::getWidth to exist, which is the case for IntegerType. So this
1719b2a1bcfSAlex Zinenko /// just derives from the ExternalModel.
1729b2a1bcfSAlex Zinenko struct TestExternalFallbackTypeIntegerModel
1739b2a1bcfSAlex Zinenko     : public TestExternalFallbackTypeInterface::ExternalModel<
1749b2a1bcfSAlex Zinenko           TestExternalFallbackTypeIntegerModel, IntegerType> {};
1759b2a1bcfSAlex Zinenko 
1769b2a1bcfSAlex Zinenko /// The interface provides a default implementation that expects
1779b2a1bcfSAlex Zinenko /// ConcreteType::getWidth to exist, which is *not* the case for VectorType. Use
1789b2a1bcfSAlex Zinenko /// FallbackModel instead to override this and make sure the code still compiles
1799b2a1bcfSAlex Zinenko /// because we never instantiate the ExternalModel class template with a
1809b2a1bcfSAlex Zinenko /// template argument that would have led to compilation failures.
1819b2a1bcfSAlex Zinenko struct TestExternalFallbackTypeVectorModel
1829b2a1bcfSAlex Zinenko     : public TestExternalFallbackTypeInterface::FallbackModel<
1839b2a1bcfSAlex Zinenko           TestExternalFallbackTypeVectorModel> {
1849b2a1bcfSAlex Zinenko   unsigned getBitwidth(Type type) const {
1855550c821STres Popp     IntegerType elementType =
1865550c821STres Popp         dyn_cast_or_null<IntegerType>(cast<VectorType>(type).getElementType());
1879b2a1bcfSAlex Zinenko     return elementType ? elementType.getWidth() : 0;
1889b2a1bcfSAlex Zinenko   }
1899b2a1bcfSAlex Zinenko };
1909b2a1bcfSAlex Zinenko 
1919b2a1bcfSAlex Zinenko TEST(InterfaceAttachment, Fallback) {
1929b2a1bcfSAlex Zinenko   MLIRContext context;
1939b2a1bcfSAlex Zinenko 
1949b2a1bcfSAlex Zinenko   // Just check that we can attach the interface.
1959b2a1bcfSAlex Zinenko   IntegerType i8 = IntegerType::get(&context, 8);
1965550c821STres Popp   ASSERT_FALSE(isa<TestExternalFallbackTypeInterface>(i8));
1979b2a1bcfSAlex Zinenko   IntegerType::attachInterface<TestExternalFallbackTypeIntegerModel>(context);
1985550c821STres Popp   ASSERT_TRUE(isa<TestExternalFallbackTypeInterface>(i8));
1999b2a1bcfSAlex Zinenko 
2009b2a1bcfSAlex Zinenko   // Call the method so it is guaranteed not to be instantiated.
2019b2a1bcfSAlex Zinenko   VectorType vec = VectorType::get({42}, i8);
2025550c821STres Popp   ASSERT_FALSE(isa<TestExternalFallbackTypeInterface>(vec));
2039b2a1bcfSAlex Zinenko   VectorType::attachInterface<TestExternalFallbackTypeVectorModel>(context);
2045550c821STres Popp   ASSERT_TRUE(isa<TestExternalFallbackTypeInterface>(vec));
2055550c821STres Popp   EXPECT_EQ(cast<TestExternalFallbackTypeInterface>(vec).getBitwidth(), 8u);
2069b2a1bcfSAlex Zinenko }
2079b2a1bcfSAlex Zinenko 
2089b2a1bcfSAlex Zinenko /// External model for attribute interfaces.
209d7e89121SAlex Zinenko struct TestExternalIntegerAttrModel
2109b2a1bcfSAlex Zinenko     : public TestExternalAttrInterface::ExternalModel<
211d7e89121SAlex Zinenko           TestExternalIntegerAttrModel, IntegerAttr> {
2129b2a1bcfSAlex Zinenko   const Dialect *getDialectPtr(Attribute attr) const {
2135550c821STres Popp     return &cast<IntegerAttr>(attr).getDialect();
2149b2a1bcfSAlex Zinenko   }
2159b2a1bcfSAlex Zinenko 
2169b2a1bcfSAlex Zinenko   static int getSomeNumber() { return 42; }
2179b2a1bcfSAlex Zinenko };
2189b2a1bcfSAlex Zinenko 
2199b2a1bcfSAlex Zinenko TEST(InterfaceAttachment, Attribute) {
2209b2a1bcfSAlex Zinenko   MLIRContext context;
2219b2a1bcfSAlex Zinenko 
2229b2a1bcfSAlex Zinenko   // Attribute interfaces use the exact same mechanism as types, so just check
2239b2a1bcfSAlex Zinenko   // that the basics work for attributes.
2249b2a1bcfSAlex Zinenko   IntegerAttr attr = IntegerAttr::get(IntegerType::get(&context, 32), 42);
2255550c821STres Popp   ASSERT_FALSE(isa<TestExternalAttrInterface>(attr));
226d7e89121SAlex Zinenko   IntegerAttr::attachInterface<TestExternalIntegerAttrModel>(context);
2275550c821STres Popp   auto iface = dyn_cast<TestExternalAttrInterface>(attr);
2289b2a1bcfSAlex Zinenko   ASSERT_TRUE(iface != nullptr);
2299b2a1bcfSAlex Zinenko   EXPECT_EQ(iface.getDialectPtr(), &attr.getDialect());
2309b2a1bcfSAlex Zinenko   EXPECT_EQ(iface.getSomeNumber(), 42);
2319b2a1bcfSAlex Zinenko }
2329b2a1bcfSAlex Zinenko 
233d7e89121SAlex Zinenko /// External model for an interface attachable to a non-builtin attribute.
234d7e89121SAlex Zinenko struct TestExternalSimpleAAttrModel
235d7e89121SAlex Zinenko     : public TestExternalAttrInterface::ExternalModel<
236d7e89121SAlex Zinenko           TestExternalSimpleAAttrModel, test::SimpleAAttr> {
237d7e89121SAlex Zinenko   const Dialect *getDialectPtr(Attribute attr) const {
238d7e89121SAlex Zinenko     return &attr.getDialect();
239d7e89121SAlex Zinenko   }
240d7e89121SAlex Zinenko 
241d7e89121SAlex Zinenko   static int getSomeNumber() { return 21; }
242d7e89121SAlex Zinenko };
243d7e89121SAlex Zinenko 
244d7e89121SAlex Zinenko TEST(InterfaceAttachmentTest, AttributeDelayed) {
245d7e89121SAlex Zinenko   // Attribute interfaces use the exact same mechanism as types, so just check
246d7e89121SAlex Zinenko   // that the delayed registration work for attributes.
247d7e89121SAlex Zinenko   DialectRegistry registry;
248d7e89121SAlex Zinenko   registry.insert<test::TestDialect>();
24977eee579SRiver Riddle   registry.addExtension(+[](MLIRContext *ctx, test::TestDialect *dialect) {
25077eee579SRiver Riddle     test::SimpleAAttr::attachInterface<TestExternalSimpleAAttrModel>(*ctx);
25177eee579SRiver Riddle   });
252d7e89121SAlex Zinenko 
253d7e89121SAlex Zinenko   MLIRContext context(registry);
254d7e89121SAlex Zinenko   context.loadDialect<test::TestDialect>();
255d7e89121SAlex Zinenko   auto attr = test::SimpleAAttr::get(&context);
2565550c821STres Popp   EXPECT_TRUE(isa<TestExternalAttrInterface>(attr));
257d7e89121SAlex Zinenko 
258d7e89121SAlex Zinenko   MLIRContext initiallyEmpty;
259d7e89121SAlex Zinenko   initiallyEmpty.loadDialect<test::TestDialect>();
260d7e89121SAlex Zinenko   attr = test::SimpleAAttr::get(&initiallyEmpty);
2615550c821STres Popp   EXPECT_FALSE(isa<TestExternalAttrInterface>(attr));
262d7e89121SAlex Zinenko   initiallyEmpty.appendDialectRegistry(registry);
2635550c821STres Popp   EXPECT_TRUE(isa<TestExternalAttrInterface>(attr));
264d7e89121SAlex Zinenko }
265d7e89121SAlex Zinenko 
26623cdf7b6SAlex Zinenko /// External interface model for the module operation. Only provides non-default
26723cdf7b6SAlex Zinenko /// methods.
26823cdf7b6SAlex Zinenko struct TestExternalOpModel
26923cdf7b6SAlex Zinenko     : public TestExternalOpInterface::ExternalModel<TestExternalOpModel,
27023cdf7b6SAlex Zinenko                                                     ModuleOp> {
27123cdf7b6SAlex Zinenko   unsigned getNameLengthPlusArg(Operation *op, unsigned arg) const {
27223cdf7b6SAlex Zinenko     return op->getName().getStringRef().size() + arg;
27323cdf7b6SAlex Zinenko   }
27423cdf7b6SAlex Zinenko 
27523cdf7b6SAlex Zinenko   static unsigned getNameLengthPlusArgTwice(unsigned arg) {
27623cdf7b6SAlex Zinenko     return ModuleOp::getOperationName().size() + 2 * arg;
27723cdf7b6SAlex Zinenko   }
27823cdf7b6SAlex Zinenko };
27923cdf7b6SAlex Zinenko 
28023cdf7b6SAlex Zinenko /// External interface model for the func operation. Provides non-deafult and
28123cdf7b6SAlex Zinenko /// overrides default methods.
28223cdf7b6SAlex Zinenko struct TestExternalOpOverridingModel
28323cdf7b6SAlex Zinenko     : public TestExternalOpInterface::FallbackModel<
28423cdf7b6SAlex Zinenko           TestExternalOpOverridingModel> {
28523cdf7b6SAlex Zinenko   unsigned getNameLengthPlusArg(Operation *op, unsigned arg) const {
28623cdf7b6SAlex Zinenko     return op->getName().getStringRef().size() + arg;
28723cdf7b6SAlex Zinenko   }
28823cdf7b6SAlex Zinenko 
28923cdf7b6SAlex Zinenko   static unsigned getNameLengthPlusArgTwice(unsigned arg) {
29036550692SRiver Riddle     return UnrealizedConversionCastOp::getOperationName().size() + 2 * arg;
29123cdf7b6SAlex Zinenko   }
29223cdf7b6SAlex Zinenko 
29323cdf7b6SAlex Zinenko   unsigned getNameLengthTimesArg(Operation *op, unsigned arg) const {
29423cdf7b6SAlex Zinenko     return 42;
29523cdf7b6SAlex Zinenko   }
29623cdf7b6SAlex Zinenko 
29723cdf7b6SAlex Zinenko   static unsigned getNameLengthMinusArg(unsigned arg) { return 21; }
29823cdf7b6SAlex Zinenko };
29923cdf7b6SAlex Zinenko 
30023cdf7b6SAlex Zinenko TEST(InterfaceAttachment, Operation) {
30123cdf7b6SAlex Zinenko   MLIRContext context;
30236550692SRiver Riddle   OpBuilder builder(&context);
30323cdf7b6SAlex Zinenko 
30423cdf7b6SAlex Zinenko   // Initially, the operation doesn't have the interface.
30536550692SRiver Riddle   OwningOpRef<ModuleOp> moduleOp =
30636550692SRiver Riddle       builder.create<ModuleOp>(UnknownLoc::get(&context));
307db79f4a2SMehdi Amini   ASSERT_FALSE(isa<TestExternalOpInterface>(moduleOp->getOperation()));
30823cdf7b6SAlex Zinenko 
30923cdf7b6SAlex Zinenko   // We can attach an external interface and now the operaiton has it.
31023cdf7b6SAlex Zinenko   ModuleOp::attachInterface<TestExternalOpModel>(context);
311db79f4a2SMehdi Amini   auto iface = dyn_cast<TestExternalOpInterface>(moduleOp->getOperation());
31223cdf7b6SAlex Zinenko   ASSERT_TRUE(iface != nullptr);
313f8479d9dSRiver Riddle   EXPECT_EQ(iface.getNameLengthPlusArg(10), 24u);
314f8479d9dSRiver Riddle   EXPECT_EQ(iface.getNameLengthTimesArg(3), 42u);
315f8479d9dSRiver Riddle   EXPECT_EQ(iface.getNameLengthPlusArgTwice(18), 50u);
316f8479d9dSRiver Riddle   EXPECT_EQ(iface.getNameLengthMinusArg(5), 9u);
31723cdf7b6SAlex Zinenko 
31823cdf7b6SAlex Zinenko   // Default implementation can be overridden.
31936550692SRiver Riddle   OwningOpRef<UnrealizedConversionCastOp> castOp =
32036550692SRiver Riddle       builder.create<UnrealizedConversionCastOp>(UnknownLoc::get(&context),
32136550692SRiver Riddle                                                  TypeRange(), ValueRange());
32236550692SRiver Riddle   ASSERT_FALSE(isa<TestExternalOpInterface>(castOp->getOperation()));
32336550692SRiver Riddle   UnrealizedConversionCastOp::attachInterface<TestExternalOpOverridingModel>(
32436550692SRiver Riddle       context);
32536550692SRiver Riddle   iface = dyn_cast<TestExternalOpInterface>(castOp->getOperation());
32623cdf7b6SAlex Zinenko   ASSERT_TRUE(iface != nullptr);
32736550692SRiver Riddle   EXPECT_EQ(iface.getNameLengthPlusArg(10), 44u);
32823cdf7b6SAlex Zinenko   EXPECT_EQ(iface.getNameLengthTimesArg(0), 42u);
32936550692SRiver Riddle   EXPECT_EQ(iface.getNameLengthPlusArgTwice(8), 50u);
33023cdf7b6SAlex Zinenko   EXPECT_EQ(iface.getNameLengthMinusArg(1000), 21u);
33123cdf7b6SAlex Zinenko 
33223cdf7b6SAlex Zinenko   // Another context doesn't have the interfaces registered.
33323cdf7b6SAlex Zinenko   MLIRContext other;
334db79f4a2SMehdi Amini   OwningOpRef<ModuleOp> otherModuleOp =
335db79f4a2SMehdi Amini       ModuleOp::create(UnknownLoc::get(&other));
336db79f4a2SMehdi Amini   ASSERT_FALSE(isa<TestExternalOpInterface>(otherModuleOp->getOperation()));
33723cdf7b6SAlex Zinenko }
33823cdf7b6SAlex Zinenko 
3399b50844fSVladislav Vinogradov template <class ConcreteOp>
340d7e89121SAlex Zinenko struct TestExternalTestOpModel
3419b50844fSVladislav Vinogradov     : public TestExternalOpInterface::ExternalModel<
3429b50844fSVladislav Vinogradov           TestExternalTestOpModel<ConcreteOp>, ConcreteOp> {
343d7e89121SAlex Zinenko   unsigned getNameLengthPlusArg(Operation *op, unsigned arg) const {
344d7e89121SAlex Zinenko     return op->getName().getStringRef().size() + arg;
345d7e89121SAlex Zinenko   }
346d7e89121SAlex Zinenko 
347d7e89121SAlex Zinenko   static unsigned getNameLengthPlusArgTwice(unsigned arg) {
3489b50844fSVladislav Vinogradov     return ConcreteOp::getOperationName().size() + 2 * arg;
349d7e89121SAlex Zinenko   }
350d7e89121SAlex Zinenko };
351d7e89121SAlex Zinenko 
352d7e89121SAlex Zinenko TEST(InterfaceAttachment, OperationDelayedContextConstruct) {
353d7e89121SAlex Zinenko   DialectRegistry registry;
354d7e89121SAlex Zinenko   registry.insert<test::TestDialect>();
35577eee579SRiver Riddle   registry.addExtension(+[](MLIRContext *ctx, BuiltinDialect *dialect) {
35677eee579SRiver Riddle     ModuleOp::attachInterface<TestExternalOpModel>(*ctx);
35777eee579SRiver Riddle   });
35877eee579SRiver Riddle   registry.addExtension(+[](MLIRContext *ctx, test::TestDialect *dialect) {
35977eee579SRiver Riddle     test::OpJ::attachInterface<TestExternalTestOpModel<test::OpJ>>(*ctx);
36077eee579SRiver Riddle     test::OpH::attachInterface<TestExternalTestOpModel<test::OpH>>(*ctx);
36177eee579SRiver Riddle   });
362d7e89121SAlex Zinenko 
36377eee579SRiver Riddle   // Construct the context directly from a registry. The interfaces are
36477eee579SRiver Riddle   // expected to be readily available on operations.
365d7e89121SAlex Zinenko   MLIRContext context(registry);
366d7e89121SAlex Zinenko   context.loadDialect<test::TestDialect>();
3679b50844fSVladislav Vinogradov 
368db79f4a2SMehdi Amini   OwningOpRef<ModuleOp> module = ModuleOp::create(UnknownLoc::get(&context));
369db79f4a2SMehdi Amini   OpBuilder builder(module->getBody(), module->getBody()->begin());
3709b50844fSVladislav Vinogradov   auto opJ =
371d7e89121SAlex Zinenko       builder.create<test::OpJ>(builder.getUnknownLoc(), builder.getI32Type());
3729b50844fSVladislav Vinogradov   auto opH =
3739b50844fSVladislav Vinogradov       builder.create<test::OpH>(builder.getUnknownLoc(), opJ.getResult());
3749b50844fSVladislav Vinogradov   auto opI =
3759b50844fSVladislav Vinogradov       builder.create<test::OpI>(builder.getUnknownLoc(), opJ.getResult());
3769b50844fSVladislav Vinogradov 
377db79f4a2SMehdi Amini   EXPECT_TRUE(isa<TestExternalOpInterface>(module->getOperation()));
3789b50844fSVladislav Vinogradov   EXPECT_TRUE(isa<TestExternalOpInterface>(opJ.getOperation()));
3799b50844fSVladislav Vinogradov   EXPECT_TRUE(isa<TestExternalOpInterface>(opH.getOperation()));
3809b50844fSVladislav Vinogradov   EXPECT_FALSE(isa<TestExternalOpInterface>(opI.getOperation()));
381d7e89121SAlex Zinenko }
382d7e89121SAlex Zinenko 
383d7e89121SAlex Zinenko TEST(InterfaceAttachment, OperationDelayedContextAppend) {
384d7e89121SAlex Zinenko   DialectRegistry registry;
385d7e89121SAlex Zinenko   registry.insert<test::TestDialect>();
38677eee579SRiver Riddle   registry.addExtension(+[](MLIRContext *ctx, BuiltinDialect *dialect) {
38777eee579SRiver Riddle     ModuleOp::attachInterface<TestExternalOpModel>(*ctx);
38877eee579SRiver Riddle   });
38977eee579SRiver Riddle   registry.addExtension(+[](MLIRContext *ctx, test::TestDialect *dialect) {
39077eee579SRiver Riddle     test::OpJ::attachInterface<TestExternalTestOpModel<test::OpJ>>(*ctx);
39177eee579SRiver Riddle     test::OpH::attachInterface<TestExternalTestOpModel<test::OpH>>(*ctx);
39277eee579SRiver Riddle   });
393d7e89121SAlex Zinenko 
394d7e89121SAlex Zinenko   // Construct the context, create ops, and only then append the registry. The
395d7e89121SAlex Zinenko   // interfaces are expected to be available after appending the registry.
396d7e89121SAlex Zinenko   MLIRContext context;
397d7e89121SAlex Zinenko   context.loadDialect<test::TestDialect>();
3989b50844fSVladislav Vinogradov 
399db79f4a2SMehdi Amini   OwningOpRef<ModuleOp> module = ModuleOp::create(UnknownLoc::get(&context));
400db79f4a2SMehdi Amini   OpBuilder builder(module->getBody(), module->getBody()->begin());
4019b50844fSVladislav Vinogradov   auto opJ =
402d7e89121SAlex Zinenko       builder.create<test::OpJ>(builder.getUnknownLoc(), builder.getI32Type());
4039b50844fSVladislav Vinogradov   auto opH =
4049b50844fSVladislav Vinogradov       builder.create<test::OpH>(builder.getUnknownLoc(), opJ.getResult());
4059b50844fSVladislav Vinogradov   auto opI =
4069b50844fSVladislav Vinogradov       builder.create<test::OpI>(builder.getUnknownLoc(), opJ.getResult());
4079b50844fSVladislav Vinogradov 
408db79f4a2SMehdi Amini   EXPECT_FALSE(isa<TestExternalOpInterface>(module->getOperation()));
4099b50844fSVladislav Vinogradov   EXPECT_FALSE(isa<TestExternalOpInterface>(opJ.getOperation()));
4109b50844fSVladislav Vinogradov   EXPECT_FALSE(isa<TestExternalOpInterface>(opH.getOperation()));
4119b50844fSVladislav Vinogradov   EXPECT_FALSE(isa<TestExternalOpInterface>(opI.getOperation()));
4129b50844fSVladislav Vinogradov 
413d7e89121SAlex Zinenko   context.appendDialectRegistry(registry);
4149b50844fSVladislav Vinogradov 
415db79f4a2SMehdi Amini   EXPECT_TRUE(isa<TestExternalOpInterface>(module->getOperation()));
4169b50844fSVladislav Vinogradov   EXPECT_TRUE(isa<TestExternalOpInterface>(opJ.getOperation()));
4179b50844fSVladislav Vinogradov   EXPECT_TRUE(isa<TestExternalOpInterface>(opH.getOperation()));
4189b50844fSVladislav Vinogradov   EXPECT_FALSE(isa<TestExternalOpInterface>(opI.getOperation()));
419d7e89121SAlex Zinenko }
420d7e89121SAlex Zinenko 
421d0e6fd99SFabian Mora TEST(InterfaceAttachmentTest, PromisedInterfaces) {
422d0e6fd99SFabian Mora   // Attribute interfaces use the exact same mechanism as types, so just check
423d0e6fd99SFabian Mora   // that the promise mechanism works for attributes.
424d0e6fd99SFabian Mora   MLIRContext context;
42544324799SMehdi Amini   auto *testDialect = context.getOrLoadDialect<test::TestDialect>();
426d0e6fd99SFabian Mora   auto attr = test::SimpleAAttr::get(&context);
427d0e6fd99SFabian Mora 
428d0e6fd99SFabian Mora   // `SimpleAAttr` doesn't implement nor promises the
429d0e6fd99SFabian Mora   // `TestExternalAttrInterface` interface.
430d0e6fd99SFabian Mora   EXPECT_FALSE(isa<TestExternalAttrInterface>(attr));
431d0e6fd99SFabian Mora   EXPECT_FALSE(
432d0e6fd99SFabian Mora       attr.hasPromiseOrImplementsInterface<TestExternalAttrInterface>());
433d0e6fd99SFabian Mora 
434d0e6fd99SFabian Mora   // Add a promise `TestExternalAttrInterface`.
43535d55f28SJustin Fargnoli   testDialect->declarePromisedInterface<TestExternalAttrInterface,
43635d55f28SJustin Fargnoli                                         test::SimpleAAttr>();
437d0e6fd99SFabian Mora   EXPECT_TRUE(
438d0e6fd99SFabian Mora       attr.hasPromiseOrImplementsInterface<TestExternalAttrInterface>());
439d0e6fd99SFabian Mora 
440d0e6fd99SFabian Mora   // Attach the interface.
441d0e6fd99SFabian Mora   test::SimpleAAttr::attachInterface<TestExternalAttrInterface>(context);
442d0e6fd99SFabian Mora   EXPECT_TRUE(isa<TestExternalAttrInterface>(attr));
443d0e6fd99SFabian Mora   EXPECT_TRUE(
444d0e6fd99SFabian Mora       attr.hasPromiseOrImplementsInterface<TestExternalAttrInterface>());
445d0e6fd99SFabian Mora }
446d0e6fd99SFabian Mora 
447be0a7e9fSMehdi Amini } // namespace
448