Weird behavior of llvm::TypeSwitch on FunctionOpInterface argument

I am running into a behavior that I don’t quite understand in the following snippet:

llvm::TypeSwitch<FunctionOpInterface>(op)
    .Case<func::FuncOp, triton::FuncOp>(
        [&](auto op) {
          llvm::errs() << __PRETTY_FUNCTION__ << "\n";
          llvm::errs() << "  op name: " << op->getName() << "\n";
          auto boolToString = [](bool b) { return b ? "yes" : "no"; };
          llvm::errs() << "  isa<func::FuncOp>: "
                       << boolToString(llvm::dyn_cast<func::FuncOp>(
                              op.getOperation()))
                       << "\n";
          llvm::errs() << "  isa<triton::FuncOp>: "
                       << boolToString(llvm::dyn_cast<triton::FuncOp>(
                              op.getOperation()))
                       << "\n";
        })

On my machine, this produces the (to me) expected output:

// Run on a func::FuncOp:
auto (anonymous namespace)::ConvertTritonSPMDToFuncArgsPass::runOnOperation()::(anonymous class)::operator()(auto) const [gridFunc:auto = mlir::func::FuncOp]
  op name: func.func
  isa<func::FuncOp>: yes
  isa<triton::FuncOp>: no
// Run on a triton::FuncOp:
auto (anonymous namespace)::ConvertTritonSPMDToFuncArgsPass::runOnOperation()::(anonymous class)::operator()(auto) const [gridFunc:auto = mlir::triton::FuncOp]
  op name: tt.func
  isa<func::FuncOp>: no
  isa<triton::FuncOp>: yes

However, to my big surprise, the output is different in CI:

// Run on a func::FuncOp:
auto (anonymous namespace)::ConvertTritonSPMDToFuncArgsPass::runOnOperation()::(anonymous class)::operator()(auto) const [gridFunc:auto = mlir::func::FuncOp]
  gridFunc: func.func
  isa<func::FuncOp>: yes
  isa<triton::FuncOp>: no
// Run on a triton::FuncOp:
auto (anonymous namespace)::ConvertTritonSPMDToFuncArgsPass::runOnOperation()::(anonymous class)::operator()(auto) const [gridFunc:auto = mlir::func::FuncOp]
  kernelFunc: tt.func
  gridFunc: tt.func
  isa<func::FuncOp>: no
  isa<triton::FuncOp>: yes

Notice that the template parameter of the auto lambda is a func::FuncOp in both cases! In other words, the TypeSwitch calls the wrong function!

I have made the two systems as similar as I can. Both use the same LLVM code base; my machine has Clang 14.0.6, the CI has 14.0.0.

As an immediate solution, I have found a “fix” to my problem: if I change the first line of my snippet to the following:

llvm::TypeSwitch<Operation *>(op)

then all works as expected.

But I am worried that there is something fundamental that I don’t understand. To resolve this: can anyone explain why my CI system behaves as it does? Is that a bug, possibly one that was fixed between Clang 14.0.0 and 14.0.6? Or is there an expected difference between making a TypeSwitch<Operation *> and a TypeSwitch<FunctionOpInterface> (assuming both compile)?

Edit: Here are some CI runs in action: failing with FunctionOpInterface and succeeding with Operation *.