<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Sat, Nov 18, 2017 at 11:58 AM, Saar Raz <span dir="ltr"><<a href="mailto:saarraz1@gmail.com" target="_blank">saarraz1@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><span class=""><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>Concepts are not instantiated. They are evaluated for satisfaction. Much of the wording to allow "global" knowledge to be used for instantiating templates relies on point-of-instantiation.<br></div><div><br>void foo(int, int);<br><br>template <typename T><br>concept C = requires (T t) { ::foo(t); };<br><br>constexpr bool a = C<int>;<br><br>void foo(int, int = 0);<br>constexpr bool b = C<int>;<br><br>static_assert(a != b);</div></div></div></div></blockquote><div> </div></div></span><div dir="ltr"><div class="gmail_quote"><div>Mmm, good example. So does that mean we cannot hope to cache results of concept evaluations? That sounds like quite the performance hit for highly conceptized code (e.g. ranges), is it not?</div><div><br></div><div>Maybe we can form some sort of condition under which a concept evaluation result may be cached? There are, I think, at least some expression classes that cannot change once the concept has been instantiated with a type (e.g. expressions that do not involve namespace lookup).</div></div></div></div></blockquote><div>There are indeed things that can be cached (like an atomic constraint of ::std::is_union_v<T>). Even if the constraint expressed by the concept specialization cannot be entirely cached, the constraint can be rewritten when cacheable portions are evaluated.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div dir="ltr"><div class="gmail_quote"><div>I'd say that's premature optimization but it impacts the design quite substantially - do we model concepts specializations as Decls or not?</div></div></div></div></blockquote><div>If I understand you correctly, you are saying that the trade-off is uncertain on whether using Decls would buy us enough for the cost of having them.<br>Using Decls or not affects how we should refer to the concept specializations from expression nodes that name fully resolved concept specializations.<br>If we do not use the Decls to keep enough information around, then they only form an extra layer of indirection.<br><br></div><div>Other than that, the purist in me says that concept specializations are merely a pair of concept template and a set of template arguments. There is not even an AST with the arguments substituted in.<br></div><div> <br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><span class=""><div dir="ltr"><div class="gmail_quote"><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>My understanding is that it is viable for conjunction and disjunction to be represented by && and || for satisfaction checking; it would be context-dependent during the evaluation whether the node is within an atomic constraint (and thus not representative of a conjunction or disjunction). <br></div></div></div></div></blockquote></div></div></span><div dir="ltr"><div class="gmail_quote"><div>What worries me about checking for satisfaction with && and || is the following:</div><div>[temp.constr.atomic]: "[Example:</div><div>  template<typename T></div><div>  concept C = sizeof(T) == 4 && !true; // requires atomic constraints</div><div>                                     // sizeof(T) == 4 and !true</div><div>  template<typename T></div><div>  struct S {</div><div>    constexpr operator bool() const { return true; }</div><div>  };</div><div>  </div><div>  template<typename T></div><div>    requires (S<T>{})</div><div>      void f(T); // #1</div><div>      </div><div>  void f(int); // #2</div><div>  </div><div>  void g() {</div><div>  f(0); // error: expression S<int>{} does not have type bool</div><div>        // while checking satisfaction of deduced arguments of #1,</div><div>        // even though #2 is a better match</div><div>  }</div><div>— end example ]"</div><div>By using regular Exprs we cannot enforce the bool-ness of operands.</div></div></div></div></blockquote><div>I think this depends on timing. I am not sure how eager Clang is in performing semantic analysis that would trigger implicit conversions. We probably need to hamper resolution of the && and || operators somehow while we are in a constraint, but not atomic constraint, context. I believe that would be sufficient to prevent the type of atomic constraints from being coerced early.<br></div></div></div></div>