This is an archive of the discontinued LLVM Phabricator instance.

Move mlir LogicalResult to llvm Support library
AbandonedPublic

Authored by ezhulenev on Jul 27 2022, 9:37 AM.

Details

Summary

Inconsistent use of boolean to report success or failure in the LLVM code base was mentioned couple of times, one example: https://lists.llvm.org/pipermail/llvm-dev/2018-September/126182.html

S-u-m-m-a-r-y:

  1. mlir::LogicalResult moved to llvm::LogicalResult (llvm/Support/LogicalResult.h)
  2. mlir::FailureOr moved to llvm::FailureOr
  3. mlir defines aliases to bring them back into the mlir namespace
  4. mlir::ParseResult stays in the mlir/Support/LogicalResult.h because it's very MLIR specific

Diff Detail

Event Timeline

ezhulenev created this revision.Jul 27 2022, 9:37 AM
ezhulenev requested review of this revision.Jul 27 2022, 9:37 AM
Herald added a reviewer: herhut. · View Herald Transcript
Herald added projects: Restricted Project, Restricted Project. · View Herald Transcript
ezhulenev edited the summary of this revision. (Show Details)Jul 27 2022, 9:40 AM
kuhar added a subscriber: kuhar.Jul 27 2022, 9:53 AM

+1, I like this direction. bool return values confused me numerous times in functions like check*, verify*, etc., and many places in LLVM would benefit from this.

ezhulenev edited the summary of this revision. (Show Details)Jul 27 2022, 10:10 AM

Adding some non-MLIR folks to review the addition to LLVM libSupport (@dexonsmith and @dblaikie )

I mean this looks fine to me, but would love review from those Mehdi linked.

llvm/include/llvm/Support/LogicalResult.h
76

The formatting here looks off.

102

I know some downstream users would benefit from this, not sure how that would manifest (rename or something) if we find it desirable.

SGTM in general (I like the idea) but I don’t have time to look in detail or think hard about it for at least a few weeks (no need to wait for me).

CC: @lhames as well (author of Error/Expected).

Roll back some of formatting changes

ezhulenev added inline comments.Jul 27 2022, 11:54 AM
llvm/include/llvm/Support/LogicalResult.h
76

That's how the arcanist reformats the code once I upload it

rriddle added inline comments.Jul 27 2022, 12:29 PM
llvm/include/llvm/Support/LogicalResult.h
76

Weird, oh well. Do whatever the formatter tells you.

dblaikie added inline comments.Jul 27 2022, 12:44 PM
llvm/include/llvm/Support/LogicalResult.h
96

Seems unfortunate that this type (FailureOr) will have different API than ErrorOr though - any chance this could keep an explicit operator bool?

& making something private that's inherited is pretty awkward - since you can still access the private things via a base reference, or slicing, etc.

I guess alternatively, maybe ErrorOr ends up with operator LogicalResult instead of explicit operator bool? (@lhames )

ezhulenev added inline comments.Jul 27 2022, 1:16 PM
llvm/include/llvm/Support/LogicalResult.h
96

If I got the history correctly that's a very deliberate decision (cc @rriddle), only the mlir::ParseResult allows implicit casting to bool, because it's intended for use in a very narrow context of parsing. Because if FailureOr would have implicit casting it would be another source of confusion, I myself always double check the rules for casting llvm::Error and llvm::Expected.

rriddle added inline comments.Jul 27 2022, 1:21 PM
llvm/include/llvm/Support/LogicalResult.h
96

Yeah, no conversions to bool was an explicit choice. Alongside LogicalResult, we wanted something that is explicit about bool treatment.

The choice of using Optional is the base class was just convenience, we could change to something cleaner.

dblaikie added inline comments.Jul 27 2022, 1:26 PM
llvm/include/llvm/Support/LogicalResult.h
96

Oh, no doubt it was deliberate - but I guess to summarize: given the similarities with Error/ErrorOr, it seems important these have similar/the same API in this regard. So I think it's important to at least have a plan everyone agrees to about how these'll all end up the same in relatively short order before this gets added to LLVM. (& ideally, avoid even temporary divergence if practical/if some of it can be frontloaded (I guess that'd involve introducing LogicalResult first, using that in ErrorOr, then introducing FailureOr at that point since it would be consistent at that point))

rriddle added inline comments.Jul 27 2022, 3:08 PM
llvm/include/llvm/Support/LogicalResult.h
96

I don't think FailureOr should have any kind of boolean conversion (whether implicit/explicit). I'd be fine with aligning on the other API though, of which it only seems like ErrorOr has operator*/operator->/get that would be shared (there doesn't seem to be any other non-Error related API on ErrorOr). Given the use of Optional, they are already kind of aligned there. I suppose to make them truly aligned, we'd need to drop various methods inherited from Optional.

dblaikie added inline comments.Jul 27 2022, 3:15 PM
llvm/include/llvm/Support/LogicalResult.h
96

Mostly the boolean conversion I think should be consistent between them - both or neither. It'd be pretty awkward if we have lots of code that does:

if (ErrorOr<T> T = func())

(which we do)
and then FailureOr can't be used in a similar way - that'll be pretty jarring for developers to have to handle these two things so fundamentally differently. I think this usage is pretty important & it'd be a loss to break all that ErrorOr usage - so I hope there's some way to align these while preserving such usage, but that ultimately (I think) boils down to at least an explicit conversion to bool. Which would mean we're at an impass.

rriddle added inline comments.Jul 27 2022, 3:21 PM
llvm/include/llvm/Support/LogicalResult.h
96

The point of the LogicalResult stuff is to not have boolean conversions. If the requirement for moving into LLVM Support is to have them, I suppose we shouldn't pull any of this into LLVM.

dblaikie added inline comments.Jul 27 2022, 3:32 PM
llvm/include/llvm/Support/LogicalResult.h
96

For FailureOr in particular, though - what're the risks of explicit conversion to bool that the absence is intended to mitigate?

rriddle added inline comments.Jul 27 2022, 5:26 PM
llvm/include/llvm/Support/LogicalResult.h
96

We don't want to allow things like:

FailureOr<Foo> someMethod();

...

if (someMethod()) {
}
if (!someMethod()) {
}
ezhulenev added inline comments.Jul 27 2022, 5:43 PM
llvm/include/llvm/Support/LogicalResult.h
96

Indeed it's the whole purpose of LogicalResult: a thing that is not implicitly convertible to bool. Of course returning a type instead of a bool is better, because the semantics of boolean conversion is defined in one place, but that was not the reason why original LogicalResult was added, Error or ErrorOr would work just fine.

dblaikie added inline comments.Jul 27 2022, 6:09 PM
llvm/include/llvm/Support/LogicalResult.h
96

Ah, Error/ErrorOr sort of avoid that problem because the error itself must be inspected, but that's a runtime failure (which still means if the error path is untested the boolean check is sufficient/the failure to handle the error is unchecked). We wouldn't even have that backstop with FailureOr if it weren't for the syntactic difference you're suggesting/have.

I'd feel less bad about this (& maybe even encourage Error/ErrorOr to move away from (even explicit) conversion to bool in favor of an explicit "success" check) if we were already using C++17 if-with-initializers https://www.tutorialspoint.com/cplusplus17-if-statement-with-initializer though that's still a bit awkward to write when the initializer is long & then after that you have to do the success check.

Are there particular cases where LogicalResult and FailureOr are intended to be used? Or is the intent/possibility that they become ubiquitous/replace all boolean success/failure returns in the LLVM project? If there's a narrow enough subset of cases we can say they're suitable for and others they aren't I'd be less worried about the awkward ergonomics of having to explicitly test success.

@lhames is out at the moment I think, but I'll be curious to hear his thoughts next week, hopefully.

ezhulenev added inline comments.Jul 27 2022, 6:15 PM
llvm/include/llvm/Support/LogicalResult.h
96

I'd say that every function that returns bool to signal success/failure should use LogicalResult, and every function that is happy with Error/ErrorOr should continue using them. LogicalResult is a huge productivity boost when you use not very familiar APIs, because you don't have to double check what is the semantics of the return booleans.

But I'm not familiar with LLVM code base to say if there are any concrete use cases that will benefit from it today.

lhames added inline comments.Aug 8 2022, 12:06 PM
llvm/include/llvm/Support/LogicalResult.h
25

LogicalResult seems like a misnomer -- isn't bool already a logical result?

If possible, I think you want a name that conveys that this is specifically about success/failure.

96

@lhames is out at the moment I think, but I'll be curious to hear his thoughts next week, hopefully.

I really like the idea of replacing bool with LogicalResult for fallible functions, since (even if you did allow boolean conversion) the return type tells you how to interpret its values.

The lack of boolean conversion is concerning though, especially on FailureOr. If we tried to harmonize all of these APIs (Optional, Error, Expected, FailureOr, ...) by removing implicit conversion from the existing utilities I think that it would add a lot of boilerplate around if checks (even if we do use if-with-initializers). If we don't harmonize the APIs then we're increasing the number of idioms that people need to be familiar with, and in that case it's not clear that the benefits would outweigh the costs.

I'm trying to think of a way that we could extend the succeeded and failed functions to make them applicable to Error and Expected in a way that would allow us to harmonize with FailureOr, but haven't come up with anything yet.

ezhulenev abandoned this revision.Aug 23 2022, 3:17 PM

Abandoning this change