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