This is an archive of the discontinued LLVM Phabricator instance.

Correctly handle Substitution failure in concept specialization.
AbandonedPublic

Authored by usaxena95 on Nov 9 2022, 6:42 AM.

Details

Summary

Fixes: https://github.com/llvm/llvm-project/issues/45563

template<class T>  concept True = true;

template <class T>
concept C1 = requires (T) {
   requires True<typename T::value> || True<T>;
};

template <class T>
constexpr bool foo() 
requires True<typename T::value> || True<T> {
    return true;
}
static_assert(C1<double>); // Previously failed due to SFINAE error
static_assert(foo<int>()); // but this works fine.

The issue here is the discrepancy between how a nested requirement is evaluated Vs how a non-nested requirement is evaluated.

In non-nested cases, we explicitly handle Binary operators transforming, substituting and evaluating each branch separately while for a nested requirement we substituting the complete the constraint expr leading to SFINAE failure for the complete expression. Restricting the SFINAE failure to the atomic constraint and marking the constraint only as "not-satisfied" solves the issue.

  1. https://eel.is/c++draft/expr.prim.req#nested-1
Substitution of template arguments into a nested-requirement does not result in substitution into the constraint-expression other than as specified in [temp.constr.constr].

https://eel.is/c++draft/expr.prim.req#nested-example-1

[Example 1:
template<typename U> concept C = sizeof(U) == 1;

template<typename T> concept D = requires (T t) {
  requires C<decltype (+t)>;
};
D<T> is satisfied if sizeof(decltype (+t)) == 1 ([temp.constr.atomic]). — end example]
  1. https://eel.is/c++draft/temp.constr.constr#temp.constr.atomic-3
To determine if an atomic constraint is satisfied, the parameter mapping and template arguments are first substituted into its expression. If substitution results in an invalid type or expression, the constraint is not satisfied.

Therefore in case of substitution failure in a constraint expression, the corresponding atomic constraint should be failed and not the entire constraint expression/nested requires expression.

TODO: I want to properly propagate the substitution failure.

Diff Detail

Event Timeline

usaxena95 created this revision.Nov 9 2022, 6:42 AM
Herald added a project: Restricted Project. · View Herald TranscriptNov 9 2022, 6:42 AM
usaxena95 requested review of this revision.Nov 9 2022, 6:42 AM
Herald added a project: Restricted Project. · View Herald TranscriptNov 9 2022, 6:42 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
usaxena95 added subscribers: Restricted Project, ilya-biryukov, erichkeane.Nov 9 2022, 6:55 AM
usaxena95 edited the summary of this revision. (Show Details)Nov 9 2022, 7:10 AM

I dont have a good hold as to why this is the solution, can you better explain the issue and the solution that you made? I will take a look when I get a chance next week, as the ISO meeting is taking up my week.

usaxena95 edited the summary of this revision. (Show Details)Nov 10 2022, 2:38 AM
usaxena95 edited the summary of this revision. (Show Details)
shafik added a subscriber: shafik.Nov 10 2022, 8:54 PM
shafik added inline comments.
clang/lib/AST/ExprConcepts.cpp
120–121

I think I got the parameter names correct.

usaxena95 updated this revision to Diff 474716.Nov 11 2022, 3:50 AM

Moving closer to show diagnostic of entity for which SFNIAE occurred.

I think this is a move in the right direction, and generally is probably pretty close. The new bool for ArgsHasSubstitutionFailure isn't something I get the need for yet though.

we also need release notes.

clang/include/clang/AST/ExprConcepts.h
101

Does this really belong here instead of as a part of the ConceptSpecializationDecl?

158

I had a previous patch at something else where I was moving toward doing this change, so I think this is probably something inevitable.

However, I'm having a tough time splitting this patch mentally between the 'fix' and the infrastructure changes needed. A part of me thinks we should split this patch a bit in that direction.

usaxena95 marked 3 inline comments as done.

Addressed commented. Added Release notes.
Removed infrastructure changes and deferred them to a future patch.

clang/include/clang/AST/ExprConcepts.h
101

I am not aware of the original goals for this. For example, ASTTemplateArgumentListInfo is part of ConceptReference while TemplateArgument is part of ImplicitConceptSpecializationDecl. Both of them are invalid in such a case.
I am open to suggestion to where to place this.

158

I agree. This is a larger change which probably belongs to a separate change. Until then I think it is fine to not provide diagnostic of the exact expression of the SubstitutedEntity for which SF occurred.

clang/lib/AST/ExprConcepts.cpp
120–121

Could you please elaborate what is your suggestion here ?

erichkeane added inline comments.Nov 15 2022, 8:39 AM
clang/include/clang/AST/ExprConcepts.h
101

Yeah, the design of all of these is a little wonky unfortunately. The ConceptReference is so it can be a base of a TypeConstraint as well, so anything that also needs to be in type-constraint belongs there.

ConceptSpecializationDecl is a recent split off ConstraintSpecializationExpr whose intent is to be something that later instantiation (like instantiating a lambda defined in a concept decl) can use to make sure we get template arguments correct. So I suspect the args failure needs to be contained in that as well.

IF this is something that can also be invalid in a TypeConstraint, it belongs in ConceptReference AND ConceptSpecializationDecl separately I believe (as we'll likely need to check that's validity later).

We discussed this with Utkarsh offline, he's OOO for some time, so I wanted to leave the summary here.
This patch attempts to attack the problem by keeping the information about substitution failure in template arguments of ConceptSpecializationExpr.
However, it seems that the standard does not give any special treatment to those as compared to other forms of substitution failure and we should rethink our approach. The problem seems to be in how we evaluate SFINAE inside requires-expression and/or nested-requirement.

E.g. if we replace a concept by a template variable, a failure to substitute template arguments should not cause the whole requires-expression to evaluate to false:

template <class T> constexpr bool true_v = true; 
template <class T> constexpr int foo() requires requires { requires true_v<typename T::type> || true_v<T>; }
    { return 123; }

static_assert(foo<int>() == 123);

GCC and MSVC succeed here, Clang fails: https://gcc.godbolt.org/z/WGdKWYM7e. Clang's behavior seems to be non-standard here.
The proper fix seems to be changing Clang behavior so that the requires-expression evaluates to true despite its first branch having a subsitution failure.

Utkarsh has also found an interesting example where GCC (IMO, unexpectedly) evaluates the concept to true despite a substitution failure in its template arguments:

template <class U> concept true_c = true; 
template <class T> constexpr int foo() 
    requires requires { requires true_c<typename T::type>; }
    { return 123; }

static_assert(foo<int>() == 123);

Clang and MSVC fail here, GCC succeeds: https://gcc.godbolt.org/z/5qYjcW75q.
The standard does not seem to be very clear here. It says that an "atomic constraint" that we must evaluate contains both an expression (true) and a template parameter mapping (U -> typename T::type, where T is int). The template parameter mapping is clearly not well-formed, the expression still evaluates to true and the standard does not seem to mention what to do when template parameter mapping is not well-formed. IMO, to form the template parameter mapping we must substitute T -> int into the template argument list (<typename T::type>) and at that point we see a substitution failure, hence requires-expression should evaluate to false. Someone should probably raise this with WG21 to know for sure.

usaxena95 abandoned this revision.Nov 29 2022, 5:22 AM

Second attempt along the lines of the last comment: https://reviews.llvm.org/D138914
PTAL.