This is an archive of the discontinued LLVM Phabricator instance.

[clang] retain type sugar in auto / template argument deduction
ClosedPublic

Authored by mizvekov on Sep 21 2021, 7:31 PM.

Details

Summary

This implements the following changes:

  • AutoType retains sugared deduced-as-type.
  • Template argument deduction machinery analyses the sugared type all the way

down. It would previously lose the sugar on first recursion.

  • Undeduced AutoType will be properly canonicalized, including the constraint

template arguments.

  • Remove the decltype node created from the decltype(auto) deduction.

As a result, we start seeing sugared types in a lot more test cases,
including some which showed very unfriendly type-parameter-*-* types.

Signed-off-by: Matheus Izvekov <mizvekov@gmail.com>

Diff Detail

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
mizvekov marked an inline comment as not done.Sep 27 2021, 10:11 AM
mizvekov added inline comments.
clang-tools-extra/clangd/unittests/InlayHintTests.cpp
470

Could you explain to me why the former behavior is more desirable here?

I initially understood that this hint should provide the type written in a form that would actually work if it replaced the 'auto'.
It is strange, but if it is just meant as a hint, it's still fine I guess.

Or maybe this was broken in the first place and just was just missing a FIXME?

nridge added inline comments.Sep 27 2021, 5:09 PM
clang-tools-extra/clangd/unittests/InlayHintTests.cpp
470

The type is being printed with a PrintingPolicy which has the non-default SuppressScope flag set.

The documentation of this flag says "Suppresses printing of scope specifiers", which I understand to mean both namespace and class scope specifiers.

If you're asking why we are using this flag for inlay hints, it's to keep the hints short so they don't take up too much horizontal space. Since the hints are displayed inline, hints that are too long can result in the line of code visually extending past the width of the editor window, and we try to minimize the impact of this.

mizvekov added inline comments.Sep 27 2021, 5:45 PM
clang-tools-extra/clangd/unittests/InlayHintTests.cpp
470

I see.

The problem is that the ElaboratedType sugar does not respect this when printing the nested name specifier it contains.
The quick fix of trying to make it respect it shows that there are a bunch of tests that expect it not to.
It seems that specific policy is used in places that need it to recover the type as written.

So if we want this behavior, we probably need to extend the printing policy with a new flag here.

nridge added inline comments.Sep 27 2021, 5:49 PM
clang-tools-extra/clangd/unittests/InlayHintTests.cpp
470

Thanks for the explanation!

I think this test expectation change is fine then, and would just suggest adding a "// FIXME: SuppressScope is not respected in this case" comment perhaps.

Thanks, this is very exciting, and the diagnostic changes in the test suite look great!

clang-tools-extra/clangd/unittests/ASTTests.cpp
189

Very true. But probably not worth keeping the comment. :)

clang-tools-extra/test/clang-tidy/checkers/cert-static-object-exception.cpp
276

What happened here? It looks like nothing this expression does can actually throw. (This lambda's operator() isn't noexcept, but the functions it calls all are.) Was this only avoiding a false positive by accident before?

clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-owning-memory.cpp
94–96

@JonasToth What was the intent here -- is there a specification for how gsl::owner is supposed to interact with auto deduction? That is, is this a bug fix or a regression?

clang/lib/Sema/SemaTemplateDeduction.cpp
250

I wonder if we can come up with a good heuristic for determining which of X and Y we want to return here. One thing that immediately springs to mind is: if either type is canonical, we should return the other one.

1051–1052

Hm, do we need the getUnqualifiedType() calls here? I'd expect that we'd have the TDF_IgnoreQualifiers flag set in this case, so we should be ignoring qualifiers anyway. Perhaps DeduceTemplateArgumentsByTypeMatch should be removing qualifiers itself if that flag is set?

1372

Par is an unusual name for a parameter type; Parm or Param would be more common and I'd find either of those to be clearer. (Ideally use the same spelling for Param and ParDesug.) Given that the description in the standard uses P and A as the names of these types, those names would be fine here too if you want something short.

1372–1384

Instead of tracking two types here, I think it'd be clearer to follow the more usual pattern in Clang: track only Param and Arg, use Arg->getAs<T>() instead of dyn_cast<T>(ArgDesug) to identify if Arg is sugar for a T, and use Arg->getAs<T>() instead of cast<T>(ArgDesug) to get a minimally-desugared T* from Arg.

The only place where I think we'd want something different from that is in the switch (Param->getTypeClass()) below, where I think we should switch on the type class of the canonical type (or equivalently on the type class of the desugared type, but it's cheaper and more obviously correct to ask for the type class of the canonical type).

1566–1567

Can we remove these? It looks like we're almost not using them at all, and that we shouldn't need to use them: any type matching should work equally well with the desugared type, and we never want to include these in our deduced results or our diagnostics.

1648–1649

This looks wrong to me: we should be comparing the types, not how they're written. Context.hasSameType(Param, Arg) (or Context.hasSameUnqualifiedType(Param, Arg) in the TDF_IgnoreQualifiers case) would be appropriate here.

1816–1817

Ignoring qualifiers here doesn't seem right to me. For example:

template<typename T> void f(T(T));
const int g(int);
void h() { f(g); }

... should result in a deduction failure, because we should get T = const int from the return type but T = int from the parameter type.

2311

Presumably this indentation change is unintended?

2383–2394

This suggests that we're passing in unresolved template arguments to this function. That seems problematic to me: those unresolved template arguments (as-written) will not be properly converted to the template's parameter type, will miss default arguments, and might not have had arguments properly combined into packs.

For *type* template arguments, I think it's fine to pass the argument as written if we're sure we know what it is, because basically no conversions apply for type template parameters and there may be meaningful sugar we want to preserve, but otherwise I think we should be passing in the resolved template argument.

4838

Hm, I see, adding the type sugar here presumably means that we get better diagnostic quality: we can complain that we can't deduce (eg) auto* against int instead of complaining that we can't deduce type-parameter-0-0* against int, right?

Can we remove the UseTypeSugar flag entirely now, or is it used for something else too?

clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp
565

It'd be nice to check that this deduces the right type for T. I think this case would also be useful to test:

template<class T> void foo0(fptr1<T>) {
   using ExpectedT = const char*; 
   using ExpectedT = T;
}
void bar0(const char *const volatile __restrict);
void t0() { foo0(&bar0); }
569

Interesting, GCC rejects this due to the restrict qualifier mismatch. But I think accepting is correct.

clang/test/SemaTemplate/attributes.cpp
127–128

Looks like we'll need to use a different mechanism to remove the type sugar in this test. (Maybe one day we'll run out of ways to remove type sugar and we won't need this test any more!)

Thanks for working on this! How hard would it be to support:

using size_t = __SIZE_TYPE__;
template<typename T> struct Id { typedef T type; };
int main() {
  struct S {} s;
  Id<size_t>::type f = s; // just 'unsigned long', 'size_t' sugar has been lost
}

Thanks for working on this!How hard would it be to support:

using size_t = __SIZE_TYPE__;
template<typename T> struct Id { typedef T type; };
int main() {
  struct S {} s;
  Id<size_t>::type f = s; // just 'unsigned long', 'size_t' sugar has been lost
}

Actually supporting that is in my radar :)

Getting the basic idea up is not too complicated.
We need to support 'alias' template specializations which can carry non-canonical arguments,
and they would just point to the canonical template specializations.
We would then have to implement our desugaring of 'SubstTemplateTypeParmType' so it's able to access some context where it can get the correctly sugared ReplacementType for the given replaced 'TemplateTypeParmType'.

But there are some little details that need to be implemented so that it is reasonably usable.
For example, in the general case we cannot currently, for any given declaration, figure out the template arguments used to instantiate it. We can do it for class and function templates, but not for alias and variable templates.
We need to make anything that carries template arguments also be a declaration context, so we can walk up from any declaration and see those.

There is also the trouble that some elements are not properly parented. For example in a requires clause:
template <class T> requires WHATEVER struct X {}, WHATEVER is not currently parented to the class template, so we would not be able to recover the instantiation arguments there unless we fixed it.

And who knows what other non-obvious problems might be lurking :)

A lot of what I said also overlaps with what we need to properly support lazy substitution, such as required by C++20 concepts, which is also something I am working on.

Getting the 'template argument deduction' machinery to preserve the sugar, which we do in this patch, certainly benefits when we get there.

Thanks, this is very exciting, and the diagnostic changes in the test suite look great!

Thank you, much appreciated, and also thanks for the review!

clang-tools-extra/test/clang-tidy/checkers/cert-static-object-exception.cpp
276

I haven't debugged it, I was hoping @aaron.ballman would know something about it and it would save me the trouble :)

clang/lib/Sema/SemaTemplateDeduction.cpp
250

I was thinking about the opposite actually, something along the lines of returning the common sugar between them. Starting from the cannonical type and going 'backwards' (adding back the sugar), we return the type just before adding sugar back would make them different.

1051–1052

The case I am worried about is checking the parameters in the function prototype case, and TDF_IgnoreQualifiers is neither set there, nor it would help if it was set anyway.
I will take a look again, but this was the simplest solution I could come up at the time.

1816–1817

Right, I must have simply assumed we had the same situation here with regards to function prototype parameters.

This feels like a standard defect to me, we should not be looking at top level qualifiers on return types :)

2311

arc lint decided to add it automatically, and I decided not to fight it :-)

2383–2394

Sure, I will revert that, presumably because this would be a separate can of worms we should not deal in this patch, but ideally we would get back to this so we can actually see some sugar here?

4838

The primary reason for it was moving in the direction of removing this flag.
We don't need it anymore in this case, although there are other users of it that need to be cleaned up. This one was just flipping the flag here, so I said why the heck not.
But other users are a bit more complicated, and I was afraid the patch was getting too big.

This could help improving the diagnostics in the future I suppose, but right now we are not using the result of the template argument deduction here very much, just for inconsistent deduction from initializer lists, the rest we defer to a generic variable 'foo' with type 'bar' has incompatible initializer of type 'baz'.

mizvekov added inline comments.Sep 30 2021, 3:59 PM
clang/lib/Sema/SemaTemplateDeduction.cpp
1648–1649

You are right, but the reason we don't get into any troubles here is because this is dead code anyway, the non-dependent case will always be handled above :)

Although perhaps, I wonder if we should dig down into non-dependent types anyway, in case the types are too complex and it's not immediately obvious what does not match, we could perhaps improve the diagnostic?

I will experiment a little bit with this idea.

mizvekov added inline comments.Sep 30 2021, 5:01 PM
clang/lib/Sema/SemaTemplateDeduction.cpp
1648–1649

Here are the results of this experiment:

error: 'note' diagnostics expected but not seen:
  File SemaCXX\cxx1z-noexcept-function-type.cpp Line 21: could not match 'void (I) noexcept(false)' (aka 'void (int) noexcept(false)') against 'void (int) noexcept'
error: 'note' diagnostics seen but not expected:
  File SemaCXX\cxx1z-noexcept-function-type.cpp Line 21: candidate template ignored: failed template argument deduction

error: 'note' diagnostics expected but not seen:
  File SemaCXX\deduced-return-type-cxx14.cpp Line 146: candidate template ignored: could not match 'auto ()' against 'int ()'
  File SemaCXX\deduced-return-type-cxx14.cpp Line 161: candidate template ignored: could not match 'auto ()' against 'void ()'
error: 'note' diagnostics seen but not expected:
  File SemaCXX\deduced-return-type-cxx14.cpp Line 146: candidate template ignored: could not match 'auto' against 'int'
  File SemaCXX\deduced-return-type-cxx14.cpp Line 161: candidate template ignored: could not match 'auto' against 'void'

error: 'note' diagnostics expected but not seen:
  File SemaCXX\pass-object-size.cpp Line 62 (directive at SemaCXX\pass-object-size.cpp:69): candidate address cannot be taken because parameter 1 has pass_object_size attribute
  File SemaCXX\pass-object-size.cpp Line 56 (directive at SemaCXX\pass-object-size.cpp:74): candidate address cannot be taken because parameter 1 has pass_object_size attribute
  File SemaCXX\pass-object-size.cpp Line 62 (directive at SemaCXX\pass-object-size.cpp:75): candidate address cannot be taken because parameter 1 has pass_object_size attribute
error: 'note' diagnostics seen but not expected:
  File SemaCXX\pass-object-size.cpp Line 56: candidate template ignored: failed template argument deduction
  File SemaCXX\pass-object-size.cpp Line 62: candidate template ignored: failed template argument deduction
  File SemaCXX\pass-object-size.cpp Line 62: candidate template ignored: failed template argument deduction

error: 'note' diagnostics expected but not seen:
  File SemaTemplate\deduction.cpp Line 316: deduced non-type template argument does not have the same type as the corresponding template parameter ('std::nullptr_t' vs 'int *')
  File SemaTemplate\deduction.cpp Line 323: values of conflicting types
error: 'note' diagnostics seen but not expected:
  File SemaTemplate\deduction.cpp Line 275: candidate template ignored: could not match 'const int' against 'int'
  File SemaTemplate\deduction.cpp Line 316: candidate template ignored: could not match 'int *' against 'std::nullptr_t'
  File SemaTemplate\deduction.cpp Line 323: candidate template ignored: could not match 'int *' against 'std::nullptr_t'

error: 'note' diagnostics expected but not seen:
  File SemaTemplate\explicit-instantiation.cpp Line 64: candidate template ignored: could not match 'void ()' against 'void (float *)'
  File SemaTemplate\explicit-instantiation.cpp Line 70: candidate template ignored: could not match 'void (int *)' against 'void (float *)'
error: 'note' diagnostics seen but not expected:
  File SemaTemplate\explicit-instantiation.cpp Line 70: candidate template ignored: could not match 'int' against 'float'
  File SemaTemplate\explicit-instantiation.cpp Line 64: candidate template ignored: failed template argument deduction

It's interesting to note that it reveals several cases we give too generic 'failed template argument deduction' errors, like the different noexcept specifications, function prototypes with different amount of parameters, and the 'pass_object_size attribute' case.

mizvekov updated this revision to Diff 376657.Oct 1 2021, 5:41 PM
  • Address most comments / requests.

Still missing:

  • Maybe do something about ASTImporter regression.
  • Debug change in behavior in cert-static-object-exception.cpp.
mizvekov marked 12 inline comments as done.Oct 1 2021, 5:54 PM
mizvekov added inline comments.
clang-tools-extra/clangd/unittests/ASTTests.cpp
189

Such a party pooper =)

clang/lib/Sema/SemaTemplateDeduction.cpp
1372–1384

There was one small catch in your suggestion:
Using getAs on TemplateSpecializationType is a bit tricky because that type can be sugar as well, so you might end up with a type alias instead of the underlying thing you wanted. I think that was the only tricky type though.
And it was productive that I ran into this problem, because I ended up discovering some cases where we were losing some sugar there, and there was a tiny diagnostic improvement in one of the test cases as a result.

mizvekov marked 2 inline comments as done.Oct 2 2021, 1:19 PM
mizvekov updated this revision to Diff 376724.Oct 2 2021, 4:03 PM
  • Fix issue with ASTMatcher only looking through one layer of DeducedType. This fix makes cert-static-object-exception.cpp not affected by this patch anymore.
mizvekov updated this revision to Diff 376725.Oct 2 2021, 4:51 PM
  • Fix ASTImporter regression.

Thanks for working on this!How hard would it be to support:

using size_t = __SIZE_TYPE__;
template<typename T> struct Id { typedef T type; };
int main() {
  struct S {} s;
  Id<size_t>::type f = s; // just 'unsigned long', 'size_t' sugar has been lost
}

Actually supporting that is in my radar :)

Over the years we had some interest from people but never actually got implemented. Here were some ideas @rsmith and I discussed over the years. If that is helpful, let me know if I should dig a bit more into private email exchange.

Over the years we had some interest from people but never actually got implemented. Here were some ideas @rsmith and I discussed over the years. If that is helpful, let me know if I should dig a bit more into private email exchange.

Sure, that is helpful :)

There is other lower hanging fruit where we are losing sugar, and it would be a shame if we implemented this but then did not get much benefit from it because the sugar never got into the template argument in the first place.

One such example is that we do not mark as 'elaborate' types which are written bare, without any scope specifiers.

So for example in a case like this:

namespace foo {
  struct Foo {};
  Foo x = 0;
};

We would still diagnose that assignment with the type of the variable printed as 'foo::Foo' instead of just 'Foo', as it was written, because the parser will have produced a type that is not wrapped in an ElaboratedType (or perhaps some other cheaper mechanism).

Over the years we had some interest from people but never actually got implemented. Here were some ideas @rsmith and I discussed over the years. If that is helpful, let me know if I should dig a bit more into private email exchange.

Sure, that is helpful :)

There is other lower hanging fruit where we are losing sugar, and it would be a shame if we implemented this but then did not get much benefit from it because the sugar never got into the template argument in the first place.

One such example is that we do not mark as 'elaborate' types which are written bare, without any scope specifiers.

So for example in a case like this:

namespace foo {
  struct Foo {};
  Foo x = 0;
};

We would still diagnose that assignment with the type of the variable printed as 'foo::Foo' instead of just 'Foo', as it was written, because the parser will have produced a type that is not wrapped in an ElaboratedType (or perhaps some other cheaper mechanism).

Handling that case is nice. I am more interested in retaining the sugar in template instantiations as it is essential for an optimization in our I/O system which uses clang as a library. If implement the instantiation diagnostic we can get rid of several hacky patches: https://github.com/vgvassilev/clang/commit/d87e2bbc8a266e295ee5a2065f1e587b325d4284 https://github.com/vgvassilev/clang/commit/fcc492fcab14e8b8dc156688dda6f237a04563a7 and https://github.com/vgvassilev/clang/commit/fe17b953325530267643f3391bfd59ac1519ef39

clang/lib/Sema/SemaTemplateDeduction.cpp
1452–1453
1855
mizvekov updated this revision to Diff 376855.Oct 4 2021, 4:01 AM
  • Address v.g.vassilev's suggestions.
mizvekov marked 2 inline comments as done.Oct 4 2021, 4:05 AM
craig.topper added inline comments.
clang/include/clang/ASTMatchers/ASTMatchersInternal.h
1032 ↗(On Diff #376855)

gcc 8.4.1 doesn't like T being the same name as a template parameter for this class.

Looks like this fixes PR51282.

mizvekov marked an inline comment as done.EditedOct 4 2021, 12:21 PM

Looks like this fixes PR51282.

Okay, interesting. This could be just papering over the bug.
I will take a careful look at it later.

clang/include/clang/ASTMatchers/ASTMatchersInternal.h
1032 ↗(On Diff #376855)

Oh wow, didn't even realize, thanks!!

mizvekov updated this revision to Diff 377006.Oct 4 2021, 12:40 PM
mizvekov marked an inline comment as done.
  • Rename variable so it doesn't shadow template parameter.

Looks like this fixes PR51282.

I guess it does fix it, but the underlying implementation of alignment is very fragile. The alignment is stored in AttributedType nodes, which are always sugar, so they are not considered part of the type in a structural sense...

Looks like this fixes PR51282.

I guess it does fix it, but the underlying implementation of alignment is very fragile. The alignment is stored in AttributedType nodes, which are always sugar, so they are not considered part of the type in a structural sense...

Unless I'm missing something, alignment on aliases is itself fragile. Let's say you have some template, then the instantiation with the alias is of course the same as the instantiation with the canonical type, so if that template has a member or local variable of that type parameter that won't get the alignment.

Unless I'm missing something, alignment on aliases is itself fragile. Let's say you have some template, then the instantiation with the alias is of course the same as the instantiation with the canonical type, so if that template has a member or local variable of that type parameter that won't get the alignment.

If the attribute was not just type sugar and could be made part of the canonical type, then the attribute would have to be attached to the canonical type of the alias when building it,
in a similar way to how you don't lose other structural parts of the type when canonicalizing it.

But then you would have to deal with the whole mess this would cause in the rest of clang :)

mizvekov added inline comments.Oct 7 2021, 10:01 AM
clang/lib/Sema/SemaTemplateDeduction.cpp
250

I have a proposal for what to do here in this patch: https://reviews.llvm.org/D111283

I think it's ready except that it needs more manual labor to support digging down into more types.

Anyway, this patch above refines the behavior of the present one, by implementing a criteria to fold the type sugar when deduction happens from multiple arguments,
or in the case of deducing from auto returning function, to fold them from the multiple return statements.

Otherwise, as in the present patch, the sugar is considered only from the first argument deduced from.

So any stakeholders, please subscribe and provide feedback about the rules as they are implemented :)

mizvekov updated this revision to Diff 378836.Oct 11 2021, 5:24 PM
mizvekov edited the summary of this revision. (Show Details)

rebase.

rsmith added inline comments.Oct 27 2021, 1:46 PM
clang/include/clang/AST/Type.h
4968
clang/lib/AST/ASTImporter.cpp
3275 ↗(On Diff #379153)
3285 ↗(On Diff #379153)

(Preparing for D112374)

clang/lib/Sema/SemaTemplateDeduction.cpp
1169

The == comparisons here and below should presumably be hasSameType comparisons now that we're preserving sugar.

1248–1264

I think we should:

  • track ToVisit as a vector of QualType, so we preserve the type sugar as written in base specifier lists
  • track Visited as a set of (canonical) const CXXRecordDecl*s rather than relying on RecordTypes always being canonical (they currently are, but I don't think that's intended to be guaranteed)
1422–1425

Do we need to use Context.getUnqualifiedArrayType here, for cases like:

typedef const int CInt;
// Partial ordering should deduce `T = int` from both parameters here,
// even though `T = const int` might make more sense for the first one.
template<int N> void f(CInt (&)[N], int*);
template<int N, typename T> void f(T (&)[N], T*);
CInt arr[5];
void g() { f(arr, arr); }

? I think getUnqualifiedType() on CInt[N] will not remove the indirect const qualifier.

1476

A could be sugar for an array type here.

1599–1617

I think we can excise CanA and CanP entirely here so people aren't tempted to use them and lose sugar.

1648–1649

Hm, this doesn't seem to be an improvement; I think it's better to present the larger non-matching types and use %diff in the diagnostic to highlight the part that differs. Especially because we don't provide a source location for (eg) where in the type we found a float, providing the more complete context of (eg) void (float*) seems more useful.

martong removed a subscriber: martong.Oct 28 2021, 1:07 AM
mizvekov updated this revision to Diff 383635.Oct 30 2021, 9:34 PM
mizvekov marked 10 inline comments as done.

Address rsmith's review comments.

mizvekov added inline comments.Oct 30 2021, 9:38 PM
clang/lib/AST/ASTImporter.cpp
3285 ↗(On Diff #379153)

Good catch, thanks :)

clang/lib/Sema/SemaTemplateDeduction.cpp
1422–1425

I see, that is true, if P and A could be sugared here, we would need getUnqualifiedArrayType.

But this issue had been sidestepped because we were not handling sugar in the PartialOrdering case, because that was only used in the isAtLeastAsSpecialized path where we were still using the canonical type, as we don't care about what types where actually deduced there.

I just changed it to not use the canonical type anymore and took this change.

1599–1617

Nice suggestion!

Though we must keep the last return as conditional, as I did there, otherwise we
break a test case such as the one from PR12132:

template<typename S> void fun(const int* const S::* member) {}
struct A { int* x; };
void foo() {
    fun(&A::x);
}

When ignoring qualifiers, hasSameUnqualifiedType being false is not enough to deduce a mismatch, so this is the one corner case where we analyze with a non-dependent parameter type.

That is what I tried to explain in that comment, but I have just reworded it to be clearer on this point.

mizvekov edited the summary of this revision. (Show Details)Oct 30 2021, 9:38 PM
rsmith accepted this revision.Nov 10 2021, 7:14 PM

Awesome!

clang/lib/Sema/SemaTemplateDeduction.cpp
574–576

I think we can retain type sugar here without too much effort.

576
579

Does this matter on the P side? (Can we remove this FIXME?)

581

Can we avoid using the Internal version here? (The only difference is whether we preserve top-level qualifiers that are outside any type sugar.)

Might also be worth a comment here explaining that we're going to the canonical type first here to step over any alias templates.

This revision is now accepted and ready to land.Nov 10 2021, 7:14 PM
mizvekov updated this revision to Diff 386674.Nov 11 2021, 2:52 PM
mizvekov marked 3 inline comments as done.

address last comments, one last CI push.

mizvekov marked an inline comment as done.Nov 11 2021, 2:54 PM
mizvekov added inline comments.
clang/lib/Sema/SemaTemplateDeduction.cpp
574–576

That does not work, blows up some 80 tests.
I will leave the FIXME and circle back to this after I am done with my pile of patches.

579

Even though P sugar is not relevant to the deduced type, it is relevant to diagnostics when there is a deduction failure. This has been relevant in the other parts of this patch, so I don't see why we would not at least want to preserve it here, though it might be too much trouble to be worth the effort.

I will keep the comment for now, if we have any discussion where it becomes clear there would be no difference here, I will make a one line NFC and remove it.

581

The Internal version is more convenient because TP is a Type * not a QualType, so the suggestion as you have written would not work.

Removing the alias template would be handled by getUnqualifiedDesugaredType, above, which my understanding is that will remove all top level sugar.

But I see that I went all this way around to preserving the non-canonical TP but all I needed really from it was the templateName, which probably is always the same as the canonical one, though not sure if that is by design.

I just made a change to get the canonical type on TP above instead, and we can circle back to this later.

phosek added a subscriber: phosek.Nov 11 2021, 10:17 PM

This change seems to have broken two libc++ tests:

libc++ :: std/utilities/any/any.nonmembers/any.cast/const_correctness.fail.cpp

Script:
--
: 'COMPILED WITH';  /b/s/w/ir/x/w/staging/llvm_build/./bin/clang++ /b/s/w/ir/x/w/llvm-project/libcxx/test/std/utilities/any/any.nonmembers/any.cast/const_correctness.fail.cpp -v --sysroot=/b/s/w/ir/x/w/cipd/linux --target=x86_64-unknown-linux-gnu -include /b/s/w/ir/x/w/llvm-project/libcxx/test/support/nasty_macros.h -nostdinc++ -I/b/s/w/ir/x/w/staging/llvm_build/include/x86_64-unknown-linux-gnu/c++/v1 -I/b/s/w/ir/x/w/staging/llvm_build/include/c++/v1 -I/b/s/w/ir/x/w/staging/llvm_build/runtimes/runtimes-x86_64-unknown-linux-gnu-bins/libcxx/include/c++build -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -I/b/s/w/ir/x/w/llvm-project/libcxx/test/support -std=c++2b -Werror -Wall -Wextra -Wshadow -Wundef -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-c++11-extensions -Wno-user-defined-literals -Wno-noexcept-type -Wno-atomic-alignment -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -D_LIBCPP_DISABLE_AVAILABILITY -fcoroutines-ts -Werror=thread-safety -Wuser-defined-warnings -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -Wno-macro-redefined -D_LIBCPP_HAS_THREAD_API_PTHREAD -Wno-macro-redefined -D_LIBCPP_ABI_VERSION=2  -fsyntax-only -Wno-error -Xclang -verify -Xclang -verify-ignore-unexpected=note -ferror-limit=0
--
Exit Code: 1

Command Output (stdout):
--
$ ":" "COMPILED WITH"
$ "/b/s/w/ir/x/w/staging/llvm_build/./bin/clang++" "/b/s/w/ir/x/w/llvm-project/libcxx/test/std/utilities/any/any.nonmembers/any.cast/const_correctness.fail.cpp" "-v" "--sysroot=/b/s/w/ir/x/w/cipd/linux" "--target=x86_64-unknown-linux-gnu" "-include" "/b/s/w/ir/x/w/llvm-project/libcxx/test/support/nasty_macros.h" "-nostdinc++" "-I/b/s/w/ir/x/w/staging/llvm_build/include/x86_64-unknown-linux-gnu/c++/v1" "-I/b/s/w/ir/x/w/staging/llvm_build/include/c++/v1" "-I/b/s/w/ir/x/w/staging/llvm_build/runtimes/runtimes-x86_64-unknown-linux-gnu-bins/libcxx/include/c++build" "-D__STDC_FORMAT_MACROS" "-D__STDC_LIMIT_MACROS" "-D__STDC_CONSTANT_MACROS" "-I/b/s/w/ir/x/w/llvm-project/libcxx/test/support" "-std=c++2b" "-Werror" "-Wall" "-Wextra" "-Wshadow" "-Wundef" "-Wno-unused-command-line-argument" "-Wno-attributes" "-Wno-pessimizing-move" "-Wno-c++11-extensions" "-Wno-user-defined-literals" "-Wno-noexcept-type" "-Wno-atomic-alignment" "-Wsign-compare" "-Wunused-variable" "-Wunused-parameter" "-Wunreachable-code" "-Wno-unused-local-typedef" "-D_LIBCPP_DISABLE_AVAILABILITY" "-fcoroutines-ts" "-Werror=thread-safety" "-Wuser-defined-warnings" "-D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER" "-Wno-macro-redefined" "-D_LIBCPP_HAS_THREAD_API_PTHREAD" "-Wno-macro-redefined" "-D_LIBCPP_ABI_VERSION=2" "-fsyntax-only" "-Wno-error" "-Xclang" "-verify" "-Xclang" "-verify-ignore-unexpected=note" "-ferror-limit=0"
# command stderr:
Fuchsia clang version 14.0.0 (https://llvm.googlesource.com/a/llvm-project e1d6f29a1e640e267e1d2b94d0d761e1d15e99bd)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /b/s/w/ir/x/w/staging/llvm_build/./bin
Found candidate GCC installation: /b/s/w/ir/x/w/cipd/linux/usr/lib/gcc/i586-linux-gnu/4.8
Found candidate GCC installation: /b/s/w/ir/x/w/cipd/linux/usr/lib/gcc/x86_64-linux-gnu/4.8
Selected GCC installation: /b/s/w/ir/x/w/cipd/linux/usr/lib/gcc/x86_64-linux-gnu/4.8
Candidate multilib: .;@m64
Selected multilib: .;@m64
 (in-process)
 "/b/s/w/ir/x/w/staging/llvm_build/bin/clang-14" -cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only -disable-free -clear-ast-before-backend -main-file-name const_correctness.fail.cpp -mrelocation-model static -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -v -fcoverage-compilation-dir=/b/s/w/ir/x/w/staging/llvm_build/runtimes/runtimes-x86_64-unknown-linux-gnu-bins/libcxx/test/std/utilities/any/any.nonmembers/any.cast -nostdinc++ -resource-dir /b/s/w/ir/x/w/staging/llvm_build/lib/clang/14.0.0 -include /b/s/w/ir/x/w/llvm-project/libcxx/test/support/nasty_macros.h -I /b/s/w/ir/x/w/staging/llvm_build/include/x86_64-unknown-linux-gnu/c++/v1 -I /b/s/w/ir/x/w/staging/llvm_build/include/c++/v1 -I /b/s/w/ir/x/w/staging/llvm_build/runtimes/runtimes-x86_64-unknown-linux-gnu-bins/libcxx/include/c++build -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -D __STDC_CONSTANT_MACROS -I /b/s/w/ir/x/w/llvm-project/libcxx/test/support -D _LIBCPP_DISABLE_AVAILABILITY -D _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D _LIBCPP_HAS_THREAD_API_PTHREAD -D _LIBCPP_ABI_VERSION=2 -isysroot /b/s/w/ir/x/w/cipd/linux -internal-isystem /b/s/w/ir/x/w/staging/llvm_build/lib/clang/14.0.0/include -internal-isystem /b/s/w/ir/x/w/cipd/linux/usr/local/include -internal-isystem /b/s/w/ir/x/w/cipd/linux/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../x86_64-linux-gnu/include -internal-externc-isystem /b/s/w/ir/x/w/cipd/linux/usr/include/x86_64-linux-gnu -internal-externc-isystem /b/s/w/ir/x/w/cipd/linux/include -internal-externc-isystem /b/s/w/ir/x/w/cipd/linux/usr/include -Werror -Wall -Wextra -Wshadow -Wundef -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-c++11-extensions -Wno-user-defined-literals -Wno-noexcept-type -Wno-atomic-alignment -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Werror=thread-safety -Wuser-defined-warnings -Wno-macro-redefined -Wno-macro-redefined -Wno-error -std=c++2b -fdeprecated-macro -fdebug-compilation-dir=/b/s/w/ir/x/w/staging/llvm_build/runtimes/runtimes-x86_64-unknown-linux-gnu-bins/libcxx/test/std/utilities/any/any.nonmembers/any.cast -ferror-limit 0 -fcoroutines-ts -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -verify -verify-ignore-unexpected=note -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -x c++ /b/s/w/ir/x/w/llvm-project/libcxx/test/std/utilities/any/any.nonmembers/any.cast/const_correctness.fail.cpp
clang -cc1 version 14.0.0 based upon LLVM 14.0.0git default target x86_64-unknown-linux-gnu
ignoring nonexistent directory "/b/s/w/ir/x/w/cipd/linux/usr/local/include"
ignoring nonexistent directory "/b/s/w/ir/x/w/cipd/linux/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../x86_64-linux-gnu/include"
ignoring nonexistent directory "/b/s/w/ir/x/w/cipd/linux/include"
#include "..." search starts here:
#include <...> search starts here:
 /b/s/w/ir/x/w/staging/llvm_build/include/x86_64-unknown-linux-gnu/c++/v1
 /b/s/w/ir/x/w/staging/llvm_build/include/c++/v1
 /b/s/w/ir/x/w/staging/llvm_build/runtimes/runtimes-x86_64-unknown-linux-gnu-bins/libcxx/include/c++build
 /b/s/w/ir/x/w/llvm-project/libcxx/test/support
 /b/s/w/ir/x/w/staging/llvm_build/lib/clang/14.0.0/include
 /b/s/w/ir/x/w/cipd/linux/usr/include/x86_64-linux-gnu
 /b/s/w/ir/x/w/cipd/linux/usr/include
End of search list.
error: 'error' diagnostics expected but not seen: 
  File /b/s/w/ir/x/w/staging/llvm_build/include/c++/v1/any Line * (directive at /b/s/w/ir/x/w/llvm-project/libcxx/test/std/utilities/any/any.nonmembers/any.cast/const_correctness.fail.cpp:37): cannot cast from lvalue of type 'const TestType' to rvalue reference type 'TestType &&'; types are not compatible
  File /b/s/w/ir/x/w/staging/llvm_build/include/c++/v1/any Line * (directive at /b/s/w/ir/x/w/llvm-project/libcxx/test/std/utilities/any/any.nonmembers/any.cast/const_correctness.fail.cpp:45): cannot cast from lvalue of type 'const TestType2' to rvalue reference type 'TestType2 &&'; types are not compatible
error: 'error' diagnostics seen but not expected: 
  File /b/s/w/ir/x/w/staging/llvm_build/include/c++/v1/any Line 605: cannot cast from lvalue of type 'typename remove_reference<const TestType>::type' (aka 'const TestType') to rvalue reference type 'TestType &&'; types are not compatible
  File /b/s/w/ir/x/w/staging/llvm_build/include/c++/v1/any Line 605: cannot cast from lvalue of type 'typename remove_reference<const TestType2>::type' (aka 'const TestType2') to rvalue reference type 'TestType2 &&'; types are not compatible
4 errors generated.

error: command failed with exit status: 1

--
libc++ :: std/utilities/any/any.nonmembers/any.cast/not_copy_constructible.fail.cpp

Script:
--
: 'COMPILED WITH';  /b/s/w/ir/x/w/staging/llvm_build/./bin/clang++ /b/s/w/ir/x/w/llvm-project/libcxx/test/std/utilities/any/any.nonmembers/any.cast/not_copy_constructible.fail.cpp -v --sysroot=/b/s/w/ir/x/w/cipd/linux --target=x86_64-unknown-linux-gnu -include /b/s/w/ir/x/w/llvm-project/libcxx/test/support/nasty_macros.h -nostdinc++ -I/b/s/w/ir/x/w/staging/llvm_build/include/x86_64-unknown-linux-gnu/c++/v1 -I/b/s/w/ir/x/w/staging/llvm_build/include/c++/v1 -I/b/s/w/ir/x/w/staging/llvm_build/runtimes/runtimes-x86_64-unknown-linux-gnu-bins/libcxx/include/c++build -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -I/b/s/w/ir/x/w/llvm-project/libcxx/test/support -std=c++2b -Werror -Wall -Wextra -Wshadow -Wundef -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-c++11-extensions -Wno-user-defined-literals -Wno-noexcept-type -Wno-atomic-alignment -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -D_LIBCPP_DISABLE_AVAILABILITY -fcoroutines-ts -Werror=thread-safety -Wuser-defined-warnings -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -Wno-macro-redefined -D_LIBCPP_HAS_THREAD_API_PTHREAD -Wno-macro-redefined -D_LIBCPP_ABI_VERSION=2  -fsyntax-only -Wno-error -Xclang -verify -Xclang -verify-ignore-unexpected=note -ferror-limit=0
--
Exit Code: 1

Command Output (stdout):
--
$ ":" "COMPILED WITH"
$ "/b/s/w/ir/x/w/staging/llvm_build/./bin/clang++" "/b/s/w/ir/x/w/llvm-project/libcxx/test/std/utilities/any/any.nonmembers/any.cast/not_copy_constructible.fail.cpp" "-v" "--sysroot=/b/s/w/ir/x/w/cipd/linux" "--target=x86_64-unknown-linux-gnu" "-include" "/b/s/w/ir/x/w/llvm-project/libcxx/test/support/nasty_macros.h" "-nostdinc++" "-I/b/s/w/ir/x/w/staging/llvm_build/include/x86_64-unknown-linux-gnu/c++/v1" "-I/b/s/w/ir/x/w/staging/llvm_build/include/c++/v1" "-I/b/s/w/ir/x/w/staging/llvm_build/runtimes/runtimes-x86_64-unknown-linux-gnu-bins/libcxx/include/c++build" "-D__STDC_FORMAT_MACROS" "-D__STDC_LIMIT_MACROS" "-D__STDC_CONSTANT_MACROS" "-I/b/s/w/ir/x/w/llvm-project/libcxx/test/support" "-std=c++2b" "-Werror" "-Wall" "-Wextra" "-Wshadow" "-Wundef" "-Wno-unused-command-line-argument" "-Wno-attributes" "-Wno-pessimizing-move" "-Wno-c++11-extensions" "-Wno-user-defined-literals" "-Wno-noexcept-type" "-Wno-atomic-alignment" "-Wsign-compare" "-Wunused-variable" "-Wunused-parameter" "-Wunreachable-code" "-Wno-unused-local-typedef" "-D_LIBCPP_DISABLE_AVAILABILITY" "-fcoroutines-ts" "-Werror=thread-safety" "-Wuser-defined-warnings" "-D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER" "-Wno-macro-redefined" "-D_LIBCPP_HAS_THREAD_API_PTHREAD" "-Wno-macro-redefined" "-D_LIBCPP_ABI_VERSION=2" "-fsyntax-only" "-Wno-error" "-Xclang" "-verify" "-Xclang" "-verify-ignore-unexpected=note" "-ferror-limit=0"
# command stderr:
Fuchsia clang version 14.0.0 (https://llvm.googlesource.com/a/llvm-project e1d6f29a1e640e267e1d2b94d0d761e1d15e99bd)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /b/s/w/ir/x/w/staging/llvm_build/./bin
Found candidate GCC installation: /b/s/w/ir/x/w/cipd/linux/usr/lib/gcc/i586-linux-gnu/4.8
Found candidate GCC installation: /b/s/w/ir/x/w/cipd/linux/usr/lib/gcc/x86_64-linux-gnu/4.8
Selected GCC installation: /b/s/w/ir/x/w/cipd/linux/usr/lib/gcc/x86_64-linux-gnu/4.8
Candidate multilib: .;@m64
Selected multilib: .;@m64
 (in-process)
 "/b/s/w/ir/x/w/staging/llvm_build/bin/clang-14" -cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only -disable-free -clear-ast-before-backend -main-file-name not_copy_constructible.fail.cpp -mrelocation-model static -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -v -fcoverage-compilation-dir=/b/s/w/ir/x/w/staging/llvm_build/runtimes/runtimes-x86_64-unknown-linux-gnu-bins/libcxx/test/std/utilities/any/any.nonmembers/any.cast -nostdinc++ -resource-dir /b/s/w/ir/x/w/staging/llvm_build/lib/clang/14.0.0 -include /b/s/w/ir/x/w/llvm-project/libcxx/test/support/nasty_macros.h -I /b/s/w/ir/x/w/staging/llvm_build/include/x86_64-unknown-linux-gnu/c++/v1 -I /b/s/w/ir/x/w/staging/llvm_build/include/c++/v1 -I /b/s/w/ir/x/w/staging/llvm_build/runtimes/runtimes-x86_64-unknown-linux-gnu-bins/libcxx/include/c++build -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -D __STDC_CONSTANT_MACROS -I /b/s/w/ir/x/w/llvm-project/libcxx/test/support -D _LIBCPP_DISABLE_AVAILABILITY -D _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D _LIBCPP_HAS_THREAD_API_PTHREAD -D _LIBCPP_ABI_VERSION=2 -isysroot /b/s/w/ir/x/w/cipd/linux -internal-isystem /b/s/w/ir/x/w/staging/llvm_build/lib/clang/14.0.0/include -internal-isystem /b/s/w/ir/x/w/cipd/linux/usr/local/include -internal-isystem /b/s/w/ir/x/w/cipd/linux/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../x86_64-linux-gnu/include -internal-externc-isystem /b/s/w/ir/x/w/cipd/linux/usr/include/x86_64-linux-gnu -internal-externc-isystem /b/s/w/ir/x/w/cipd/linux/include -internal-externc-isystem /b/s/w/ir/x/w/cipd/linux/usr/include -Werror -Wall -Wextra -Wshadow -Wundef -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-c++11-extensions -Wno-user-defined-literals -Wno-noexcept-type -Wno-atomic-alignment -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Werror=thread-safety -Wuser-defined-warnings -Wno-macro-redefined -Wno-macro-redefined -Wno-error -std=c++2b -fdeprecated-macro -fdebug-compilation-dir=/b/s/w/ir/x/w/staging/llvm_build/runtimes/runtimes-x86_64-unknown-linux-gnu-bins/libcxx/test/std/utilities/any/any.nonmembers/any.cast -ferror-limit 0 -fcoroutines-ts -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -verify -verify-ignore-unexpected=note -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -x c++ /b/s/w/ir/x/w/llvm-project/libcxx/test/std/utilities/any/any.nonmembers/any.cast/not_copy_constructible.fail.cpp
clang -cc1 version 14.0.0 based upon LLVM 14.0.0git default target x86_64-unknown-linux-gnu
ignoring nonexistent directory "/b/s/w/ir/x/w/cipd/linux/usr/local/include"
ignoring nonexistent directory "/b/s/w/ir/x/w/cipd/linux/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../x86_64-linux-gnu/include"
ignoring nonexistent directory "/b/s/w/ir/x/w/cipd/linux/include"
#include "..." search starts here:
#include <...> search starts here:
 /b/s/w/ir/x/w/staging/llvm_build/include/x86_64-unknown-linux-gnu/c++/v1
 /b/s/w/ir/x/w/staging/llvm_build/include/c++/v1
 /b/s/w/ir/x/w/staging/llvm_build/runtimes/runtimes-x86_64-unknown-linux-gnu-bins/libcxx/include/c++build
 /b/s/w/ir/x/w/llvm-project/libcxx/test/support
 /b/s/w/ir/x/w/staging/llvm_build/lib/clang/14.0.0/include
 /b/s/w/ir/x/w/cipd/linux/usr/include/x86_64-linux-gnu
 /b/s/w/ir/x/w/cipd/linux/usr/include
End of search list.
error: 'error' diagnostics expected but not seen: 
  File /b/s/w/ir/x/w/staging/llvm_build/include/c++/v1/any Line * (directive at /b/s/w/ir/x/w/llvm-project/libcxx/test/std/utilities/any/any.nonmembers/any.cast/not_copy_constructible.fail.cpp:48): static_cast from 'no_copy' to 'no_copy' uses deleted function
  File /b/s/w/ir/x/w/staging/llvm_build/include/c++/v1/any Line * (directive at /b/s/w/ir/x/w/llvm-project/libcxx/test/std/utilities/any/any.nonmembers/any.cast/not_copy_constructible.fail.cpp:52): static_cast from 'const no_copy' to 'no_copy' uses deleted function
error: 'error' diagnostics seen but not expected: 
  File /b/s/w/ir/x/w/staging/llvm_build/include/c++/v1/any Line 620: static_cast from 'typename remove_reference<no_copy>::type' (aka 'no_copy') to 'no_copy' uses deleted function
  File /b/s/w/ir/x/w/staging/llvm_build/include/c++/v1/any Line 605: static_cast from 'typename remove_reference<const no_copy>::type' (aka 'const no_copy') to 'no_copy' uses deleted function
4 errors generated.

error: command failed with exit status: 1

--

Would it be possible to take a look or revert the change?

bjope added a subscriber: bjope.Nov 12 2021, 1:02 AM

Obviously the error message was just extended by the as-written type. Could have just adapted the expected results instead of reverting...

Exactly what Aaron said :-)

Though might be a complication here if libcxx tests this with both ToT and stable clang, so could be perhaps this needs to be changed in a way that works in both.

I will check this much later today, having a busy day.

Adding Louis for the libcxx expertise.

Exactly what Aaron said :-)

Though might be a complication here if libcxx tests this with both ToT and stable clang, so could be perhaps this needs to be changed in a way that works in both.

I will check this much later today, having a busy day.

Adding Louis for the libcxx expertise.

Thanks for the heads up, I'm thinking about a fix right now. I'll paste the diff here and you can incorporate it to this review, which should trigger libc++ CI and make sure everything works.

We should probably start running the libc++ CI on Clang changes systematically, but I'm somewhat concerned about the bandwidth of our CI.

I think this should do it:

diff --git a/libcxx/test/std/utilities/any/any.nonmembers/any.cast/const_correctness.fail.cpp b/libcxx/test/std/utilities/any/any.nonmembers/any.cast/const_correctness.verify.cpp
similarity index 81%
rename from libcxx/test/std/utilities/any/any.nonmembers/any.cast/const_correctness.fail.cpp
rename to libcxx/test/std/utilities/any/any.nonmembers/any.cast/const_correctness.verify.cpp
index 234efc83423b..a28e3fc9d1d7 100644
--- a/libcxx/test/std/utilities/any/any.nonmembers/any.cast/const_correctness.fail.cpp
+++ b/libcxx/test/std/utilities/any/any.nonmembers/any.cast/const_correctness.verify.cpp
@@ -18,6 +18,13 @@
 
 // Try and cast away const.
 
+// This test only checks that we static_assert in any_cast when the
+// constraints are not respected, however Clang will sometimes emit
+// additional errors while trying to instantiate the rest of any_cast
+// following the static_assert. We ignore unexpected errors in
+// clang-verify to make the test more robust to changes in Clang.
+// ADDITIONAL_COMPILE_FLAGS: -Xclang -verify-ignore-unexpected=error
+
 #include <any>
 
 struct TestType {};
@@ -30,19 +37,15 @@ int main(int, char**)
 
     any a;
 
-    // expected-error@any:* {{drops 'const' qualifier}}
     // expected-error-re@any:* {{static_assert failed{{.*}} "ValueType is required to be a const lvalue reference or a CopyConstructible type"}}
     any_cast<TestType &>(static_cast<any const&>(a)); // expected-note {{requested here}}
 
-    // expected-error@any:* {{cannot cast from lvalue of type 'const TestType' to rvalue reference type 'TestType &&'; types are not compatible}}
     // expected-error-re@any:* {{static_assert failed{{.*}} "ValueType is required to be a const lvalue reference or a CopyConstructible type"}}
     any_cast<TestType &&>(static_cast<any const&>(a)); // expected-note {{requested here}}
 
-    // expected-error@any:* {{drops 'const' qualifier}}
     // expected-error-re@any:* {{static_assert failed{{.*}} "ValueType is required to be a const lvalue reference or a CopyConstructible type"}}
     any_cast<TestType2 &>(static_cast<any const&&>(a)); // expected-note {{requested here}}
 
-    // expected-error@any:* {{cannot cast from lvalue of type 'const TestType2' to rvalue reference type 'TestType2 &&'; types are not compatible}}
     // expected-error-re@any:* {{static_assert failed{{.*}} "ValueType is required to be a const lvalue reference or a CopyConstructible type"}}
     any_cast<TestType2 &&>(static_cast<any const&&>(a)); // expected-note {{requested here}}
 
diff --git a/libcxx/test/std/utilities/any/any.nonmembers/any.cast/not_copy_constructible.fail.cpp b/libcxx/test/std/utilities/any/any.nonmembers/any.cast/not_copy_constructible.verify.cpp
similarity index 81%
rename from libcxx/test/std/utilities/any/any.nonmembers/any.cast/not_copy_constructible.fail.cpp
rename to libcxx/test/std/utilities/any/any.nonmembers/any.cast/not_copy_constructible.verify.cpp
index 44a67f7aa03d..ec40eeeec11b 100644
--- a/libcxx/test/std/utilities/any/any.nonmembers/any.cast/not_copy_constructible.fail.cpp
+++ b/libcxx/test/std/utilities/any/any.nonmembers/any.cast/not_copy_constructible.verify.cpp
@@ -24,6 +24,13 @@
 
 // Test instantiating the any_cast with a non-copyable type.
 
+// This test only checks that we static_assert in any_cast when the
+// constraints are not respected, however Clang will sometimes emit
+// additional errors while trying to instantiate the rest of any_cast
+// following the static_assert. We ignore unexpected errors in
+// clang-verify to make the test more robust to changes in Clang.
+// ADDITIONAL_COMPILE_FLAGS: -Xclang -verify-ignore-unexpected=error
+
 #include <any>
 
 using std::any;
@@ -45,17 +52,14 @@ struct no_move {
 int main(int, char**) {
     any a;
     // expected-error-re@any:* {{static_assert failed{{.*}} "ValueType is required to be an lvalue reference or a CopyConstructible type"}}
-    // expected-error@any:* {{static_cast from 'no_copy' to 'no_copy' uses deleted function}}
     any_cast<no_copy>(static_cast<any&>(a)); // expected-note {{requested here}}
 
     // expected-error-re@any:* {{static_assert failed{{.*}} "ValueType is required to be a const lvalue reference or a CopyConstructible type"}}
-    // expected-error@any:* {{static_cast from 'const no_copy' to 'no_copy' uses deleted function}}
     any_cast<no_copy>(static_cast<any const&>(a)); // expected-note {{requested here}}
 
     any_cast<no_copy>(static_cast<any &&>(a)); // OK
 
     // expected-error-re@any:* {{static_assert failed{{.*}} "ValueType is required to be an rvalue reference or a CopyConstructible type"}}
-    // expected-error@any:* {{static_cast from 'typename remove_reference<no_move &>::type' (aka 'no_move') to 'no_move' uses deleted function}}
     any_cast<no_move>(static_cast<any &&>(a));
 
   return 0;

Use git apply -- in particular mind the renaming of the files.

ldionne reopened this revision.Nov 12 2021, 11:16 AM
This revision is now accepted and ready to land.Nov 12 2021, 11:16 AM
mizvekov updated this revision to Diff 386980.Nov 12 2021, 4:18 PM
  • Incorporating fixes for post-commit failures.

One last CI push, hopefully for real this time :-)

And thanks Louis, that fix is indeed much better!

(by the way git apply needs the --3way option to handle that rename nicely)

Herald added projects: Restricted Project, Restricted Project. · View Herald TranscriptNov 12 2021, 4:18 PM
Herald added a reviewer: Restricted Project. · View Herald Transcript
This revision now requires review to proceed.Nov 12 2021, 4:18 PM
mizvekov updated this revision to Diff 386984.Nov 12 2021, 4:25 PM

use --nolint this time, it seems to have flubbed the rename somehow.

Okay, libcxx pipeline passes, disregarding the clang-format failure for pre-existing badness in those files, which would need to be fixed separately or else git loses track of the rename.
The AIX failures also seem completely unrelated.

This revision was not accepted when it landed; it landed in state Needs Review.Nov 12 2021, 6:35 PM
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.

This broke building ANGLE as part of Qt 5.15 for a mingw target, with the following error:

../../../3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/ResourceManager11.cpp:532:38: error: explicit instantiation of 'allocate' does not refer to a function template, variable template, member function, member class, or static data member
ANGLE_RESOURCE_TYPE_OP(Instantitate, ANGLE_INSTANTIATE_OP)
                                     ^
../../../3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/ResourceManager11.h:301:15: note: candidate template ignored: could not match 'GetInitDataFromD3D11<T>' (aka 'typename InitDataType<ResourceTypeFromD3D11<type-parameter-0-0>::Value>::Value') against 'const D3D11_SUBRESOURCE_DATA' 
    gl::Error allocate(Renderer11 *renderer,
              ^
../../../3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/ResourceManager11.cpp:358:30: note: candidate template ignored: could not match 'GetInitDataFromD3D11<T>' (aka 'typename InitDataType<ResourceTypeFromD3D11<type-parameter-0-0>::Value>::Value') against 'const D3D11_SUBRESOURCE_DATA'
gl::Error ResourceManager11::allocate(Renderer11 *renderer,
                             ^

Do you happen to know what's going on here? The files mentioned are https://code.qt.io/cgit/qt/qtbase.git/tree/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/ResourceManager11.cpp?h=5.15.1 and https://code.qt.io/cgit/qt/qtbase.git/tree/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/ResourceManager11.h?h=5.15.1.

Thanks for the report!

Not immediately obvious to me, would need some time to isolate / create a minimal test case.
Would you be in a position to extract a preprocessed source file + command line which repros this error?

Also, feel free to revert if it helps you.

Thanks for the report!

Not immediately obvious to me, would need some time to isolate / create a minimal test case.
Would you be in a position to extract a preprocessed source file + command line which repros this error?

Sure: https://martin.st/temp/angle-preproc.cpp, built with clang -target x86_64-w64-mingw32 -std=c++17 -w -c angle-preproc.cpp.

Also, feel free to revert if it helps you.

No big hurry for me wrt that yet, at least until we know which part is at fault here.

(I haven't tested with the very latest Qt and/or ANGLE - the full build setup for this is rather complex.)

mizvekov reopened this revision.Nov 14 2021, 3:30 PM

Reverting for now.

mizvekov added a comment.EditedNov 15 2021, 8:13 AM

FYI this is a minimal repro taken from Martin's preprocessed source (Thanks!):

template <class> struct a { using b = const float; };
template <class c> using d = typename a<c>::b;  

template <class c> void e(d<c> *, c) {}
template void e(const float *, int);
mizvekov updated this revision to Diff 387376.Nov 15 2021, 1:10 PM
  • IsPossiblyOpaquelyQualifiedType should use canonical type.
  • Add test
bjope removed a subscriber: bjope.Nov 15 2021, 1:18 PM
ldionne accepted this revision.Nov 15 2021, 1:42 PM
ldionne removed a subscriber: ldionne.

LGTM from the libc++ perspective.

Unsubscribing to reduce spam, please ping me on Discord if you need further input. Thanks!

This revision is now accepted and ready to land.Nov 15 2021, 1:42 PM
This revision was landed with ongoing or failed builds.Nov 15 2021, 2:08 PM
This revision was automatically updated to reflect the committed changes.
hans added a subscriber: hans.Nov 16 2021, 11:06 AM

We're seeing a diagnostic change in Chromium which looks funny. For the following code (span<> is our own class):

int WontCompile() {
  const std::vector<int> v;
  span<int> s = make_span(v);
}

the diagnostic changes from:

fatal error: no viable conversion from 'span<const int, [...]>' to 'span<int, [...]>

to

fatal error: no viable conversion from 'span<T, [...]>' to 'span<int, [...]>

This looks very strange.

mizvekov added a comment.EditedNov 16 2021, 12:55 PM

We're seeing a diagnostic change in Chromium which looks funny. For the following code (span<> is our own class):
....
the diagnostic changes from:

fatal error: no viable conversion from 'span<const int, [...]>' to 'span<int, [...]>

to

fatal error: no viable conversion from 'span<T, [...]>' to 'span<int, [...]>

This looks very strange.

Hi! thanks for the report.

I am not sure how to reproduce this.

I tried mocking this situation:

template <class T> struct vector {};
template <class T, class=void> struct span {};

template <class T> auto make_span(const vector<T> &) { return span<const T>(); }

void WontCompile() {
  const vector<int> v;

  span<int> s1 = make_span(v);
}

Which I get:

error: no viable conversion from 'span<const int>' to 'span<int>'

Looking at the dump of that type:

AutoType 0x246b83676c0 'span<const int>' sugar
`-ElaboratedType 0x246b8366d60 'span<const int>' sugar
  `-TemplateSpecializationType 0x246b8366d20 'span<const int>' sugar span
    |-TemplateArgument type 'const int':'const int'
    | `-QualType 0x246b8366751 'const int' const
    |   `-SubstTemplateTypeParmType 0x246b8366750 'int' sugar
    |     |-TemplateTypeParmType 0x246b835c790 'T' dependent depth 0 index 0
    |     | `-TemplateTypeParm 0x246b835c740 'T'
    |     `-BuiltinType 0x246b82e81e0 'int'
    `-RecordType 0x246b8366d00 'struct span<const int>'
      `-ClassTemplateSpecialization 0x246b8366be8 'span'

In particular, the SubstTemplateTypeParmType desugars to int, not T, and I am not sure how you managed to get that ellipsis (from the type diff) on the template specialization arguments.

I am looking though at a clang built from D112374, which is main with other patches that have no effect here besides the addition of the ElaboratedType node on that dump.

hans added a comment.Nov 17 2021, 5:12 AM

I am not sure how to reproduce this.

Attaching preprocessed source. With Clang built at 508aa4fe0c82b3b409e2e194d591ee6d665c8623 it reproduces for me like this:

$ clang++ -c /tmp/a.ii
../../base/containers/span_unittest.cc:11:13: error: no viable conversion from 'span<T, [...]>' to 'span<int, [...]>'
  span<int> s = make_span(v);
            ^   ~~~~~~~~~~~~
../../base/containers/span.h:340:13: note: candidate constructor not viable: no known conversion from 'span<T, Extent::value>' (aka 'span<const int, Extent::value>') to 'const base::span<int, 18446744073709551615> &' for 1st argument
  constexpr span(const span& other) noexcept = default;
            ^
../../base/containers/span.h:303:13: note: candidate template ignored: could not match 'int[N]' against 'span<T, Extent::value>' (aka 'span<const int, Extent::value>')
  constexpr span(T (&array)[N]) noexcept : span(base::data(array), N) {}
            ^
../../base/containers/span.h:310:13: note: candidate template ignored: could not match 'array' against 'span'
  constexpr span(std::array<U, N>& array) noexcept
            ^
../../base/containers/span.h:317:13: note: candidate template ignored: could not match 'array' against 'span'
  constexpr span(const std::array<U, N>& array) noexcept
            ^
../../base/containers/span.h:328:13: note: candidate template ignored: requirement 'conjunction<base::negation<base::internal::IsSpanImpl<base::span<const int, 18446744073709551615>>>, base::negation<base::internal::IsStdArrayImpl<base::span<const int, 18446744073709551615>>>, base::negation<std::is_array<base::span<const int, 18446744073709551615>>>, std::is_convertible<const int (*)[], int (*)[]>, std::is_integral<unsigned long>>::value' was not satisfied [with Container = base::span<const int, 18446744073709551615>]
  constexpr span(Container& container) noexcept{F20424748}
            ^
../../base/containers/span.h:337:13: note: candidate template ignored: requirement 'conjunction<base::negation<base::internal::IsSpanImpl<base::span<const int, 18446744073709551615>>>, base::negation<base::internal::IsStdArrayImpl<base::span<const int, 18446744073709551615>>>, base::negation<std::is_array<const base::span<const int, 18446744073709551615>>>, std::is_convertible<const int (*)[], int (*)[]>, std::is_integral<unsigned long>>::value' was not satisfied [with Container = base::span<const int, 18446744073709551615>]
  constexpr span(const Container& container) noexcept
            ^
../../base/containers/span.h:350:13: note: candidate template ignored: requirement 'is_convertible<const int (*)[], int (*)[]>::value' was not satisfied [with U = const int, OtherExtent = 18446744073709551615]
  constexpr span(const span<U, OtherExtent>& other)
            ^
1 error generated.
hans added a comment.Nov 17 2021, 5:13 AM

Sorry, the attached file is here:

I am not sure how to reproduce this.

Attaching preprocessed source. With Clang built at 508aa4fe0c82b3b409e2e194d591ee6d665c8623 it reproduces for me like this:

$ clang++ -c /tmp/a.ii
../../base/containers/span_unittest.cc:11:13: error: no viable conversion from 'span<T, [...]>' to 'span<int, [...]>'
  span<int> s = make_span(v);
            ^   ~~~~~~~~~~~~
../../base/containers/span.h:340:13: note: candidate constructor not viable: no known conversion from 'span<T, Extent::value>' (aka 'span<const int, Extent::value>') to 'const base::span<int, 18446744073709551615> &' for 1st argument
  constexpr span(const span& other) noexcept = default;
            ^

Oh so that one was a curve ball!!!! T was not a template parameter, just a typedef, see the implementation of make_span in that preprocessed file:

template <int&... ExplicitArgumentBarrier, typename Container>
constexpr auto make_span(Container&& container) noexcept {
  using T =
      std::remove_pointer_t<decltype(base::data(std::declval<Container>()))>;
  using Extent = internal::Extent<Container>;
  return span<T, Extent::value>(std::forward<Container>(container));
}

So, some notes:

  • We should probably also do an AKA on the type diff.
  • Chromium should use a clearer name for that typedef perhaps? Though calling the first template parameter of span as T is common, maybe a better name here would be element_type.

Having a convenient way to just name a type like that also seems useful, so maybe the rename coupled with the AKA would be an improvement overall?

Otherwise, if we get too much trouble with with legacy codebases which cannot be updated with that simple rename, we could perhaps make the type printer be context sensitive, and when printing type nodes tied to NamedDecls, just step over those that refer to a name which is not accessible in the current context?

Isolated example:

template <class T, int=0> struct span {};

auto make_span() {
  using T = int;
  return span<const T, 0>();
}

void WontCompile() {
  span<int> s1 = make_span();
}

https://godbolt.org/z/rjd6Y6f9d

testcc:9:13: error: no viable conversion from 'span<const T, [...]>' to 'span<int, [...]>'
  span<int> s1 = make_span();
            ^    ~~~~~~~~~~~
test.cc:1:34: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'span<const T, 0>'
      (aka 'span<const int, 0>') to 'const span<int, 0> &' for 1st argument
template <class T, int=0> struct span {};
                                 ^
test.cc:1:34: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'span<const T, 0>'
      (aka 'span<const int, 0>') to 'span<int, 0> &&' for 1st argument
template <class T, int=0> struct span {};
lkail added a subscriber: lkail.Nov 17 2021, 6:35 PM

Hi we found regression in our internal tests. It compiles with clang-13.0.0 https://godbolt.org/z/3abGrcf7o and gcc https://godbolt.org/z/K9oj3Grs1, but fails with clang-trunk https://godbolt.org/z/1Tehxa1x9. Is it an intended change? If so, do we have to document this?

thakis added a subscriber: thakis.Nov 18 2021, 10:10 AM

So, some notes:

  • We should probably also do an AKA on the type diff.
  • Chromium should use a clearer name for that typedef perhaps? Though calling the first template parameter of span as T is common, maybe a better name here would be element_type.

Having a convenient way to just name a type like that also seems useful, so maybe the rename coupled with the AKA would be an improvement overall?

Haha :) Thanks for taking a look. aka for typedefs sounds like a nice approach to me.

Hi we found regression in our internal tests. It compiles with clang-13.0.0 https://godbolt.org/z/3abGrcf7o and gcc https://godbolt.org/z/K9oj3Grs1, but fails with clang-trunk https://godbolt.org/z/1Tehxa1x9. Is it an intended change? If so, do we have to document this?

Thanks for the report!

No it was not intended change, this patch should not affect partial ordering like that, modulo any bugs we might have accidentally fixed.
What happened here is that we lost a very small piece of code in the refactoring, which was introduced more than 10 years ago, but had no test coverage.
You just provided it though, and I am working on a patch to restore it shortly.

Haha :) Thanks for taking a look. aka for typedefs sounds like a nice approach to me.

Thanks, low on bandwidth right now but I will get to it :-)