256 lines · cpp
1//===- IRDLVerifiers.cpp - IRDL verifiers ------------------------- C++ -*-===//2//3// This file is licensed under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Verifiers for objects declared by IRDL.10//11//===----------------------------------------------------------------------===//12 13#include "mlir/Dialect/IRDL/IRDLVerifiers.h"14#include "mlir/IR/Attributes.h"15#include "mlir/IR/BuiltinAttributes.h"16#include "mlir/IR/Diagnostics.h"17#include "mlir/IR/ExtensibleDialect.h"18#include "mlir/IR/Location.h"19#include "mlir/IR/Region.h"20#include "mlir/IR/Value.h"21 22using namespace mlir;23using namespace mlir::irdl;24 25ConstraintVerifier::ConstraintVerifier(26 ArrayRef<std::unique_ptr<Constraint>> constraints)27 : constraints(constraints), assigned() {28 assigned.resize(this->constraints.size());29}30 31LogicalResult32ConstraintVerifier::verify(function_ref<InFlightDiagnostic()> emitError,33 Attribute attr, unsigned variable) {34 35 assert(variable < constraints.size() && "invalid constraint variable");36 37 // If the variable is already assigned, check that the attribute is the same.38 if (assigned[variable].has_value()) {39 if (attr == assigned[variable].value()) {40 return success();41 }42 if (emitError)43 return emitError() << "expected '" << assigned[variable].value()44 << "' but got '" << attr << "'";45 return failure();46 }47 48 // Otherwise, check the constraint and assign the attribute to the variable.49 LogicalResult result = constraints[variable]->verify(emitError, attr, *this);50 if (succeeded(result))51 assigned[variable] = attr;52 53 return result;54}55 56LogicalResult IsConstraint::verify(function_ref<InFlightDiagnostic()> emitError,57 Attribute attr,58 ConstraintVerifier &context) const {59 if (attr == expectedAttribute)60 return success();61 62 if (emitError)63 return emitError() << "expected '" << expectedAttribute << "' but got '"64 << attr << "'";65 return failure();66}67 68LogicalResult69BaseAttrConstraint::verify(function_ref<InFlightDiagnostic()> emitError,70 Attribute attr, ConstraintVerifier &context) const {71 if (attr.getTypeID() == baseTypeID)72 return success();73 74 if (emitError)75 return emitError() << "expected base attribute '" << baseName76 << "' but got '" << attr.getAbstractAttribute().getName()77 << "'";78 return failure();79}80 81LogicalResult82BaseTypeConstraint::verify(function_ref<InFlightDiagnostic()> emitError,83 Attribute attr, ConstraintVerifier &context) const {84 auto typeAttr = dyn_cast<TypeAttr>(attr);85 if (!typeAttr) {86 if (emitError)87 return emitError() << "expected type, got attribute '" << attr;88 return failure();89 }90 91 Type type = typeAttr.getValue();92 if (type.getTypeID() == baseTypeID)93 return success();94 95 if (emitError)96 return emitError() << "expected base type '" << baseName << "' but got '"97 << type.getAbstractType().getName() << "'";98 return failure();99}100 101LogicalResult DynParametricAttrConstraint::verify(102 function_ref<InFlightDiagnostic()> emitError, Attribute attr,103 ConstraintVerifier &context) const {104 105 // Check that the base is the expected one.106 auto dynAttr = dyn_cast<DynamicAttr>(attr);107 if (!dynAttr || dynAttr.getAttrDef() != attrDef) {108 if (emitError) {109 StringRef dialectName = attrDef->getDialect()->getNamespace();110 StringRef attrName = attrDef->getName();111 return emitError() << "expected base attribute '" << attrName << '.'112 << dialectName << "' but got '" << attr << "'";113 }114 return failure();115 }116 117 // Check that the parameters satisfy the constraints.118 ArrayRef<Attribute> params = dynAttr.getParams();119 if (params.size() != constraints.size()) {120 if (emitError) {121 StringRef dialectName = attrDef->getDialect()->getNamespace();122 StringRef attrName = attrDef->getName();123 emitError() << "attribute '" << dialectName << "." << attrName124 << "' expects " << params.size() << " parameters but got "125 << constraints.size();126 }127 return failure();128 }129 130 for (size_t i = 0, s = params.size(); i < s; i++)131 if (failed(context.verify(emitError, params[i], constraints[i])))132 return failure();133 134 return success();135}136 137LogicalResult DynParametricTypeConstraint::verify(138 function_ref<InFlightDiagnostic()> emitError, Attribute attr,139 ConstraintVerifier &context) const {140 // Check that the base is a TypeAttr.141 auto typeAttr = dyn_cast<TypeAttr>(attr);142 if (!typeAttr) {143 if (emitError)144 return emitError() << "expected type, got attribute '" << attr;145 return failure();146 }147 148 // Check that the type base is the expected one.149 auto dynType = dyn_cast<DynamicType>(typeAttr.getValue());150 if (!dynType || dynType.getTypeDef() != typeDef) {151 if (emitError) {152 StringRef dialectName = typeDef->getDialect()->getNamespace();153 StringRef attrName = typeDef->getName();154 return emitError() << "expected base type '" << dialectName << '.'155 << attrName << "' but got '" << attr << "'";156 }157 return failure();158 }159 160 // Check that the parameters satisfy the constraints.161 ArrayRef<Attribute> params = dynType.getParams();162 if (params.size() != constraints.size()) {163 if (emitError) {164 StringRef dialectName = typeDef->getDialect()->getNamespace();165 StringRef attrName = typeDef->getName();166 emitError() << "attribute '" << dialectName << "." << attrName167 << "' expects " << params.size() << " parameters but got "168 << constraints.size();169 }170 return failure();171 }172 173 for (size_t i = 0, s = params.size(); i < s; i++)174 if (failed(context.verify(emitError, params[i], constraints[i])))175 return failure();176 177 return success();178}179 180LogicalResult181AnyOfConstraint::verify(function_ref<InFlightDiagnostic()> emitError,182 Attribute attr, ConstraintVerifier &context) const {183 for (unsigned constr : constraints) {184 // We do not pass the `emitError` here, since we want to emit an error185 // only if none of the constraints are satisfied.186 if (succeeded(context.verify({}, attr, constr))) {187 return success();188 }189 }190 191 if (emitError)192 return emitError() << "'" << attr << "' does not satisfy the constraint";193 return failure();194}195 196LogicalResult197AllOfConstraint::verify(function_ref<InFlightDiagnostic()> emitError,198 Attribute attr, ConstraintVerifier &context) const {199 for (unsigned constr : constraints) {200 if (failed(context.verify(emitError, attr, constr))) {201 return failure();202 }203 }204 205 return success();206}207 208LogicalResult209AnyAttributeConstraint::verify(function_ref<InFlightDiagnostic()> emitError,210 Attribute attr,211 ConstraintVerifier &context) const {212 return success();213}214 215LogicalResult RegionConstraint::verify(mlir::Region ®ion,216 ConstraintVerifier &constraintContext) {217 const auto emitError = [parentOp = region.getParentOp()](mlir::Location loc) {218 return [loc, parentOp] {219 InFlightDiagnostic diag = mlir::emitError(loc);220 // If we already have been given location of the parent operation, which221 // might happen when the region location is passed, we do not want to222 // produce the note on the same location223 if (loc != parentOp->getLoc())224 diag.attachNote(parentOp->getLoc()).append("see the operation");225 return diag;226 };227 };228 229 if (blockCount.has_value() && *blockCount != region.getBlocks().size()) {230 return emitError(region.getLoc())()231 << "expected region " << region.getRegionNumber() << " to have "232 << *blockCount << " block(s) but got " << region.getBlocks().size();233 }234 235 if (argumentConstraints.has_value()) {236 auto actualArgs = region.getArguments();237 if (actualArgs.size() != argumentConstraints->size()) {238 const mlir::Location firstArgLoc =239 actualArgs.empty() ? region.getLoc() : actualArgs.front().getLoc();240 return emitError(firstArgLoc)()241 << "expected region " << region.getRegionNumber() << " to have "242 << argumentConstraints->size() << " arguments but got "243 << actualArgs.size();244 }245 246 for (auto [arg, constraint] : llvm::zip(actualArgs, *argumentConstraints)) {247 mlir::Attribute type = TypeAttr::get(arg.getType());248 if (failed(constraintContext.verify(emitError(arg.getLoc()), type,249 constraint))) {250 return failure();251 }252 }253 }254 return success();255}256