Author: Richard Smith Date: 2022-12-12T17:09:26-08:00 New Revision: 5982b0b0b84296e34057a777e3d51e10fcd8abc7
URL: https://github.com/llvm/llvm-project/commit/5982b0b0b84296e34057a777e3d51e10fcd8abc7 DIFF: https://github.com/llvm/llvm-project/commit/5982b0b0b84296e34057a777e3d51e10fcd8abc7.diff LOG: Add missing check for constant evaluation of a comparison of a pointer to member naming a weak member to nullptr. This fixes a miscompile where constant evaluation would incorrectly determine that a weak member function pointer is never null. In passing, also improve the diagnostics for constant evaluation of some nearby cases. Added: Modified: clang/docs/ReleaseNotes.rst clang/include/clang/Basic/DiagnosticASTKinds.td clang/lib/AST/ExprConstant.cpp clang/test/AST/Interp/cxx20.cpp clang/test/CXX/drs/dr0xx.cpp clang/test/CXX/drs/dr16xx.cpp clang/test/CXX/expr/expr.const/p2-0x.cpp clang/test/CodeGenCXX/weak-external.cpp clang/test/Sema/const-eval.c clang/test/SemaCXX/attr-weak.cpp clang/test/SemaCXX/builtins.cpp clang/test/SemaCXX/constant-expression-cxx11.cpp clang/www/cxx_dr_status.html clang/www/make_cxx_dr_status Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 53155b0e2a492..3aef8c93f6ffe 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -321,6 +321,9 @@ Bug Fixes `Issue 59100 <https://github.com/llvm/llvm-project/issues/59100>`_ - Fix issue using __attribute__((format)) on non-variadic functions that expect more than one formatted argument. +- Fix bug where constant evaluation treated a pointer to member that points to + a weak member as never being null. Such comparisons are now treated as + non-constant. Improvements to Clang's diagnostics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index 6a2d15ef77c6a..d4303ef834c17 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -84,7 +84,23 @@ def note_constexpr_pointer_subtraction_not_same_array : Note< def note_constexpr_pointer_subtraction_zero_size : Note< "subtraction of pointers to type %0 of zero size">; def note_constexpr_pointer_comparison_unspecified : Note< - "comparison has unspecified value">; + "comparison between '%0' and '%1' has unspecified value">; +def note_constexpr_pointer_constant_comparison : Note< + "comparison of numeric address '%0' with pointer '%1' can only be performed " + "at runtime">; +def note_constexpr_literal_comparison : Note< + "comparison of addresses of literals has unspecified value">; +def note_constexpr_pointer_weak_comparison : Note< + "comparison against address of weak declaration '%0' can only be performed " + "at runtime">; +def note_constexpr_mem_pointer_weak_comparison : Note< + "comparison against pointer to weak member %q0 can only be performed " + "at runtime">; +def note_constexpr_pointer_comparison_past_end : Note< + "comparison against pointer '%0' that points past the end of a " + "complete object has unspecified value">; +def note_constexpr_pointer_comparison_zero_sized : Note< + "comparison of pointers '%0' and '%1' to unrelated zero-sized objects">; def note_constexpr_pointer_comparison_base_classes : Note< "comparison of addresses of subobjects of diff erent base classes " "has unspecified value">; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 280b37adc0321..8fd46197af334 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -2483,6 +2483,7 @@ static bool EvalPointerValueAsBool(const APValue &Value, bool &Result) { // A null base expression indicates a null pointer. These are always // evaluatable, and they are false unless the offset is zero. if (!Value.getLValueBase()) { + // TODO: Should a non-null pointer with an offset of zero evaluate to true? Result = !Value.getLValueOffset().isZero(); return true; } @@ -2495,6 +2496,7 @@ static bool EvalPointerValueAsBool(const APValue &Value, bool &Result) { } static bool HandleConversionToBool(const APValue &Val, bool &Result) { + // TODO: This function should produce notes if it fails. switch (Val.getKind()) { case APValue::None: case APValue::Indeterminate: @@ -2519,6 +2521,9 @@ static bool HandleConversionToBool(const APValue &Val, bool &Result) { case APValue::LValue: return EvalPointerValueAsBool(Val, Result); case APValue::MemberPointer: + if (Val.getMemberPointerDecl() && Val.getMemberPointerDecl()->isWeak()) { + return false; + } Result = Val.getMemberPointerDecl(); return true; case APValue::Vector: @@ -12938,41 +12943,55 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, // Reject diff ering bases from the normal codepath; we special-case // comparisons to null. if (!HasSameBase(LHSValue, RHSValue)) { + auto DiagComparison = [&] (unsigned DiagID, bool Reversed = false) { + std::string LHS = LHSValue.toString(Info.Ctx, E->getLHS()->getType()); + std::string RHS = RHSValue.toString(Info.Ctx, E->getRHS()->getType()); + Info.FFDiag(E, DiagID) + << (Reversed ? RHS : LHS) << (Reversed ? LHS : RHS); + return false; + }; // Inequalities and subtractions between unrelated pointers have // unspecified or undefined behavior. - if (!IsEquality) { - Info.FFDiag(E, diag::note_constexpr_pointer_comparison_unspecified); - return false; - } + if (!IsEquality) + return DiagComparison( + diag::note_constexpr_pointer_comparison_unspecified); // A constant address may compare equal to the address of a symbol. // The one exception is that address of an object cannot compare equal // to a null pointer constant. + // TODO: Should we restrict this to actual null pointers, and exclude the + // case of zero cast to pointer type? if ((!LHSValue.Base && !LHSValue.Offset.isZero()) || (!RHSValue.Base && !RHSValue.Offset.isZero())) - return Error(E); + return DiagComparison(diag::note_constexpr_pointer_constant_comparison, + !RHSValue.Base); // It's implementation-defined whether distinct literals will have // distinct addresses. In clang, the result of such a comparison is // unspecified, so it is not a constant expression. However, we do know // that the address of a literal will be non-null. if ((IsLiteralLValue(LHSValue) || IsLiteralLValue(RHSValue)) && LHSValue.Base && RHSValue.Base) - return Error(E); + return DiagComparison(diag::note_constexpr_literal_comparison); // We can't tell whether weak symbols will end up pointing to the same // object. if (IsWeakLValue(LHSValue) || IsWeakLValue(RHSValue)) - return Error(E); + return DiagComparison(diag::note_constexpr_pointer_weak_comparison, + !IsWeakLValue(LHSValue)); // We can't compare the address of the start of one object with the // past-the-end address of another object, per C++ DR1652. - if ((LHSValue.Base && LHSValue.Offset.isZero() && - isOnePastTheEndOfCompleteObject(Info.Ctx, RHSValue)) || - (RHSValue.Base && RHSValue.Offset.isZero() && - isOnePastTheEndOfCompleteObject(Info.Ctx, LHSValue))) - return Error(E); + if (LHSValue.Base && LHSValue.Offset.isZero() && + isOnePastTheEndOfCompleteObject(Info.Ctx, RHSValue)) + return DiagComparison(diag::note_constexpr_pointer_comparison_past_end, + true); + if (RHSValue.Base && RHSValue.Offset.isZero() && + isOnePastTheEndOfCompleteObject(Info.Ctx, LHSValue)) + return DiagComparison(diag::note_constexpr_pointer_comparison_past_end, + false); // We can't tell whether an object is at the same address as another // zero sized object. if ((RHSValue.Base && isZeroSized(LHSValue)) || (LHSValue.Base && isZeroSized(RHSValue))) - return Error(E); + return DiagComparison( + diag::note_constexpr_pointer_comparison_zero_sized); return Success(CmpResult::Unequal, E); } @@ -13076,6 +13095,19 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, if (!EvaluateMemberPointer(E->getRHS(), RHSValue, Info) || !LHSOK) return false; + // If either operand is a pointer to a weak function, the comparison is not + // constant. + if (LHSValue.getDecl() && LHSValue.getDecl()->isWeak()) { + Info.FFDiag(E, diag::note_constexpr_mem_pointer_weak_comparison) + << LHSValue.getDecl(); + return true; + } + if (RHSValue.getDecl() && RHSValue.getDecl()->isWeak()) { + Info.FFDiag(E, diag::note_constexpr_mem_pointer_weak_comparison) + << RHSValue.getDecl(); + return true; + } + // C++11 [expr.eq]p2: // If both operands are null, they compare equal. Otherwise if only one is // null, they compare unequal. diff --git a/clang/test/AST/Interp/cxx20.cpp b/clang/test/AST/Interp/cxx20.cpp index 6445abd402a02..38f05844a1ec9 100644 --- a/clang/test/AST/Interp/cxx20.cpp +++ b/clang/test/AST/Interp/cxx20.cpp @@ -101,6 +101,7 @@ constexpr bool b1 = foo(p1) == foo(p1); static_assert(b1); constexpr bool b2 = foo(p1) == foo(p2); // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{comparison of addresses of literals}} \ // ref-note {{declared here}} static_assert(!b2); // ref-error {{not an integral constant expression}} \ // ref-note {{not a constant expression}} @@ -111,6 +112,7 @@ constexpr auto name2() { return "name2"; } constexpr auto b3 = name1() == name1(); static_assert(b3); constexpr auto b4 = name1() == name2(); // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{has unspecified value}} \ // ref-note {{declared here}} static_assert(!b4); // ref-error {{not an integral constant expression}} \ // ref-note {{not a constant expression}} diff --git a/clang/test/CXX/drs/dr0xx.cpp b/clang/test/CXX/drs/dr0xx.cpp index e34dc75a1b6c1..6e7c5c557ec11 100644 --- a/clang/test/CXX/drs/dr0xx.cpp +++ b/clang/test/CXX/drs/dr0xx.cpp @@ -988,10 +988,10 @@ namespace dr70 { // dr70: yes // dr72: dup 69 #if __cplusplus >= 201103L -namespace dr73 { // dr73: no - // The resolution to dr73 is unworkable. Consider: +namespace dr73 { // dr73: sup 1652 int a, b; static_assert(&a + 1 != &b, ""); // expected-error {{not an integral constant expression}} + // expected-note@-1 {{comparison against pointer '&a + 1' that points past the end of a complete object}} } #endif diff --git a/clang/test/CXX/drs/dr16xx.cpp b/clang/test/CXX/drs/dr16xx.cpp index 83b444766d035..6f8ced5668f79 100644 --- a/clang/test/CXX/drs/dr16xx.cpp +++ b/clang/test/CXX/drs/dr16xx.cpp @@ -42,6 +42,12 @@ namespace dr1611 { // dr1611: dup 1658 C c; } +namespace dr1652 { // dr1652: 3.6 + int a, b; + int arr[&a + 1 == &b ? 1 : 2]; // expected-error 2{{variable length array}} + // expected-note@-1 {{points past the end}} +} + namespace dr1684 { // dr1684: 3.6 #if __cplusplus >= 201103L struct NonLiteral { // expected-note {{because}} diff --git a/clang/test/CXX/expr/expr.const/p2-0x.cpp b/clang/test/CXX/expr/expr.const/p2-0x.cpp index 8b10f33e80185..d8b228f123a90 100644 --- a/clang/test/CXX/expr/expr.const/p2-0x.cpp +++ b/clang/test/CXX/expr/expr.const/p2-0x.cpp @@ -499,22 +499,22 @@ namespace UnspecifiedRelations { // diff erent objects that are not members of the same array or to diff erent // functions, or if only one of them is null, the results of p<q, p>q, p<=q, // and p>=q are unspecified. - constexpr bool u1 = p < q; // expected-error {{constant expression}} expected-note {{comparison has unspecified value}} - constexpr bool u2 = p > q; // expected-error {{constant expression}} expected-note {{comparison has unspecified value}} - constexpr bool u3 = p <= q; // expected-error {{constant expression}} expected-note {{comparison has unspecified value}} - constexpr bool u4 = p >= q; // expected-error {{constant expression}} expected-note {{comparison has unspecified value}} - constexpr bool u5 = p < (int*)0; // expected-error {{constant expression}} expected-note {{comparison has unspecified value}} - constexpr bool u6 = p <= (int*)0; // expected-error {{constant expression}} expected-note {{comparison has unspecified value}} - constexpr bool u7 = p > (int*)0; // expected-error {{constant expression}} expected-note {{comparison has unspecified value}} - constexpr bool u8 = p >= (int*)0; // expected-error {{constant expression}} expected-note {{comparison has unspecified value}} - constexpr bool u9 = (int*)0 < q; // expected-error {{constant expression}} expected-note {{comparison has unspecified value}} - constexpr bool u10 = (int*)0 <= q; // expected-error {{constant expression}} expected-note {{comparison has unspecified value}} - constexpr bool u11 = (int*)0 > q; // expected-error {{constant expression}} expected-note {{comparison has unspecified value}} - constexpr bool u12 = (int*)0 >= q; // expected-error {{constant expression}} expected-note {{comparison has unspecified value}} + constexpr bool u1 = p < q; // expected-error {{constant expression}} expected-note {{comparison between '&a' and '&b' has unspecified value}} + constexpr bool u2 = p > q; // expected-error {{constant expression}} expected-note {{comparison between '&a' and '&b' has unspecified value}} + constexpr bool u3 = p <= q; // expected-error {{constant expression}} expected-note {{comparison between '&a' and '&b' has unspecified value}} + constexpr bool u4 = p >= q; // expected-error {{constant expression}} expected-note {{comparison between '&a' and '&b' has unspecified value}} + constexpr bool u5 = p < (int*)0; // expected-error {{constant expression}} expected-note {{comparison between '&a' and 'nullptr' has unspecified value}} + constexpr bool u6 = p <= (int*)0; // expected-error {{constant expression}} expected-note {{comparison between '&a' and 'nullptr' has unspecified value}} + constexpr bool u7 = p > (int*)0; // expected-error {{constant expression}} expected-note {{comparison between '&a' and 'nullptr' has unspecified value}} + constexpr bool u8 = p >= (int*)0; // expected-error {{constant expression}} expected-note {{comparison between '&a' and 'nullptr' has unspecified value}} + constexpr bool u9 = (int*)0 < q; // expected-error {{constant expression}} expected-note {{comparison between 'nullptr' and '&b' has unspecified value}} + constexpr bool u10 = (int*)0 <= q; // expected-error {{constant expression}} expected-note {{comparison between 'nullptr' and '&b' has unspecified value}} + constexpr bool u11 = (int*)0 > q; // expected-error {{constant expression}} expected-note {{comparison between 'nullptr' and '&b' has unspecified value}} + constexpr bool u12 = (int*)0 >= q; // expected-error {{constant expression}} expected-note {{comparison between 'nullptr' and '&b' has unspecified value}} void f(), g(); constexpr void (*pf)() = &f, (*pg)() = &g; - constexpr bool u13 = pf < pg; // expected-error {{constant expression}} expected-note {{comparison has unspecified value}} + constexpr bool u13 = pf < pg; // expected-error {{constant expression}} expected-note {{comparison between '&f' and '&g' has unspecified value}} // expected-warning@-1 {{ordered comparison of function pointers}} constexpr bool u14 = pf == pg; @@ -566,11 +566,11 @@ namespace UnspecifiedRelations { constexpr void *pv = (void*)&s.a; constexpr void *qv = (void*)&s.b; constexpr bool v1 = null < (int*)0; - constexpr bool v2 = null < pv; // expected-error {{constant expression}} expected-note {{comparison has unspecified value}} + constexpr bool v2 = null < pv; // expected-error {{constant expression}} expected-note {{comparison between 'nullptr' and '&s.a' has unspecified value}} constexpr bool v3 = null == pv; // ok constexpr bool v4 = qv == pv; // ok constexpr bool v5 = qv >= pv; // expected-error {{constant expression}} expected-note {{unequal pointers to void}} - constexpr bool v6 = qv > null; // expected-error {{constant expression}} expected-note {{comparison has unspecified value}} + constexpr bool v6 = qv > null; // expected-error {{constant expression}} expected-note {{comparison between '&s.b' and 'nullptr' has unspecified value}} constexpr bool v7 = qv <= (void*)&s.b; // ok constexpr bool v8 = qv > (void*)&s.a; // expected-error {{constant expression}} expected-note {{unequal pointers to void}} } diff --git a/clang/test/CodeGenCXX/weak-external.cpp b/clang/test/CodeGenCXX/weak-external.cpp index 433fb3c806249..34af2028a9d30 100644 --- a/clang/test/CodeGenCXX/weak-external.cpp +++ b/clang/test/CodeGenCXX/weak-external.cpp @@ -76,3 +76,23 @@ namespace not_weak_on_first { func, }; } + +namespace constant_eval { + [[gnu::weak]] extern int a; + // CHECK-LABEL: define {{.*}} @__cxx_global_var_init + // CHECK: store i8 zext (i1 icmp ne (ptr @_ZN13constant_eval1aE, ptr null) to i8), ptr @_ZN13constant_eval6has_a1E, + bool has_a1 = &a; + // CHECK-LABEL: define {{.*}} @__cxx_global_var_init + // CHECK: store i8 zext (i1 icmp ne (ptr @_ZN13constant_eval1aE, ptr null) to i8), ptr @_ZN13constant_eval6has_a2E, + bool has_a2 = &a != nullptr; + + struct X { + [[gnu::weak]] void f(); + }; + // CHECK-LABEL: define {{.*}} @__cxx_global_var_init + // CHECK: store i8 zext (i1 icmp ne (i64 ptrtoint (ptr @_ZN13constant_eval1X1fEv to i64), i64 0) to i8), ptr @_ZN13constant_eval6has_f1E, + bool has_f1 = &X::f; + // CHECK-LABEL: define {{.*}} @__cxx_global_var_init + // CHECK: store i8 zext (i1 icmp ne (i64 ptrtoint (ptr @_ZN13constant_eval1X1fEv to i64), i64 0) to i8), ptr @_ZN13constant_eval6has_f2E, + bool has_f2 = &X::f != nullptr; +} diff --git a/clang/test/Sema/const-eval.c b/clang/test/Sema/const-eval.c index 286a58f503ef3..bbffc79752fdd 100644 --- a/clang/test/Sema/const-eval.c +++ b/clang/test/Sema/const-eval.c @@ -138,6 +138,7 @@ void PR21945(void) { int i = (({}), 0l); } void PR24622(void); struct PR24622 {} pr24622; EVAL_EXPR(52, &pr24622 == (void *)&PR24622); // expected-error {{not an integer constant expression}} + // expected-note@-1 {{past the end}} // We evaluate these by providing 2s' complement semantics in constant // expressions, like we do for integers. diff --git a/clang/test/SemaCXX/attr-weak.cpp b/clang/test/SemaCXX/attr-weak.cpp index 51deb664ce6ea..f065bfd9483f8 100644 --- a/clang/test/SemaCXX/attr-weak.cpp +++ b/clang/test/SemaCXX/attr-weak.cpp @@ -38,3 +38,20 @@ template struct Test7<int>; class __attribute__((weak)) Test8 {}; // OK __attribute__((weak)) auto Test9 = Internal(); // expected-error {{weak declaration cannot have internal linkage}} + +[[gnu::weak]] void weak_function(); +struct WithWeakMember { + [[gnu::weak]] void weak_method(); + [[gnu::weak]] virtual void virtual_weak_method(); +}; +constexpr bool weak_function_is_non_null = &weak_function != nullptr; // expected-error {{must be initialized by a constant expression}} +// expected-note@-1 {{comparison against address of weak declaration '&weak_function' can only be performed at runtime}} +constexpr bool weak_method_is_non_null = &WithWeakMember::weak_method != nullptr; // expected-error {{must be initialized by a constant expression}} +// expected-note@-1 {{comparison against pointer to weak member 'WithWeakMember::weak_method' can only be performed at runtime}} +// GCC accepts this and says the result is always non-null. That's consistent +// with the ABI rules for member pointers, but seems unprincipled, so we do not +// follow it for now. +// TODO: Consider warning on such comparisons, as they do not test whether the +// virtual member function is present. +constexpr bool virtual_weak_method_is_non_null = &WithWeakMember::virtual_weak_method != nullptr; // expected-error {{must be initialized by a constant expression}} +// expected-note@-1 {{comparison against pointer to weak member 'WithWeakMember::virtual_weak_method' can only be performed at runtime}} diff --git a/clang/test/SemaCXX/builtins.cpp b/clang/test/SemaCXX/builtins.cpp index 300bc4a2c6fb7..3687acdeed695 100644 --- a/clang/test/SemaCXX/builtins.cpp +++ b/clang/test/SemaCXX/builtins.cpp @@ -48,6 +48,7 @@ void a(void) {} int n; void *p = __builtin_function_start(n); // expected-error {{argument must be a function}} static_assert(__builtin_function_start(a) == a, ""); // expected-error {{static assertion expression is not an integral constant expression}} +// expected-note@-1 {{comparison of addresses of literals has unspecified value}} } // namespace function_start void no_ms_builtins() { diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp index 0d2b10800fdc4..fea45d56f0fd3 100644 --- a/clang/test/SemaCXX/constant-expression-cxx11.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -359,7 +359,8 @@ struct Str { extern char externalvar[]; constexpr bool constaddress = (void *)externalvar == (void *)0x4000UL; // expected-error {{must be initialized by a constant expression}} expected-note {{reinterpret_cast}} constexpr bool litaddress = "foo" == "foo"; // expected-error {{must be initialized by a constant expression}} -// cxx20_2b-warning@-1 {{comparison between two arrays is deprecated}} +// expected-note@-1 {{comparison of addresses of literals has unspecified value}} +// cxx20_2b-warning@-2 {{comparison between two arrays is deprecated}} static_assert(0 != "foo", ""); } @@ -2181,6 +2182,7 @@ namespace PR19010 { void PR21327(int a, int b) { static_assert(&a + 1 != &b, ""); // expected-error {{constant expression}} + // expected-note@-1 {{comparison against pointer '&a + 1' that points past the end of a complete object has unspecified value}} } namespace EmptyClass { @@ -2200,6 +2202,7 @@ namespace PR21786 { extern void (*start[])(); extern void (*end[])(); static_assert(&start != &end, ""); // expected-error {{constant expression}} + // expected-note@-1 {{comparison of pointers '&start' and '&end' to unrelated zero-sized objects}} static_assert(&start != nullptr, ""); struct Foo; diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index f131df751d0ef..e171ef442e7b6 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -475,7 +475,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://wg21.link/cwg73">73</a></td> <td>TC1</td> <td>Pointer equality</td> - <td class="none" align="center">No</td> + <td class="full" align="center">Superseded by <a href="#1652">1652</a></td> </tr> <tr id="74"> <td><a href="https://wg21.link/cwg74">74</a></td> @@ -9719,7 +9719,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://wg21.link/cwg1652">1652</a></td> <td>CD4</td> <td>Object addresses in <TT>constexpr</TT> expressions</td> - <td class="none" align="center">Unknown</td> + <td class="full" align="center">Clang 3.6</td> </tr> <tr id="1653"> <td><a href="https://wg21.link/cwg1653">1653</a></td> diff --git a/clang/www/make_cxx_dr_status b/clang/www/make_cxx_dr_status index 1b4fdb6c61846..205922169ebe8 100755 --- a/clang/www/make_cxx_dr_status +++ b/clang/www/make_cxx_dr_status @@ -15,10 +15,14 @@ class DR: return '%s (%s): %s' % (self.issue, self.status, self.title) def parse(dr): - section, issue_link, status, liaison, title = [ - col.split('>', 1)[1].split('</TD>')[0] - for col in dr.split('</TR>', 1)[0].split('<TD')[1:] - ] + try: + section, issue_link, status, liaison, title = [ + col.split('>', 1)[1].split('</TD>')[0] + for col in dr.split('</TR>', 1)[0].split('<TD')[1:] + ] + except Exception as ex: + print(f"Parse error: {ex}\n{dr}", file=sys.stderr) + sys.exit(1) _, url, issue = issue_link.split('"', 2) url = url.strip() issue = int(issue.split('>', 1)[1].split('<', 1)[0]) _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits