Skip to content

[mlir] Fix FunctionOpInterface extraSharedClassDeclaration to be fully namespace qualified #82682

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 23, 2024

Conversation

shkoo
Copy link
Contributor

@shkoo shkoo commented Feb 22, 2024

extraSharedClassDeclaration of FunctionOpInterface can be inherited by other OpInterfaces into foreign namespaces, thus types must be fully qualified to prevent compiler errors, for example:

def MyFunc : OpInterface<"MyFunc", [FunctionOpInterface]> {
    let cppNamespace = "::MyNamespace";
}

@llvmbot llvmbot added the mlir label Feb 22, 2024
@llvmbot
Copy link
Member

llvmbot commented Feb 22, 2024

@llvm/pr-subscribers-mlir

Author: None (shkoo)

Changes

extraSharedClassDeclaration of FunctionOpInterface can be inherited by other OpInterfaces into foreign namespaces, thus types must be fully qualified to prevent compiler errors, for example:

def MyFunc : OpInterface&lt;"MyFunc", [FunctionOpInterface]&gt; {
    let cppNamespace = "::MyNamespace";
}

Patch is 25.37 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/82682.diff

1 Files Affected:

  • (modified) mlir/include/mlir/Interfaces/FunctionInterfaces.td (+113-113)
diff --git a/mlir/include/mlir/Interfaces/FunctionInterfaces.td b/mlir/include/mlir/Interfaces/FunctionInterfaces.td
index 98e002565cf192..970a781c998b98 100644
--- a/mlir/include/mlir/Interfaces/FunctionInterfaces.td
+++ b/mlir/include/mlir/Interfaces/FunctionInterfaces.td
@@ -147,12 +147,12 @@ def FunctionOpInterface : OpInterface<"FunctionOpInterface", [
   }];
   let extraSharedClassDeclaration = [{
     /// Block list iterator types.
-    using BlockListType = Region::BlockListType;
+    using BlockListType = ::mlir::Region::BlockListType;
     using iterator = BlockListType::iterator;
     using reverse_iterator = BlockListType::reverse_iterator;
 
     /// Block argument iterator types.
-    using BlockArgListType = Region::BlockArgListType;
+    using BlockArgListType = ::mlir::Region::BlockArgListType;
     using args_iterator = BlockArgListType::iterator;
 
     //===------------------------------------------------------------------===//
@@ -163,7 +163,7 @@ def FunctionOpInterface : OpInterface<"FunctionOpInterface", [
     bool isExternal() { return empty(); }
 
     /// Return the region containing the body of this function.
-    Region &getFunctionBody() { return $_op->getRegion(0); }
+    ::mlir::Region &getFunctionBody() { return $_op->getRegion(0); }
 
     /// Delete all blocks from this function.
     void eraseBody() {
@@ -183,39 +183,39 @@ def FunctionOpInterface : OpInterface<"FunctionOpInterface", [
     bool empty() { return getFunctionBody().empty(); }
 
     /// Push a new block to the back of the body region.
-    void push_back(Block *block) { getFunctionBody().push_back(block); }
+    void push_back(::mlir::Block *block) { getFunctionBody().push_back(block); }
 
     /// Push a new block to the front of the body region.
-    void push_front(Block *block) { getFunctionBody().push_front(block); }
+    void push_front(::mlir::Block *block) { getFunctionBody().push_front(block); }
 
     /// Return the last block in the body region.
-    Block &back() { return getFunctionBody().back(); }
+    ::mlir::Block &back() { return getFunctionBody().back(); }
 
     /// Return the first block in the body region.
-    Block &front() { return getFunctionBody().front(); }
+    ::mlir::Block &front() { return getFunctionBody().front(); }
 
     /// Add an entry block to an empty function, and set up the block arguments
     /// to match the signature of the function. The newly inserted entry block
     /// is returned.
-    Block *addEntryBlock() {
+    ::mlir::Block *addEntryBlock() {
       assert(empty() && "function already has an entry block");
-      Block *entry = new Block();
+      ::mlir::Block *entry = new ::mlir::Block();
       push_back(entry);
 
       // FIXME: Allow for passing in locations for these arguments instead of using
       // the operations location.
-      ArrayRef<Type> inputTypes = $_op.getArgumentTypes();
-      SmallVector<Location> locations(inputTypes.size(),
-                                      $_op.getOperation()->getLoc());
+      ::llvm::ArrayRef<::mlir::Type> inputTypes = $_op.getArgumentTypes();
+      ::llvm::SmallVector<::mlir::Location> locations(inputTypes.size(),
+                                              $_op.getOperation()->getLoc());
       entry->addArguments(inputTypes, locations);
       return entry;
     }
 
     /// Add a normal block to the end of the function's block list. The function
     /// should at least already have an entry block.
-    Block *addBlock() {
+    ::mlir::Block *addBlock() {
       assert(!empty() && "function should at least have an entry block");
-      push_back(new Block());
+      push_back(new ::mlir::Block());
       return &back();
     }
 
@@ -230,8 +230,8 @@ def FunctionOpInterface : OpInterface<"FunctionOpInterface", [
     ///  - the argument/result attributes may need an update: if the new type
     ///    has less parameters we drop the extra attributes, if there are more
     ///    parameters they won't have any attributes.
-    void setType(Type newType) {
-      function_interface_impl::setFunctionType($_op, newType);
+    void setType(::mlir::Type newType) {
+      ::mlir::function_interface_impl::setFunctionType($_op, newType);
     }
 
     //===------------------------------------------------------------------===//
@@ -245,7 +245,7 @@ def FunctionOpInterface : OpInterface<"FunctionOpInterface", [
     unsigned getNumResults() { return $_op.getResultTypes().size(); }
 
     /// Returns the entry block function argument at the given index.
-    BlockArgument getArgument(unsigned idx) {
+    ::mlir::BlockArgument getArgument(unsigned idx) {
       return getFunctionBody().getArgument(idx);
     }
 
@@ -256,8 +256,8 @@ def FunctionOpInterface : OpInterface<"FunctionOpInterface", [
 
     /// Insert a single argument of type `argType` with attributes `argAttrs` and
     /// location `argLoc` at `argIndex`.
-    void insertArgument(unsigned argIndex, Type argType, DictionaryAttr argAttrs,
-                        Location argLoc) {
+    void insertArgument(unsigned argIndex, ::mlir::Type argType, ::mlir::DictionaryAttr argAttrs,
+                        ::mlir::Location argLoc) {
       insertArguments({argIndex}, {argType}, {argAttrs}, {argLoc});
     }
 
@@ -265,20 +265,20 @@ def FunctionOpInterface : OpInterface<"FunctionOpInterface", [
     /// listed indices. `argIndices` must be sorted. Arguments are inserted in the
     /// order they are listed, such that arguments with identical index will
     /// appear in the same order that they were listed here.
-    void insertArguments(ArrayRef<unsigned> argIndices, TypeRange argTypes,
-                        ArrayRef<DictionaryAttr> argAttrs,
-                        ArrayRef<Location> argLocs) {
+    void insertArguments(::llvm::ArrayRef<unsigned> argIndices, ::mlir::TypeRange argTypes,
+                        ::llvm::ArrayRef<::mlir::DictionaryAttr> argAttrs,
+                        ::llvm::ArrayRef<::mlir::Location> argLocs) {
       unsigned originalNumArgs = $_op.getNumArguments();
-      Type newType = $_op.getTypeWithArgsAndResults(
+      ::mlir::Type newType = $_op.getTypeWithArgsAndResults(
           argIndices, argTypes, /*resultIndices=*/{}, /*resultTypes=*/{});
-      function_interface_impl::insertFunctionArguments(
+      ::mlir::function_interface_impl::insertFunctionArguments(
           $_op, argIndices, argTypes, argAttrs, argLocs,
           originalNumArgs, newType);
     }
 
     /// Insert a single result of type `resultType` at `resultIndex`.
-    void insertResult(unsigned resultIndex, Type resultType,
-                      DictionaryAttr resultAttrs) {
+    void insertResult(unsigned resultIndex, ::mlir::Type resultType,
+                      ::mlir::DictionaryAttr resultAttrs) {
       insertResults({resultIndex}, {resultType}, {resultAttrs});
     }
 
@@ -286,41 +286,41 @@ def FunctionOpInterface : OpInterface<"FunctionOpInterface", [
     /// `resultIndices` must be sorted. Results are inserted in the order they are
     /// listed, such that results with identical index will appear in the same
     /// order that they were listed here.
-    void insertResults(ArrayRef<unsigned> resultIndices, TypeRange resultTypes,
-                      ArrayRef<DictionaryAttr> resultAttrs) {
+    void insertResults(::llvm::ArrayRef<unsigned> resultIndices, ::mlir::TypeRange resultTypes,
+                       ::llvm::ArrayRef<::mlir::DictionaryAttr> resultAttrs) {
       unsigned originalNumResults = $_op.getNumResults();
-      Type newType = $_op.getTypeWithArgsAndResults(
+      ::mlir::Type newType = $_op.getTypeWithArgsAndResults(
         /*argIndices=*/{}, /*argTypes=*/{}, resultIndices, resultTypes);
-      function_interface_impl::insertFunctionResults(
+      ::mlir::function_interface_impl::insertFunctionResults(
           $_op, resultIndices, resultTypes, resultAttrs,
           originalNumResults, newType);
     }
 
     /// Erase a single argument at `argIndex`.
     void eraseArgument(unsigned argIndex) {
-      BitVector argsToErase($_op.getNumArguments());
+      ::llvm::BitVector argsToErase($_op.getNumArguments());
       argsToErase.set(argIndex);
       eraseArguments(argsToErase);
     }
 
     /// Erases the arguments listed in `argIndices`.
-    void eraseArguments(const BitVector &argIndices) {
-      Type newType = $_op.getTypeWithoutArgs(argIndices);
-      function_interface_impl::eraseFunctionArguments(
+    void eraseArguments(const ::llvm::BitVector &argIndices) {
+      ::mlir::Type newType = $_op.getTypeWithoutArgs(argIndices);
+      ::mlir::function_interface_impl::eraseFunctionArguments(
         $_op, argIndices, newType);
     }
 
     /// Erase a single result at `resultIndex`.
     void eraseResult(unsigned resultIndex) {
-      BitVector resultsToErase($_op.getNumResults());
+      ::llvm::BitVector resultsToErase($_op.getNumResults());
       resultsToErase.set(resultIndex);
       eraseResults(resultsToErase);
     }
 
     /// Erases the results listed in `resultIndices`.
-    void eraseResults(const BitVector &resultIndices) {
-      Type newType = $_op.getTypeWithoutResults(resultIndices);
-      function_interface_impl::eraseFunctionResults(
+    void eraseResults(const ::llvm::BitVector &resultIndices) {
+      ::mlir::Type newType = $_op.getTypeWithoutResults(resultIndices);
+      ::mlir::function_interface_impl::eraseFunctionResults(
           $_op, resultIndices, newType);
     }
 
@@ -328,13 +328,13 @@ def FunctionOpInterface : OpInterface<"FunctionOpInterface", [
     /// results inserted. This is used to update the function's signature in
     /// the `insertArguments` and `insertResults` methods. The arrays must be
     /// sorted by increasing index.
-    Type getTypeWithArgsAndResults(
-      ArrayRef<unsigned> argIndices, TypeRange argTypes,
-      ArrayRef<unsigned> resultIndices, TypeRange resultTypes) {
-      SmallVector<Type> argStorage, resultStorage;
-      TypeRange newArgTypes = insertTypesInto(
+    ::mlir::Type getTypeWithArgsAndResults(
+      ::llvm::ArrayRef<unsigned> argIndices, ::mlir::TypeRange argTypes,
+      ::llvm::ArrayRef<unsigned> resultIndices, ::mlir::TypeRange resultTypes) {
+      ::llvm::SmallVector<::mlir::Type> argStorage, resultStorage;
+      ::mlir::TypeRange newArgTypes = insertTypesInto(
           $_op.getArgumentTypes(), argIndices, argTypes, argStorage);
-      TypeRange newResultTypes = insertTypesInto(
+      ::mlir::TypeRange newResultTypes = insertTypesInto(
           $_op.getResultTypes(), resultIndices, resultTypes, resultStorage);
       return $_op.cloneTypeWith(newArgTypes, newResultTypes);
     }
@@ -342,24 +342,24 @@ def FunctionOpInterface : OpInterface<"FunctionOpInterface", [
     /// Return the type of this function without the specified arguments and
     /// results. This is used to update the function's signature in the
     /// `eraseArguments` and `eraseResults` methods.
-    Type getTypeWithoutArgsAndResults(
-      const BitVector &argIndices, const BitVector &resultIndices) {
-      SmallVector<Type> argStorage, resultStorage;
-      TypeRange newArgTypes = filterTypesOut(
+    ::mlir::Type getTypeWithoutArgsAndResults(
+      const ::llvm::BitVector &argIndices, const ::llvm::BitVector &resultIndices) {
+      ::llvm::SmallVector<::mlir::Type> argStorage, resultStorage;
+      ::mlir::TypeRange newArgTypes = filterTypesOut(
           $_op.getArgumentTypes(), argIndices, argStorage);
-      TypeRange newResultTypes = filterTypesOut(
+      ::mlir::TypeRange newResultTypes = filterTypesOut(
           $_op.getResultTypes(), resultIndices, resultStorage);
       return $_op.cloneTypeWith(newArgTypes, newResultTypes);
     }
-    Type getTypeWithoutArgs(const BitVector &argIndices) {
-      SmallVector<Type> argStorage;
-      TypeRange newArgTypes = filterTypesOut(
+    ::mlir::Type getTypeWithoutArgs(const ::llvm::BitVector &argIndices) {
+      ::llvm::SmallVector<::mlir::Type> argStorage;
+      ::mlir::TypeRange newArgTypes = filterTypesOut(
           $_op.getArgumentTypes(), argIndices, argStorage);
       return $_op.cloneTypeWith(newArgTypes, $_op.getResultTypes());
     }
-    Type getTypeWithoutResults(const BitVector &resultIndices) {
-      SmallVector<Type> resultStorage;
-      TypeRange newResultTypes = filterTypesOut(
+    ::mlir::Type getTypeWithoutResults(const ::llvm::BitVector &resultIndices) {
+      ::llvm::SmallVector<::mlir::Type> resultStorage;
+      ::mlir::TypeRange newResultTypes = filterTypesOut(
           $_op.getResultTypes(), resultIndices, resultStorage);
       return $_op.cloneTypeWith($_op.getArgumentTypes(), newResultTypes);
     }
@@ -369,88 +369,88 @@ def FunctionOpInterface : OpInterface<"FunctionOpInterface", [
     //===------------------------------------------------------------------===//
 
     /// Return all of the attributes for the argument at 'index'.
-    ArrayRef<NamedAttribute> getArgAttrs(unsigned index) {
-      return function_interface_impl::getArgAttrs($_op, index);
+    ::llvm::ArrayRef<::mlir::NamedAttribute> getArgAttrs(unsigned index) {
+      return ::mlir::function_interface_impl::getArgAttrs($_op, index);
     }
 
     /// Return an ArrayAttr containing all argument attribute dictionaries of
     /// this function, or nullptr if no arguments have attributes.
-    ArrayAttr getAllArgAttrs() { return $_op.getArgAttrsAttr(); }
+    ::mlir::ArrayAttr getAllArgAttrs() { return $_op.getArgAttrsAttr(); }
 
     /// Return all argument attributes of this function.
-    void getAllArgAttrs(SmallVectorImpl<DictionaryAttr> &result) {
-      if (ArrayAttr argAttrs = getAllArgAttrs()) {
-        auto argAttrRange = argAttrs.template getAsRange<DictionaryAttr>();
+    void getAllArgAttrs(::llvm::SmallVectorImpl<::mlir::DictionaryAttr> &result) {
+      if (::mlir::ArrayAttr argAttrs = getAllArgAttrs()) {
+        auto argAttrRange = argAttrs.template getAsRange<::mlir::DictionaryAttr>();
         result.append(argAttrRange.begin(), argAttrRange.end());
       } else {
         result.append($_op.getNumArguments(),
-                      DictionaryAttr::get(this->getOperation()->getContext()));
+                      ::mlir::DictionaryAttr::get(this->getOperation()->getContext()));
       }
     }
 
     /// Return the specified attribute, if present, for the argument at 'index',
     /// null otherwise.
-    Attribute getArgAttr(unsigned index, StringAttr name) {
+    ::mlir::Attribute getArgAttr(unsigned index, ::mlir::StringAttr name) {
       auto argDict = getArgAttrDict(index);
       return argDict ? argDict.get(name) : nullptr;
     }
-    Attribute getArgAttr(unsigned index, StringRef name) {
+    ::mlir::Attribute getArgAttr(unsigned index, ::llvm::StringRef name) {
       auto argDict = getArgAttrDict(index);
       return argDict ? argDict.get(name) : nullptr;
     }
 
     template <typename AttrClass>
-    AttrClass getArgAttrOfType(unsigned index, StringAttr name) {
+    AttrClass getArgAttrOfType(unsigned index, ::mlir::StringAttr name) {
       return ::llvm::dyn_cast_or_null<AttrClass>(getArgAttr(index, name));
     }
     template <typename AttrClass>
-    AttrClass getArgAttrOfType(unsigned index, StringRef name) {
+    AttrClass getArgAttrOfType(unsigned index, ::llvm::StringRef name) {
       return ::llvm::dyn_cast_or_null<AttrClass>(getArgAttr(index, name));
     }
 
     /// Set the attributes held by the argument at 'index'.
-    void setArgAttrs(unsigned index, ArrayRef<NamedAttribute> attributes) {
-      function_interface_impl::setArgAttrs($_op, index, attributes);
+    void setArgAttrs(unsigned index, ::llvm::ArrayRef<::mlir::NamedAttribute> attributes) {
+      ::mlir::function_interface_impl::setArgAttrs($_op, index, attributes);
     }
 
     /// Set the attributes held by the argument at 'index'. `attributes` may be
     /// null, in which case any existing argument attributes are removed.
-    void setArgAttrs(unsigned index, DictionaryAttr attributes) {
-      function_interface_impl::setArgAttrs($_op, index, attributes);
+    void setArgAttrs(unsigned index, ::mlir::DictionaryAttr attributes) {
+      ::mlir::function_interface_impl::setArgAttrs($_op, index, attributes);
     }
-    void setAllArgAttrs(ArrayRef<DictionaryAttr> attributes) {
+    void setAllArgAttrs(::llvm::ArrayRef<::mlir::DictionaryAttr> attributes) {
       assert(attributes.size() == $_op.getNumArguments());
-      function_interface_impl::setAllArgAttrDicts($_op, attributes);
+      ::mlir::function_interface_impl::setAllArgAttrDicts($_op, attributes);
     }
-    void setAllArgAttrs(ArrayRef<Attribute> attributes) {
+    void setAllArgAttrs(::llvm::ArrayRef<::mlir::Attribute> attributes) {
       assert(attributes.size() == $_op.getNumArguments());
-      function_interface_impl::setAllArgAttrDicts($_op, attributes);
+      ::mlir::function_interface_impl::setAllArgAttrDicts($_op, attributes);
     }
-    void setAllArgAttrs(ArrayAttr attributes) {
+    void setAllArgAttrs(::mlir::ArrayAttr attributes) {
       assert(attributes.size() == $_op.getNumArguments());
       $_op.setArgAttrsAttr(attributes);
     }
 
     /// If the an attribute exists with the specified name, change it to the new
     /// value. Otherwise, add a new attribute with the specified name/value.
-    void setArgAttr(unsigned index, StringAttr name, Attribute value) {
-      function_interface_impl::setArgAttr($_op, index, name, value);
+    void setArgAttr(unsigned index, ::mlir::StringAttr name, ::mlir::Attribute value) {
+      ::mlir::function_interface_impl::setArgAttr($_op, index, name, value);
     }
-    void setArgAttr(unsigned index, StringRef name, Attribute value) {
+    void setArgAttr(unsigned index, ::llvm::StringRef name, ::mlir::Attribute value) {
       setArgAttr(index,
-                 StringAttr::get(this->getOperation()->getContext(), name),
+                 ::mlir::StringAttr::get(this->getOperation()->getContext(), name),
                  value);
     }
 
     /// Remove the attribute 'name' from the argument at 'index'. Return the
     /// attribute that was erased, or nullptr if there was no attribute with
     /// such name.
-    Attribute removeArgAttr(unsigned index, StringAttr name) {
-      return function_interface_impl::removeArgAttr($_op, index, name);
+    ::mlir::Attribute removeArgAttr(unsigned index, ::mlir::StringAttr name) {
+      return ::mlir::function_interface_impl::removeArgAttr($_op, index, name);
     }
-    Attribute removeArgAttr(unsigned index, StringRef name) {
+    ::mlir::Attribute removeArgAttr(unsigned index, ::llvm::StringRef name) {
       return removeArgAttr(
-          index, StringAttr::get(this->getOperation()->getContext(), name));
+          index, ::mlir::StringAttr::get(this->getOperation()->getContext(), name));
     }
 
     //===------------------------------------------------------------------===//
@@ -458,102 +458,102 @@ def FunctionOpInterface : OpInterface<"FunctionOpInterface", [
     //===------------------------------------------------------------------===//
 
     /// Return all of the attributes for the result at 'index'.
-    ArrayRef<NamedAttribute> getResultAttrs(unsigned index) {
-      return function_interface_impl::getResultAttrs($_op, index);
+    ::llvm::ArrayRef<::mlir::NamedAttribute> getResultAttrs(unsigned index) {
+      return ::mlir::function_interface_impl::getResultAttrs($_op, index);
     }
 
     /// Return an ArrayAttr containing all result attribute dictionaries of this
     /// function, or nullptr if no result have attributes.
-    ArrayAttr getAllResultAttrs() { return $_op.getResAttrsAttr(); }
+    ::mlir::ArrayAttr getAllResultAttrs() { return $_op.getResAttrsAttr(); }
 
     /// Return all result attributes of this function.
-    void getAllResultAttrs(SmallVectorImpl<DictionaryAttr> &result) {
-      if (ArrayAttr argAttrs = getAllResultAttrs()) {
-        auto argAttrRange = argAttrs.template getAsRange<DictionaryAttr>();
+    void getAllResultAttrs(::llvm::SmallVectorImpl<::mlir::DictionaryAttr> &result) {
+      if (::mlir::ArrayAttr argAttrs = getAllResultAttrs()) {
+        auto argAttrRange = argAttrs.template getAsRange<::mlir::DictionaryAttr>();
         result.append(argAttrRange.begin(), argAttrRange.end());
       } else {
         result.append($_op.getNumResults(),
-                      DictionaryAttr::get(this->getOperation()->...
[truncated]

Copy link

⚠️ We detected that you are using a GitHub private e-mail address to contribute to the repo.
Please turn off Keep my email addresses private setting in your account.
See LLVM Discourse for more information.

…y namespace qualified

`extraSharedClassDeclaration` of `FunctionOpInterface` can be inherited by other
`OpInterfaces` into foreign namespaces, thus types must be fully
qualified to prevent compiler errors, for example:

    def MyFunc : OpInterface<"MyFunc", [FunctionOpInterface]> {
        let cppNamespace = "::MyNamespace";
    }
@shkoo shkoo force-pushed the nils/mlir-function-interfaces-namespace branch from eaad717 to 8d4df90 Compare February 22, 2024 20:29
@joker-eph
Copy link
Collaborator

LG but please fix the issue with the GitHub email obfuscation first.

@shkoo
Copy link
Contributor Author

shkoo commented Feb 23, 2024

LG but please fix the issue with the GitHub email obfuscation first.

As far as I can tell 8d4df90 has a real email address. Is there something else I need to do? Does the "github-actions" bot need to be rerun somehow?

@joker-eph
Copy link
Collaborator

This isn't about the commit but about your GitHub profile settings.

@joker-eph
Copy link
Collaborator

See the links posted by the bot

@joker-eph joker-eph merged commit 0d72fe9 into llvm:main Feb 23, 2024
@joker-eph
Copy link
Collaborator

Actually GitHub seemed happy using the right email address, so I'm not sure now...

@shkoo
Copy link
Contributor Author

shkoo commented Feb 23, 2024

Thanks! I don't have the "Keep my email addresses private" setting enabled. I'm guessing the bot thought I did because of the author line on eaad717aaed317bfced8d9bf6d2836bea9f6d475, which I corrected in 8d4df90

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants