erichkeane updated this revision to Diff 279847. erichkeane marked an inline comment as done. erichkeane added a comment.
As CWG seems unmoved by my argument as to why CWG2303 should change, I've opted to implement it as worded. As you can see, the implementation is somewhat more complicated, but I believe I made it at least reasonably understandable. I've also now updated the cwg_dr_status.html file via the script, which I also updated to acknowledge the current released version. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D84048/new/ https://reviews.llvm.org/D84048 Files: clang/lib/Sema/SemaTemplateDeduction.cpp clang/test/CXX/drs/dr23xx.cpp clang/www/cxx_dr_status.html clang/www/make_cxx_dr_status
Index: clang/www/make_cxx_dr_status =================================================================== --- clang/www/make_cxx_dr_status +++ clang/www/make_cxx_dr_status @@ -1,4 +1,5 @@ #! /usr/bin/env python + import sys, os, re index = 'cwg_index.html' @@ -93,7 +94,7 @@ <th>Available in Clang?</th> </tr>''' -latest_release = 10 +latest_release = 11 def availability(issue): status = status_map.get(issue, 'unknown') Index: clang/www/cxx_dr_status.html =================================================================== --- clang/www/cxx_dr_status.html +++ clang/www/cxx_dr_status.html @@ -1504,7 +1504,7 @@ <td><a href="https://wg21.link/cwg244">244</a></td> <td>CD1</td> <td>Destructor lookup</td> - <td class="unreleased" align="center">Clang 11</td> + <td class="full" align="center">Clang 11</td> </tr> <tr id="245"> <td><a href="https://wg21.link/cwg245">245</a></td> @@ -2789,7 +2789,7 @@ <td><a href="https://wg21.link/cwg458">458</a></td> <td>C++11</td> <td>Hiding of member template parameters by other members</td> - <td class="unreleased" align="center">Clang 11</td> + <td class="full" align="center">Clang 11</td> </tr> <tr class="open" id="459"> <td><a href="https://wg21.link/cwg459">459</a></td> @@ -3967,7 +3967,7 @@ <td><a href="https://wg21.link/cwg654">654</a></td> <td>CD1</td> <td>Conversions to and from <TT>nullptr_t</TT></td> - <td class="unreleased" align="center">Superseded by <a href="#1423">1423</a></td> + <td class="full" align="center">Superseded by <a href="#1423">1423</a></td> </tr> <tr id="655"> <td><a href="https://wg21.link/cwg655">655</a></td> @@ -8353,7 +8353,7 @@ <td><a href="https://wg21.link/cwg1423">1423</a></td> <td>CD3</td> <td>Convertibility of <TT>nullptr</TT> to <TT>bool</TT></td> - <td class="unreleased" align="center">Clang 11</td> + <td class="full" align="center">Clang 11</td> </tr> <tr id="1424"> <td><a href="https://wg21.link/cwg1424">1424</a></td> @@ -8899,7 +8899,7 @@ <td><a href="https://wg21.link/cwg1514">1514</a></td> <td>C++14</td> <td>Ambiguity between enumeration definition and zero-length bit-field</td> - <td class="unreleased" align="center">Clang 11</td> + <td class="full" align="center">Clang 11</td> </tr> <tr id="1515"> <td><a href="https://wg21.link/cwg1515">1515</a></td> @@ -10333,7 +10333,7 @@ <td><a href="https://wg21.link/cwg1753">1753</a></td> <td>CD4</td> <td><I>decltype-specifier</I> in <I>nested-name-specifier</I> of destructor</td> - <td class="unreleased" align="center">Clang 11</td> + <td class="full" align="center">Clang 11</td> </tr> <tr id="1754"> <td><a href="https://wg21.link/cwg1754">1754</a></td> @@ -11611,7 +11611,7 @@ <td><a href="https://wg21.link/cwg1966">1966</a></td> <td>CD4</td> <td>Colon following enumeration <I>elaborated-type-specifier</I></td> - <td class="unreleased" align="center">Clang 11</td> + <td class="full" align="center">Clang 11</td> </tr> <tr id="1967"> <td><a href="https://wg21.link/cwg1967">1967</a></td> @@ -11971,7 +11971,7 @@ <td><a href="https://wg21.link/cwg2026">2026</a></td> <td>CD4</td> <td>Zero-initialization and <TT>constexpr</TT></td> - <td class="unreleased" align="center">Clang 11</td> + <td class="full" align="center">Clang 11</td> </tr> <tr id="2027"> <td><a href="https://wg21.link/cwg2027">2027</a></td> @@ -12307,7 +12307,7 @@ <td><a href="https://wg21.link/cwg2082">2082</a></td> <td>CD4</td> <td>Referring to parameters in unevaluated operands of default arguments</td> - <td class="unreleased" align="center">Clang 11</td> + <td class="full" align="center">Clang 11</td> </tr> <tr id="2083"> <td><a href="https://wg21.link/cwg2083">2083</a></td> @@ -12757,7 +12757,7 @@ <td><a href="https://wg21.link/cwg2157">2157</a></td> <td>CD4</td> <td>Further disambiguation of enumeration <I>elaborated-type-specifier</I></td> - <td class="unreleased" align="center">Clang 11</td> + <td class="full" align="center">Clang 11</td> </tr> <tr class="open" id="2158"> <td><a href="https://wg21.link/cwg2158">2158</a></td> @@ -13213,7 +13213,7 @@ <td><a href="https://wg21.link/cwg2233">2233</a></td> <td>DRWP</td> <td>Function parameter packs following default arguments</td> - <td class="unreleased" align="center">Clang 11</td> + <td class="full" align="center">Clang 11</td> </tr> <tr id="2234"> <td><a href="https://wg21.link/cwg2234">2234</a></td> @@ -13633,7 +13633,7 @@ <td><a href="https://wg21.link/cwg2303">2303</a></td> <td>DRWP</td> <td>Partial ordering and recursive variadic inheritance</td> - <td class="none" align="center">Unknown</td> + <td class="unreleased" align="center">Clang 12</td> </tr> <tr id="2304"> <td><a href="https://wg21.link/cwg2304">2304</a></td> @@ -13891,7 +13891,7 @@ <td><a href="https://wg21.link/cwg2346">2346</a></td> <td>DRWP</td> <td>Local variables in default arguments</td> - <td class="unreleased" align="center">Clang 11</td> + <td class="full" align="center">Clang 11</td> </tr> <tr id="2347"> <td><a href="https://wg21.link/cwg2347">2347</a></td> @@ -14499,6 +14499,24 @@ <td>Unintended description of abbreviated function templates</td> <td class="none" align="center">Unknown</td> </tr> + <tr class="open" id="2448"> + <td><a href="https://wg21.link/cwg2448">2448</a></td> + <td>open</td> + <td>Cv-qualification of arithmetic types and deprecation of volatile</td> + <td align="center">Not resolved</td> + </tr> + <tr class="open" id="2449"> + <td><a href="https://wg21.link/cwg2449">2449</a></td> + <td>open</td> + <td>Thunks as an implementation technique for pointers to virtual functions</td> + <td align="center">Not resolved</td> + </tr> + <tr class="open" id="2450"> + <td><a href="https://wg21.link/cwg2450">2450</a></td> + <td>open</td> + <td><I>braced-init-list</I> as a <I>template-argument</I></td> + <td align="center">Not resolved</td> + </tr> </table> </div> Index: clang/test/CXX/drs/dr23xx.cpp =================================================================== --- clang/test/CXX/drs/dr23xx.cpp +++ clang/test/CXX/drs/dr23xx.cpp @@ -113,3 +113,35 @@ extern template const int d<const int>; #endif } + +#if __cplusplus >= 201103L +namespace dr2303 { // dr2303: 12 +template <typename... T> +struct A; +template <> +struct A<> {}; +template <typename T, typename... Ts> +struct A<T, Ts...> : A<Ts...> {}; +struct B : A<int, int> {}; +struct C : A<int, int>, A<int> {}; // expected-warning {{direct base 'A<int>' is inaccessible}} +struct D : A<int>, A<int, int> {}; // expected-warning {{direct base 'A<int>' is inaccessible}} +struct E : A<int, int> {}; +struct F : B, E {}; + +template <typename... T> +void f(const A<T...> &) { + static_assert(sizeof...(T) == 2, "Should only match A<int,int>"); +} +template <typename... T> +void f2(const A<T...> *); + +void g() { + f(B{}); // This is no longer ambiguous. + B b; + f2(&b); + f(C{}); + f(D{}); + f(F{}); // expected-error {{ambiguous conversion from derived class}} +} +} //namespace dr2303 +#endif Index: clang/lib/Sema/SemaTemplateDeduction.cpp =================================================================== --- clang/lib/Sema/SemaTemplateDeduction.cpp +++ clang/lib/Sema/SemaTemplateDeduction.cpp @@ -1201,6 +1201,159 @@ return false; } +// Attempt to deduce the template arguments by checking the base types according +// to (C++ [temp.deduct.call] p4b3. +/// +/// \param S the semantic analysis object within which we are deducing +/// +/// \param RecordT the top level record object we are deducing against. +/// +/// \param TemplateParams the template parameters that we are deducing +/// +/// \param SpecParam the template specialization parameter type. +/// +/// \param Info information about the template argument deduction itself +/// +/// \param Deduced the deduced template arguments +/// +/// \returns the result of template argument deduction with the bases. "invalid" +/// means no matches, "success" found a single item, and the +/// "MiscellaneousDeductionFailure" result happens when the match is ambiguous. +static Sema::TemplateDeductionResult DeduceTemplateBases( + Sema &S, const RecordType *RecordT, TemplateParameterList *TemplateParams, + const TemplateSpecializationType *SpecParam, TemplateDeductionInfo &Info, + SmallVectorImpl<DeducedTemplateArgument> &Deduced) { + // C++14 [temp.deduct.call] p4b3: + // If P is a class and P has the form simple-template-id, then the + // transformed A can be a derived class of the deduced A. Likewise if + // P is a pointer to a class of the form simple-template-id, the + // transformed A can be a pointer to a derived class pointed to by the + // deduced A. However, if there is a class C that is a (direct or + // indirect) base class of D and derived (directly or indirectly) from a + // class B and that would be a valid deduced A, the deduced A cannot be + // B or pointer to B, respectively. + // + // These alternatives are considered only if type deduction would + // otherwise fail. If they yield more than one possible deduced A, the + // type deduction fails. + + // A structure to represent the successful matches. + struct BaseMatch { + const RecordType *RT; + TemplateDeductionInfo BaseInfo; + SmallVector<DeducedTemplateArgument, 8> Deduction; + + BaseMatch(const RecordType *RT, const TemplateDeductionInfo &Info, + const llvm::SmallVectorImpl<DeducedTemplateArgument> &Deduced) + : RT(RT), BaseInfo(TemplateDeductionInfo::ForBase, Info), + Deduction(Deduced.begin(), Deduced.end()) {} + BaseMatch(BaseMatch &&LHS) + : RT(LHS.RT), BaseInfo(TemplateDeductionInfo::ForBase, LHS.BaseInfo), + Deduction(std::move(LHS.Deduction)) { + BaseInfo.Param = std::move(LHS.BaseInfo.Param); + BaseInfo.FirstArg = std::move(LHS.BaseInfo.FirstArg); + BaseInfo.SecondArg = std::move(LHS.BaseInfo.SecondArg); + } + + BaseMatch &operator=(BaseMatch &&LHS) { + RT = LHS.RT; + BaseInfo.Param = std::move(LHS.BaseInfo.Param); + BaseInfo.FirstArg = std::move(LHS.BaseInfo.FirstArg); + BaseInfo.SecondArg = std::move(LHS.BaseInfo.SecondArg); + Deduction = std::move(LHS.Deduction); + return *this; + } + }; + // Use a breadth-first search through the bases to collect the set of + // successful matches. Visited contains the set of nodes we have already + // visited, while ToVisit is our stack of records that we still need to + // visit. Matches contains a list of matches that have yet to be + // disqualified. + llvm::SmallPtrSet<const RecordType *, 8> Visited; + SmallVector<const RecordType *, 8> ToVisit; + SmallVector<BaseMatch, 4> Matches; + + auto AddBases = [&ToVisit](const RecordType *RT) { + CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl()); + for (const auto &Base : RD->bases()) { + assert(Base.getType()->isRecordType() && + "Base class that isn't a record?"); + ToVisit.push_back(Base.getType()->getAs<RecordType>()); + } + }; + + // Set up the loop by adding all the bases. + AddBases(RecordT); + + // Search each path of bases until we either run into a successful match + // (where all bases of IT are invalid), or we run out of bases. + while (!ToVisit.empty()) { + const RecordType *NextT = ToVisit.pop_back_val(); + // If we have already seen this type, skip it. + if (!Visited.insert(NextT).second) + continue; + + BaseMatch &CurMatch = Matches.emplace_back(NextT, Info, Deduced); + Sema::TemplateDeductionResult BaseResult = DeduceTemplateArguments( + S, TemplateParams, SpecParam, QualType(NextT, 0), CurMatch.BaseInfo, + CurMatch.Deduction); + + // If the match was not a successful deduction, we have to search its bases. + if (BaseResult != Sema::TDK_Success) { + AddBases(NextT); + Matches.pop_back(); + } + } + + // At this point, 'Matches' contains a list of seemingly valid bases, however + // in the event that we have more than 1 match, it is possible that the base + // of one of the matches might be disqualified for being a base of another + // valid match. We can count on cyclical instantiations being invalid to + // simplify the disqualifications. That is, if A & B are both matches, and B + // inherits from A (disqualifying A), we know that A cannot inherit from B. + if (Matches.size() > 1) { + for (const BaseMatch &Match : Matches) + AddBases(Match.RT); + + Visited.clear(); + + // We can give up once we have a single item (or have run out of things to + // search) since cyclical inheritence isn't valid. + while (Matches.size() > 1 && !ToVisit.empty()) { + const RecordType *NextT = ToVisit.pop_back_val(); + // If we have already seen this type, skip it. + if (!Visited.insert(NextT).second) + continue; + + BaseMatch CurMatch(NextT, Info, Deduced); + Sema::TemplateDeductionResult BaseResult = DeduceTemplateArguments( + S, TemplateParams, SpecParam, QualType(NextT, 0), CurMatch.BaseInfo, + CurMatch.Deduction); + + // If this iS a match, it isn't valid due to CWG2303. So, remove it + // from the possible matches. + if (BaseResult == Sema::TDK_Success) + llvm::erase_if(Matches, + [NextT](const BaseMatch &M) { return M.RT == NextT; }); + + // Always add all bases, since the inheritence tree can contain + // disqualifications for multiple matches. + AddBases(NextT); + } + } + + if (Matches.empty()) + return Sema::TDK_Invalid; + if (Matches.size() > 1) + return Sema::TDK_MiscellaneousDeductionFailure; + + std::swap(Matches[0].Deduction, Deduced); + Info.Param = Matches[0].BaseInfo.Param; + Info.FirstArg = Matches[0].BaseInfo.FirstArg; + Info.SecondArg = Matches[0].BaseInfo.SecondArg; + return Sema::TDK_Success; +} + /// Deduce the template arguments by comparing the parameter type and /// the argument type (C++ [temp.deduct.type]). /// @@ -1787,78 +1940,15 @@ if (!S.isCompleteType(Info.getLocation(), Arg)) return Result; - // C++14 [temp.deduct.call] p4b3: - // If P is a class and P has the form simple-template-id, then the - // transformed A can be a derived class of the deduced A. Likewise if - // P is a pointer to a class of the form simple-template-id, the - // transformed A can be a pointer to a derived class pointed to by the - // deduced A. - // - // These alternatives are considered only if type deduction would - // otherwise fail. If they yield more than one possible deduced A, the - // type deduction fails. - // Reset the incorrectly deduced argument from above. Deduced = DeducedOrig; - // Use data recursion to crawl through the list of base classes. - // Visited contains the set of nodes we have already visited, while - // ToVisit is our stack of records that we still need to visit. - llvm::SmallPtrSet<const RecordType *, 8> Visited; - SmallVector<const RecordType *, 8> ToVisit; - ToVisit.push_back(RecordT); - bool Successful = false; - SmallVector<DeducedTemplateArgument, 8> SuccessfulDeduced; - while (!ToVisit.empty()) { - // Retrieve the next class in the inheritance hierarchy. - const RecordType *NextT = ToVisit.pop_back_val(); - - // If we have already seen this type, skip it. - if (!Visited.insert(NextT).second) - continue; - - // If this is a base class, try to perform template argument - // deduction from it. - if (NextT != RecordT) { - TemplateDeductionInfo BaseInfo(TemplateDeductionInfo::ForBase, Info); - Sema::TemplateDeductionResult BaseResult = - DeduceTemplateArguments(S, TemplateParams, SpecParam, - QualType(NextT, 0), BaseInfo, Deduced); - - // If template argument deduction for this base was successful, - // note that we had some success. Otherwise, ignore any deductions - // from this base class. - if (BaseResult == Sema::TDK_Success) { - // If we've already seen some success, then deduction fails due to - // an ambiguity (temp.deduct.call p5). - if (Successful) - return Sema::TDK_MiscellaneousDeductionFailure; - - Successful = true; - std::swap(SuccessfulDeduced, Deduced); - - Info.Param = BaseInfo.Param; - Info.FirstArg = BaseInfo.FirstArg; - Info.SecondArg = BaseInfo.SecondArg; - } - - Deduced = DeducedOrig; - } - - // Visit base classes - CXXRecordDecl *Next = cast<CXXRecordDecl>(NextT->getDecl()); - for (const auto &Base : Next->bases()) { - assert(Base.getType()->isRecordType() && - "Base class that isn't a record?"); - ToVisit.push_back(Base.getType()->getAs<RecordType>()); - } - } - - if (Successful) { - std::swap(SuccessfulDeduced, Deduced); - return Sema::TDK_Success; - } + // Check bases according to C++14 [temp.deduct.call] p4b3: + Sema::TemplateDeductionResult BaseResult = DeduceTemplateBases( + S, RecordT, TemplateParams, SpecParam, Info, Deduced); + if (BaseResult != Sema::TDK_Invalid) + return BaseResult; return Result; }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits