Skip to content
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

[build][tir] suppress -Woverloaded-virtual warning #13267

Merged
merged 1 commit into from
Nov 4, 2022

Conversation

cconvey
Copy link
Contributor

@cconvey cconvey commented Nov 2, 2022

Suppress a spurious -Woverloaded-virtual clang warning for the tvm::tir::DataTypeRewriter class.

I think we're safe to ignore clang version for this pragma, because it's been supported for a long time now.

@tvm-bot
Copy link
Collaborator

tvm-bot commented Nov 2, 2022

Thanks for contributing to TVM! Please refer to the contributing guidelines https://tvm.apache.org/docs/contribute/ for useful information and tips. Please request code reviews from Reviewers by @-ing them in a comment.

Generated by tvm-bot

@cconvey
Copy link
Contributor Author

cconvey commented Nov 2, 2022

CC: @Lunderberg

@@ -187,6 +187,11 @@ class DataTypeVisitor final : public StmtExprVisitor {
arith::ConstIntBoundAnalyzer::BoundMapType bound_;
};

#if __clang__
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of suppressing the warning altogether, could we instead add a using Parent::VisitStmt_; and using Parent::VisitExpr_? That would allow the parent's virtual functions to participate in overload resolution, avoiding the case that the warning is trying to catch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Maybe it should hinge on what the author (@vinx13) had in mind here w.r.t. the base-class overloads: hide them, or don't care?

@vinx13 : Can you clarify?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It only overrides some methods, for the rest it is intended to go to its parent class. I already add https://github.com/apache/tvm/blob/main/include/tvm/tir/stmt_functor.h#L530-L531 to its parent class, not sure why the warning still occurs.

Copy link
Contributor Author

@cconvey cconvey Nov 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think adding more overloads to the parent class changes the issue. Here's how I understand it (but I may be missing something...)

Suppose we have code like this:

#include <iostream>
using namespace std;

class BaseClass {
   public:
    virtual void foo(int x) {
        cout << "BaseClass::foo(int): x = " << x << endl;
    }   
};  

class DerivedClass : public BaseClass {
   public:
    virtual void foo(float x) {
        cout << "DerivedClass::foo(float): x = " << x << endl;
    }   
};  

int main() {
    DerivedClass d;
    d.foo(42);
}   

Many people would expect this to print BaseClass::foo(int): x = 42 because 42 is an int.

But what actually gets printed is DerivedClass::foo(float): x = 42. This is unintuitive for a lot of C++ programmers, which I think is why this warning was created.

IIUC, adding the using ... statements that @Lunderberg proposed would make this code behave more like you intended originally.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect it to only override virtual methods already available in DataTypeLegalizer or StmtExprMutator, instead of adding new overloads, maybe I'm missing something

Copy link
Contributor Author

@cconvey cconvey Nov 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, let me try making my point better with a slightly different example:

#include <iostream>
#include <string>
using namespace std;

class BaseClass {
   public:
    virtual void foo(int x) {
        cout << "BaseClass::foo(int): x = " << x << endl;
    }

    virtual void foo(string x) {
        cout << "BaseClass::foo(string): x = " << x << endl;
    }
};

class DerivedClass : public BaseClass {
   public:
#ifdef OVERLOAD_FOO
    virtual void foo(int x) {
         cout << "DerivedClass::foo(int): x = " << x << endl;
    }
#endif
};

int main() {
    DerivedClass d;
    d.foo("Hello, TVM!");
}

If I compile this with simply clang++ x.cpp, the compilation succeeds. And when I run the resulting executable, it prints BaseClass::foo(string): x = Hello, TVM!.

But if I try to compile this with clang++ -DOVERLOAD_FOO x.cpp, I get this error:

x.cpp:27:11: error: cannot initialize a parameter of type 'int' with an lvalue of type 'const char[12]'
    d.foo("Hello, TVM!");
          ^~~~~~~~~~~~~
x.cpp:19:26: note: passing argument to parameter 'x' here
    virtual void foo(int x) {
                         ^
1 error generated.

The issue, as I understand it, is that because DerviedClass provides even one override of foo, C++ will no longer look at any of the BaseClass::foo(...) overloads when it sees a call such as d.foo("Hello, TVM!") in the example above.

(However, those base-class functions aren't completely hidden. For example, if we use d.BaseClass::foo("Hello, TVM!") instead, the code compiles just fine.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation. I think it's common in the codebase to only override visitors for some IR nodes, for example https://github.com/apache/tvm/blob/main/src/tir/transforms/storage_rewrite.cc#L63. Any idea how it works?

Copy link
Contributor Author

@cconvey cconvey Nov 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry it took several examples to make my point :)

I think it's common in the codebase to only override visitors for some IR nodes, for example https://github.com/apache/tvm/blob/main/src/tir/transforms/storage_rewrite.cc#L63. Any idea how it works?

I think we'd need to look more closely at code that calls one of those methods to see what's going on. Two possibilities that come to mind are:

(a) We're doing something equivalent to this:

StmtExprVisitor * p = new LinearAccessPatternFinder(...);
p->VisitStmt_(...)

Which at compile time will search the VisitStmt_ overloads provided by StmtExprVisitor. (Although runtime dispatch will still use the vtable, and thus resolve to a suitable `LinearAccessPatternFinder method if it exists.)

If only code in one of the ancestor classes directly calls VisitStmt_, then maybe we could address this by making VisitStmt_ be protected rather than public? That might at least reduce the opportunities for this to bite people, although @Lunderberg 's using ... suggestion is even more robust AFAICT.

(b) We're unwittingly invoking the wrong overload provided by LinearAccessPatternFinder because C++ can automatically cast the argument to one of the overloads provided by LinearAccessPatternFinder. And TVM's testing just never noticed the mistake. (So, similar to what happens in that first example I gave, above: #13267 (comment))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(A little bit late to the discussion, but I hope this helps.)

I looked a bit into the warning, and it looks like part of it is because the DataTypeLegalizer defines the VisitStmt_ as public, whereas the StmtExprMutator defines them as protected. So the other instances of this pattern are overriding a protected method, which doesn't trigger the warning.

As example code. the warning is trying to avoid cases like the third one below, where the static type causes an unexpected overload set when calling a method on the derived type.

class Base {
public:
  virtual func(int x) {}
  virtual func(double x) {}
};

class Derived {
public:
  virtual func(double x) {}
};

int main() {
  // Static type is base, uses Base's vtable.
  std::unique_ptr<Base> base = std::make_unique<Base>();
  base->func(5); // Calls Base::func(int)

  // Static type is base, uses Derived's vtable.
  std::unique_ptr<Base> derived = std::make_unique<Dervied>();
  derived->func(5); // Calls Base::func(int)

  // Static type is Derived, uses Derived's vtable.  Overload
  // resolution selects `Derived::func(double)` because the overload
  // resolution set is based on the static type, and doesn't include
  // `Base::func(int)`.
  std::unique_ptr<Derived> derived = std::make_unique<Dervied>();
  derived->func(5); // Calls Derived::func(double)
}

@cconvey cconvey force-pushed the fix-overloaded-virtual-warning branch 3 times, most recently from 6b3e9c8 to 8f94ef4 Compare November 4, 2022 12:02
@cconvey
Copy link
Contributor Author

cconvey commented Nov 4, 2022

I've reworked this based on our discussion:

  • The warnings are eliminated via using statements, rather than pragmas.
  • I changed the methods from public to protected, to better match (AFAICT) @vinx13 's intent.

@Lunderberg @vinx13 : Ready for review.

Copy link
Contributor

@Lunderberg Lunderberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Can we apply a similar change in the DataTypeLegalizer? (Either as part of this PR or another.) It has the same issue of the VisitStmt_ methods being exposed, when external users should go through StmtExprMutator::operator() instead.

@cconvey
Copy link
Contributor Author

cconvey commented Nov 4, 2022

Can we apply a similar change in the DataTypeLegalizer? (Either as part of this PR or another.) It has the same issue of the VisitStmt_ methods being exposed, when external users should go through StmtExprMutator::operator() instead.

Thanks for the idea! It's not too much scope creep, so I'll add that to this PR.

- Address a (valid) warning from  clang-15.0.3 regarding the
  `tvm::tir::DataTypeRewriter` class.

- Make some class methods `protected` rather than `public`
  to better reflect authors' intent.
@cconvey cconvey force-pushed the fix-overloaded-virtual-warning branch from 8f94ef4 to 76a2b71 Compare November 4, 2022 13:28
@vinx13 vinx13 merged commit dec74cb into apache:main Nov 4, 2022
@cconvey cconvey deleted the fix-overloaded-virtual-warning branch November 6, 2022 15:21
xinetzone pushed a commit to daobook/tvm that referenced this pull request Nov 10, 2022
- Address a (valid) warning from  clang-15.0.3 regarding the
  `tvm::tir::DataTypeRewriter` class.

- Make some class methods `protected` rather than `public`
  to better reflect authors' intent.
xinetzone pushed a commit to daobook/tvm that referenced this pull request Nov 25, 2022
- Address a (valid) warning from  clang-15.0.3 regarding the
  `tvm::tir::DataTypeRewriter` class.

- Make some class methods `protected` rather than `public`
  to better reflect authors' intent.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants