This is an archive of the discontinued LLVM Phabricator instance.

[Concepts] Constrained partial specializations and function overloads.
ClosedPublic

Authored by saar.raz on Jan 10 2018, 10:43 AM.

Details

Summary

Added support for constraint satisfaction checking and partial ordering of constraints in constrained partial specialization and function template overloads. Depends on D41569.

Diff Detail

Event Timeline

saar.raz created this revision.Jan 10 2018, 10:43 AM
saar.raz updated this revision to Diff 137908.Mar 10 2018, 4:04 AM
  • Correct handling of non-substitutable concept specialization expressions.
saar.raz updated this revision to Diff 147187.May 16 2018, 2:54 PM
  • Adjusted constraint normalization to piecewise constraint substitution
  • Normalized constraints are now represented as described in the standard (a non-substituted expression along with a list of "template arguments").
saar.raz updated this revision to Diff 159323.Aug 6 2018, 9:49 AM

Adjusted to switch to ASTTemplateArgumentListInfo

saar.raz updated this revision to Diff 159331.Aug 6 2018, 10:24 AM
  • Fix bad handling of checking of deduced arguments in function templates
saar.raz updated this revision to Diff 159759.Aug 8 2018, 11:08 AM
  • Moved constraint checks to the end of FinishTemplateArgumentDeduction
Herald added a project: Restricted Project. · View Herald TranscriptApr 12 2019, 4:32 PM
saar.raz updated this revision to Diff 195010.Apr 13 2019, 4:37 AM

Fixed bug in normalization substitution into CSEs
Rebase onto trunk

martong requested changes to this revision.Apr 16 2019, 1:34 AM
martong added a reviewer: balazske.
martong added inline comments.
lib/AST/ASTImporter.cpp
5026 ↗(On Diff #195010)

ClassTemplate is in the "to" context, i.e. it is already imported.
PartialSpec is in the "from" context, not yet imported.
Thus, findPartialSpecialization is going search for a specialization in the "to" context but with constraints which are in the "from" context. So I suppose this will not find any match.
So, first you have to import the associated constraints expression and then use that in the search.

Something like:

ExpectedExpr ConstraintsOrErr = import(D->getAssociatedConstraints());
if (!ConstraintsOrErr)
  return ConstraintsOrErr.takeError();
// ...
ClassTemplate->findPartialSpecialization(TemplateArgs,
          *ConstraintsOrErr, InsertPos);

(The same below.)

This revision now requires changes to proceed.Apr 16 2019, 1:34 AM
balazske added inline comments.Apr 16 2019, 6:32 AM
lib/AST/ASTImporter.cpp
5096 ↗(On Diff #195010)

This looks correct: PartSpec is the new instance (but this is not the best name for it, maybe PartialSpec2?).
But: How are the "associated constraint" expressions imported (stored somewhere in the To context)?

saar.raz updated this revision to Diff 196106.Apr 22 2019, 11:46 AM
  • Fixed importing of ACs when importing partial specs.
  • Adjust to getAssociatedConstraints interface change
saar.raz updated this revision to Diff 196107.Apr 22 2019, 11:51 AM

Renamed PartSpec to PartSpec2 (CR)

martong accepted this revision.Apr 23 2019, 7:50 AM

From the ASTImporter point of view it looks good to me, thanks for the changes!

This revision is now accepted and ready to land.Apr 23 2019, 7:50 AM
martong resigned from this revision.Apr 23 2019, 7:50 AM
This revision now requires review to proceed.Apr 23 2019, 7:50 AM
saar.raz marked an inline comment as done.Oct 14 2019, 2:28 PM
rsmith added a comment.Nov 1 2019, 4:04 PM

(Didn't finish the review, but I have to run now.)

include/clang/AST/DeclTemplate.h
770

ProfileArguments &&...ProfileArgs?

2024–2040

Hmm, is this the right set of things to be profiling? The relevant rule should be that the template-heads are equivalent and the simple-template-ids are equivalent, and checking the associated constraints isn't sufficient to check the former. Should we be profiling the template parameter list and template arguments instead?

include/clang/Basic/DiagnosticSemaKinds.td
2426–2427

Please don't join sentences with periods like this; it looks strange given that we generally render diagnostics in lowercase, and not as full sentences). I'm also not sure what the second sentence here is telling the user. (What do you mean by "any"? "some", or "all", or substitutions the program requires, or something else?)

We generally use "while" not "when" for these "here's what I was doing" notes.

lib/Sema/SemaConcept.cpp
447–449

The specification says we substitute into the parameter mapping bottom-up (starting with atomic constraints and then substituting as they get used in enclosing constraints), but you're substituting top-down. That's not equivalent: specifically, the rules say that we should not perform substitution for parameters that are not used in the expansion of a concept ([temp.constr.atomic]p1: "[...] a mapping from the template parameters that appear within E to template arguments involving the [...]"). Example:

template<typename T> concept True = true;
template<typename T> concept Foo = True<T*>;
template<typename T> concept Bar = Foo<T&>;
template<typename T> void f() requires Bar<T>;

Here, the true atomic constraint has no parameter mappings, so we should never perform the substitution that would form T&*, and so shouldn't reject this for forming a pointer to a reference.

I think it's sufficient to first determine which parameters are used in a concept (perhaps when the concept is defined), and then form a parameter mapping top-down skipping the unused arguments, but you need to be careful to do that recursively (Bar doesn't use its T because Foo doesn't use its T).

740–741

There are a bunch of ways we can optimize this, but let's get the straightforward approach landed first :)

lib/Sema/SemaTemplate.cpp
3598

&& and || on prior line, please.

nridge added a subscriber: nridge.Nov 19 2019, 10:13 PM
saar.raz updated this revision to Diff 232457.Dec 5 2019, 2:50 PM
saar.raz marked 7 inline comments as done.

Address all CR comments by rsmith (including rewrite of normalization).
Decided to not support things like:

template<typename T, typename U>
concept C = ...;

template<typename... T> requires C<T...>
struct S { };

For now, as wording is not clear what the normal form of these should be.

Addressed in latest diff

rsmith accepted this revision.Dec 14 2019, 2:47 PM

Only a few non-nit comments; if you feel confident addressing those, please feel free to commit after doing so. (If you'd like another round of review, let me know.) Thanks!

clang/include/clang/AST/DeclTemplate.h
796–799 ↗(On Diff #232457)

Formatting nits:

typename ...ProfileArguments
ProfileArguments &&...ProfileArgs

clang/include/clang/Basic/DiagnosticSemaKinds.td
2507 ↗(On Diff #232457)

Consider adding "during constraint normalization" before the semicolon.

Giving advice here ("make sure [...]") isn't consistent with our normal diagnostic style, which is to present facts. I wonder if we can rephrase this as a fact? Something like "while substituting into %0 %1; constraint normalization is not a SFINAE context" would work -- though I don't think we mention SFINAE in any diagnostics yet either, and I'd prefer not to, if possible.

Maybe just "while substituting into %0 %1 during constraint normalization" is enough; the fact that we're issuing an error for a substitution failure seems to strongly imply that substitution failure in this context is an error. What do you think?

clang/include/clang/Sema/Sema.h
6036 ↗(On Diff #232457)

Nit: underindented.

clang/lib/AST/DeclTemplate.cpp
432 ↗(On Diff #232457)

Nit: parameters after a newline should start in the same column as the first parameter. (Either flow Args onto a new line or indent these parameters to line up with it.)

445–463 ↗(On Diff #232457)

Should we profile the kind of the template parameter (non-type/type/template) here? The profiling for the different kinds seems like it might not actually collide in practice, but that looks like it's happening by luck rather than by design.

Conversely, can we remove the profiling of the depth and index? That seems like it should be implied by the position of the parameter in the profiling data.

clang/lib/Sema/SemaConcept.cpp
417 ↗(On Diff #232457)

Nit: we generally don't use direct-list-initialization. Use parens here.

528–569 ↗(On Diff #232457)

You can use Sema::MarkUsedTemplateParameters for this.

... oh, except that it's broken for this case and has a FIXME to walk the full expression in OnlyDeduced = false mode:

// FIXME: if !OnlyDeduced, we have to walk the whole subexpression to
// find other occurrences of template parameters.

Oops. Please either move this into MarkUsedTemplateParameters and call that from here, or at least add a FIXME here to remind us to do so.

clang/lib/Sema/SemaTemplate.cpp
10521–10526 ↗(On Diff #232457)

Presumably this is unintentional / left behind from editing?

clang/lib/Sema/SemaTemplateDeduction.cpp
2505 ↗(On Diff #232457)

Is there any code that can be shared between this and ASTContext::getInjectedTemplateArg? (I think probably not, because that creates pack expansions and we don't want them here, but it seems worth considering.)

2514 ↗(On Diff #232457)

VK_RValue is wrong here: we want an lvalue for a reference parameter. getInjectedTemplateArg uses Expr::getValueKindForType(NTTP->getType()) rather than VK_RValue, but can you add a NonTypeTemplateParmDecl::getValueKindForRef or similar, and use that both there and here, so that we don't need to track down all these places again when adding class type non-type template parameters?

Also NTTP->getType() is wrong in the reference type case (you want getNonLValueExprType).

Alternatively, you could use BuildDeclarationNameExpr, which computes the type and value kind for you. (getInjectedTemplateArg can't do that, because it's not in Sema.)

2739 ↗(On Diff #232457)

Nit: || on the previous line, please.

5158 ↗(On Diff #232457)

This should only apply if Better1 and Better2 are both true.

(Eventually -- once the relevant core issue is fixed -- we'll want to use the deductions from the "at least as specialized" checks as inputs to the "more constrained" checks too -- we should substitute those deductions into the normalized constraints prior to checking which is more constrained, so that we're talking about constraints on the same parameters in both sets of constraints. But let's not jump the gun on that.)

5190 ↗(On Diff #232457)

As above, this should still return false.

5218 ↗(On Diff #232457)

Likewise here.

5263 ↗(On Diff #232457)

And here.

This revision is now accepted and ready to land.Dec 14 2019, 2:47 PM
thakis added a subscriber: thakis.Dec 18 2019, 11:08 AM

This makes clang crash when running tests: http://45.33.8.238/linux/5976/step_7.txt

Please use "Differential Revision: https://reviews.llvm.org/D41910" instead of "Phabricator: D41910" so that phab auto-closes the review and other auto-linking tools work.

This revision was automatically updated to reflect the committed changes.
rnk added a subscriber: rnk.Dec 22 2019, 12:24 PM

This was relanded and reverted again today (d3f5769d5e93b30d4a8b4696381d5e4a304992fa & 79cc9e9b304a90598e8def4c8b5354d1f99186eb). I got this reduction:

template <unsigned> struct a {};
struct DestructibleUnionImpl {
  template <class...> DestructibleUnionImpl(a<0>);
  template <unsigned b, class...> DestructibleUnionImpl(a<b>);
};
class c {
  c() : d(a<0>()) {}
  DestructibleUnionImpl d;
};

Compile with clang -cc1, get this assertion:
clang: /usr/local/google/home/rnk/bisect-llvm-project/llvm/include/llvm/ADT/SmallBitVector.h:452: llvm::SmallBitVector::reference llvm::SmallBitVector::operator[](unsigned int): Assertion `Idx < size() && "Out-of-bounds Bit access."' failed.

Please add this as a test when relanding.