Avoid hardcoded 4096 constant

This commit creates a static constexpr limit for the IntegerType
bitwidth and uses it. The check had to be moved because Token is
not aware of IR/Type and it was a sign the abstraction leaked:
bitwidth limit is not a property of the Token but of the IntegerType.

Added a positive and a negative test at the limit.

PiperOrigin-RevId: 210388192
diff --git a/include/mlir/IR/Types.h b/include/mlir/IR/Types.h
index 62f146a..daa62c5 100644
--- a/include/mlir/IR/Types.h
+++ b/include/mlir/IR/Types.h
@@ -134,7 +134,7 @@
   return os;
 }
 
-/// Integer types can have arbitrary bitwidth up to a large fixed limit of 4096.
+/// Integer types can have arbitrary bitwidth up to a large fixed limit.
 class IntegerType : public Type {
 public:
   static IntegerType *get(unsigned width, MLIRContext *context);
@@ -148,6 +148,9 @@
   static bool classof(const Type *type) {
     return type->getKind() == Kind::Integer;
   }
+
+  /// Integer representation maximal bitwidth.
+  static constexpr unsigned kMaxWidth = 4096;
 private:
   unsigned width;
   IntegerType(unsigned width, MLIRContext *context);
diff --git a/lib/IR/Types.cpp b/lib/IR/Types.cpp
index d32fae3..02b7d35 100644
--- a/lib/IR/Types.cpp
+++ b/lib/IR/Types.cpp
@@ -23,6 +23,7 @@
 
 IntegerType::IntegerType(unsigned width, MLIRContext *context)
   : Type(Kind::Integer, context), width(width) {
+    assert(width <= kMaxWidth && "admissible integer bitwidth exceeded");
 }
 
 FloatType::FloatType(Kind kind, MLIRContext *context) : Type(kind, context) {}
diff --git a/lib/Parser/Parser.cpp b/lib/Parser/Parser.cpp
index 285312f..756f25d 100644
--- a/lib/Parser/Parser.cpp
+++ b/lib/Parser/Parser.cpp
@@ -316,6 +316,10 @@
     auto width = getToken().getIntTypeBitwidth();
     if (!width.hasValue())
       return (emitError("invalid integer width"), nullptr);
+    if (width > IntegerType::kMaxWidth)
+      return (emitError("integer bitwidth is limited to " +
+                        Twine(IntegerType::kMaxWidth) + " bits"),
+              nullptr);
     consumeToken(Token::inttype);
     return builder.getIntegerType(width.getValue());
   }
diff --git a/lib/Parser/Token.cpp b/lib/Parser/Token.cpp
index 4c3f9e4..4e01f5a 100644
--- a/lib/Parser/Token.cpp
+++ b/lib/Parser/Token.cpp
@@ -72,9 +72,7 @@
 Optional<unsigned> Token::getIntTypeBitwidth() const {
   unsigned result = 0;
   if (spelling[1] == '0' ||
-      spelling.drop_front().getAsInteger(10, result) ||
-      // Arbitrary but large limit on bitwidth.
-      result > 4096 || result == 0)
+      spelling.drop_front().getAsInteger(10, result) || result == 0)
     return None;
   return result;
 }
diff --git a/test/IR/invalid-ops.mlir b/test/IR/invalid-ops.mlir
index f49ba44..bf9eae1 100644
--- a/test/IR/invalid-ops.mlir
+++ b/test/IR/invalid-ops.mlir
@@ -101,6 +101,15 @@
 
 // -----
 
+cfgfunc @intlimit2() {
+bb:
+  %0 = "constant"() {value: 0} : () -> i4096
+  %1 = "constant"() {value: 1} : () -> i4097 // expected-error {{integer bitwidth is limited to 4096 bits}}
+  return
+}
+
+// -----
+
 mlfunc @mlfunc_constant() {
   %x = "constant"(){value: "xyz"} : () -> i32 // expected-error {{'constant' op requires 'value' to be an integer for an integer result type}}
   return
@@ -112,4 +121,3 @@
   %x = call @calls() : () -> i32  // expected-error {{reference to function with mismatched type}}
   return
 }
-