Page MenuHomePhabricator

[Sema] Reject list-initialization of enumeration types from a brace-init-list containing a single element of a different scoped enumeration type
ClosedPublic

Authored by ahatanak on May 20 2022, 12:27 PM.

Details

Summary

It is rejected because it doesn't satisfy the condition that the element has to be implicitly convertible to the underlying type of the enum variable.

http://eel.is/c++draft/dcl.init.list#3.8

Diff Detail

Event Timeline

ahatanak created this revision.May 20 2022, 12:27 PM
Herald added a project: Restricted Project. · View Herald TranscriptMay 20 2022, 12:27 PM
ahatanak requested review of this revision.May 20 2022, 12:27 PM

The assertion assert(FromType->isIntegralOrUnscopedEnumerationType()) in StandardConversionSequence::getNarrowingKind fails when the invalid initialization is performed.

aaron.ballman added a reviewer: Restricted Project.May 25 2022, 10:28 AM

Please add more details to the summary and remove the rdar link (nobody outside of Apple can access that anyway). Also, this should have a release note for the bug fix.

clang/lib/Sema/SemaInit.cpp
4506

This doesn't match the comments immediately above here and I don't think is the correct fix.

We're handling this case: http://eel.is/c++draft/dcl.init.list#3.8

A scoped enumeration has a fixed underlying type (https://eel.is/c++draft/dcl.enum#5.sentence-5). The initializer list has a single element and that element can be implicitly converted to the underlying type (int in all of the test cases changed in this patch). And this is a direct initialization case, so I think we should be performing the conversion here rather than skipping to the next bullet.

ahatanak added inline comments.May 25 2022, 11:20 AM
clang/lib/Sema/SemaInit.cpp
4506

Can scoped enums be implicitly converted to integer types? Unscoped enums can be converted to an integer type, but I don't see any mention of scoped enums here: https://eel.is/c++draft/conv.integral

It seems that the original paper was trying to change the rules about conversions from the underlying type to a scoped enum. It doesn't look like it's allowing conversion from a scope enum to another scope enum.

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0138r2.pdf

aaron.ballman added inline comments.May 26 2022, 5:11 AM
clang/lib/Sema/SemaInit.cpp
4506

Can scoped enums be implicitly converted to integer types? Unscoped enums can be converted to an integer type, but I don't see any mention of scoped enums here: https://eel.is/c++draft/conv.integral

Correct, they cannot be implicitly converted to an integer.

It seems that the original paper was trying to change the rules about conversions from the underlying type to a scoped enum. It doesn't look like it's allowing conversion from a scope enum to another scope enum.

Agreed, however, I think where we want this to fail is below in the attempt at conversion. "v can be implicitly converted to U" is the part that should be failing here, and we're now skipping over the bit of code that's checking whether the implicit conversion is valid.

ahatanak added inline comments.Fri, May 27, 12:14 PM
clang/lib/Sema/SemaInit.cpp
4506

Is the code below checking whether the implicit conversion is valid? It looks like it's assuming the implicit conversion is valid and adding an implicit conversion sequence based on that assumption. If the source is an integer, unscoped enum, or floating type, the implicit conversion that is performed later should succeed except when there is narrowing.

Or are you suggesting we should add a check to Sema::PerformImplicitConversion that rejects conversions from scoped enums to other types? It seems to me that it's better to detect the error earlier.

ahatanak added inline comments.Fri, May 27, 12:30 PM
clang/lib/Sema/SemaInit.cpp
4506

Alternatively, we can emit a diagnostic in the code below that specifically calls out conversion from scoped enums to integer types.

aaron.ballman added inline comments.Sat, May 28, 5:38 AM
clang/lib/Sema/SemaInit.cpp
4506

Is the code below checking whether the implicit conversion is valid?

It's forming the conversion sequence as-if it must be valid, but that causes us to get the right diagnostics. We do the same for narrowing float conversions: https://github.com/llvm/llvm-project/blob/main/clang/lib/Sema/SemaInit.cpp#L4521 and I would expect us to then need changes so we get to here: https://github.com/llvm/llvm-project/blob/main/clang/lib/Sema/SemaInit.cpp#L8478

ahatanak added inline comments.Tue, May 31, 3:42 PM
clang/lib/Sema/SemaInit.cpp
4506

But a conversion from a scoped enum to another scoped enum or its underlying type isn't a narrowing conversion unless the conversion from the underlying type is narrowing. I guess the current code is forming the conversion sequence as if it is valid when the source type is a floating type just to call DiagnoseNarrowingInInitList. @rsmith, any comments?

If we want to detect the invalid conversion while performing conversion, shouldn't the call to PerformImplicitConversion, which is called before reaching the call to DiagnoseNarrowingInInitList, fail? Why should it succeed?

https://github.com/llvm/llvm-project/blob/7689c7fc9e08cc430daca3714bcffdd00fd538bd/clang/lib/Sema/SemaInit.cpp#L8467

But I think the invalid conversion should be detected at the very beginning of the function before conversion is attempted where it checks whether the initialization sequence is invalid (https://github.com/llvm/llvm-project/blob/7689c7fc9e08cc430daca3714bcffdd00fd538bd/clang/lib/Sema/SemaInit.cpp#L8020). That can be done by calling Sequence.SetFailed when the source type is a scoped enum.

ahatanak added inline comments.Tue, May 31, 3:47 PM
clang/lib/Sema/SemaInit.cpp
4506

Also, it's not clear to me why the diagnostic this patch emits (cannot initialize a variable of type 'test12::B' with an lvalue of type 'test12::A') isn't right. It's kind of generic, but it doesn't seem incorrect to me. What is the correct diagnostic in this case?

aaron.ballman added a subscriber: erichkeane.
aaron.ballman added inline comments.
clang/lib/Sema/SemaInit.cpp
4506

Given your example (but with names less likely to cause confusion):

enum class FirstEnum;
enum class SecondEnum;
FirstEnum FirstValue;
SecondEnum SecondValue{FirstValue};

Starting from recognizing that we're performing list initialization, we get to:

http://eel.is/c++draft/dcl.init.list#3.8

Based on our example, T is SecondEnum, U is int, v is FirstValue. The question then becomes can you implicitly convert FirstValue to int and the answer is no. The diagnostic we form in that case is "cannot initialize a variable of type '<type1>' with an lvalue of type '<type2>'". e.g., https://godbolt.org/z/an38EK3cs

So I think I was wrong; based on the comments on line 4508, it looks like we do *not* want to get into that if block but instead let the general single-element case below handle it. (I had missed that last sentence before and that turned out to be an important one.) Based on the diagnostic given when we do that (as your patch currently does), the diagnostic is what I'd expect us to generate.

I'm very sorry for the back and forth on this, but I *think* your patch is actually correct as-is. CC @erichkeane and @rsmith for a second opinion given that I already messed the logic up once before. :-)

ahatanak retitled this revision from [Sema] Reject implicit conversions between different scoped enum types in list initialization to [Sema] Reject list-initialization of a scoped enum variable from an element of a different scoped enum type.Wed, Jun 1, 3:31 PM
ahatanak edited the summary of this revision. (Show Details)
ahatanak added inline comments.
clang/lib/Sema/SemaInit.cpp
4506

No problem and thank you for the detailed explanation of the rules in the standard. I've updated the summary based on the discussion we had.

aaron.ballman accepted this revision.Thu, Jun 2, 8:36 AM

LGTM! Can you also add a release note about the fix before landing the changes?

This revision is now accepted and ready to land.Thu, Jun 2, 8:36 AM
ahatanak retitled this revision from [Sema] Reject list-initialization of a scoped enum variable from an element of a different scoped enum type to [Sema] Reject list-initialization of enumeration types from a brace-init-list containing a single element of a different scoped enumeration type.Thu, Jun 2, 5:18 PM
ahatanak updated this revision to Diff 433922.Thu, Jun 2, 5:20 PM

Fix the comment to match the code.

This revision was landed with ongoing or failed builds.Thu, Jun 2, 5:26 PM
This revision was automatically updated to reflect the committed changes.