From patchwork Fri Feb 4 14:12:14 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 50802 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 0F2ED3858C2C for ; Fri, 4 Feb 2022 14:12:51 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0F2ED3858C2C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1643983971; bh=6iN91ucxhsUfXfC4Kdg48/g3hAj8EEeyL1jo8YXA6/8=; h=Date:To:Subject:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=MKLlTd2A144/5FSCcF7U6G79+Z6QXcad61a3zF+SsxhNPQZIUGbjaE2YpJMruiyy6 6Ws+lFaLeDINakVhqFex5RGWK0FO3tYUksqZ7OLRrbfoDyBogCiqypxqCkEwMfa1Fe Rdxigc7YViucLJrlhhd5TwynbRAhfeY0QvCjxBRc= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 0724E3858D28 for ; Fri, 4 Feb 2022 14:12:22 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 0724E3858D28 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-178-siKt9YVgMCWGUhinMzgEgg-1; Fri, 04 Feb 2022 09:12:18 -0500 X-MC-Unique: siKt9YVgMCWGUhinMzgEgg-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 2441A64083 for ; Fri, 4 Feb 2022 14:12:18 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.39.192.125]) by smtp.corp.redhat.com (Postfix) with ESMTPS id AFFC454558; Fri, 4 Feb 2022 14:12:17 +0000 (UTC) Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.16.1/8.16.1) with ESMTPS id 214ECFgS141715 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Fri, 4 Feb 2022 15:12:15 +0100 Received: (from jakub@localhost) by tucnak.zalov.cz (8.16.1/8.16.1/Submit) id 214ECE8Y141714; Fri, 4 Feb 2022 15:12:14 +0100 Date: Fri, 4 Feb 2022 15:12:14 +0100 To: Jason Merrill Subject: [PATCH] c++: Improve diagnostics for template args terminated with >= or >>= [PR104319] Message-ID: <20220204141214.GG2646553@tucnak> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-5.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Jakub Jelinek via Gcc-patches From: Jakub Jelinek Reply-To: Jakub Jelinek Cc: gcc-patches@gcc.gnu.org Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" Hi! As mentioned in the PR, for C++98 we have diagnostics that expect >> terminating template arguments to be a mistake for > > (C++11 said it has to be treated that way), while if user trying to spare the spacebar doesn't separate > from following = or >> from following =, the diagnostics is confusing, while clang suggests adding space in between. The following patch does that for >= and >>= too. For some strange reason the error recovery emits further errors, not really sure what's going on because I overwrite the token->type like the code does for the C++11 >> case or for the C++98 >> cases, but at least the first error is nicer (well, for the C++98 nested template case and >>= I need to overwrite it to > and so the = is lost, so perhaps some follow-up errors are needed for that case). Bootstrapped/regtested on powerpc64le-linux, ok for trunk? Or shall it wait for GCC 13? 2022-02-04 Jakub Jelinek PR c++/104319 * parser.cc (cp_parser_template_argument): Treat >= like C++98 >> after a type id by setting maybe_type_id and aborting tentative parse. (cp_parser_enclosed_template_argument_list): Handle CPP_GREATER_EQ like misspelled CPP_GREATER CPP_RQ and CPP_RSHIFT_EQ like misspelled CPP_GREATER CPP_GREATER_EQ or CPP_RSHIFT CPP_EQ or CPP_GREATER CPP_GREATER CPP_EQ. (cp_parser_next_token_ends_template_argument_p): Return true also for CPP_GREATER_EQ and CPP_RSHIFT_EQ. * g++.dg/parse/template28.C: Adjust expected diagnostics. * g++.dg/parse/template30.C: New test. Jakub --- gcc/cp/parser.cc.jj 2022-02-04 14:36:54.765608651 +0100 +++ gcc/cp/parser.cc 2022-02-04 14:42:14.761139259 +0100 @@ -18820,8 +18820,13 @@ cp_parser_template_argument (cp_parser* In C++0x, the '>>' will be considered two separate '>' tokens. */ if (!cp_parser_error_occurred (parser) - && cxx_dialect == cxx98 - && cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT)) + && ((cxx_dialect == cxx98 + && cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT)) + /* Similarly for >= which + cp_parser_next_token_ends_template_argument_p treats for + diagnostics purposes as mistyped > =, but can be valid + after a type-id. */ + || cp_lexer_next_token_is (parser->lexer, CPP_GREATER_EQ))) { maybe_type_id = true; cp_parser_abort_tentative_parse (parser); @@ -32029,7 +32034,9 @@ cp_parser_enclosed_template_argument_lis cp_evaluated ev; /* Parse the template-argument-list itself. */ if (cp_lexer_next_token_is (parser->lexer, CPP_GREATER) - || cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT)) + || cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT) + || cp_lexer_next_token_is (parser->lexer, CPP_GREATER_EQ) + || cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT_EQ)) arguments = NULL_TREE; else arguments = cp_parser_template_argument_list (parser); @@ -32086,6 +32093,38 @@ cp_parser_enclosed_template_argument_lis "a template argument list"); } } + /* Similarly for >>= and >=. */ + else if (cp_lexer_next_token_is (parser->lexer, CPP_GREATER_EQ) + || cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT_EQ)) + { + cp_token *token = cp_lexer_consume_token (parser->lexer); + gcc_rich_location richloc (token->location); + enum cpp_ttype new_type; + const char *replacement; + if (token->type == CPP_GREATER_EQ) + { + replacement = "> ="; + new_type = CPP_EQ; + } + else if (!saved_greater_than_is_operator_p) + { + if (cxx_dialect != cxx98) + replacement = ">> ="; + else + replacement = "> > ="; + new_type = CPP_GREATER; + } + else + { + replacement = "> >="; + new_type = CPP_GREATER_EQ; + } + richloc.add_fixit_replace (replacement); + error_at (&richloc, "%qs should be %qs to terminate a template " + "argument list", + cpp_type2name (token->type, token->flags), replacement); + token->type = new_type; + } else cp_parser_require_end_of_template_parameter_list (parser); /* The `>' token might be a greater-than operator again now. */ @@ -33163,7 +33202,11 @@ cp_parser_next_token_ends_template_argum return (token->type == CPP_COMMA || token->type == CPP_GREATER || token->type == CPP_ELLIPSIS - || ((cxx_dialect != cxx98) && token->type == CPP_RSHIFT)); + || ((cxx_dialect != cxx98) && token->type == CPP_RSHIFT) + /* For better diagnostics, treat >>= like that too, that + shouldn't appear non-nested in template arguments. */ + || token->type == CPP_GREATER_EQ + || token->type == CPP_RSHIFT_EQ); } /* Returns TRUE iff the n-th token is a "<", or the n-th is a "[" and the --- gcc/testsuite/g++.dg/parse/template28.C.jj 2020-01-12 11:54:37.228401112 +0100 +++ gcc/testsuite/g++.dg/parse/template28.C 2022-02-04 14:51:13.387630759 +0100 @@ -2,8 +2,8 @@ template struct A {}; -template void foo(A=A()) {} // { dg-error "24:variable or field .foo. declared void" } -// { dg-error "template" "" { target *-*-* } .-1 } +template void foo(A=A()) {} // { dg-error "'>=' should be '> =' to terminate a template argument list" } +// { dg-error "expected" "" { target *-*-* } .-1 } void bar() { --- gcc/testsuite/g++.dg/parse/template30.C.jj 2022-02-04 14:42:14.761139259 +0100 +++ gcc/testsuite/g++.dg/parse/template30.C 2022-02-04 14:42:14.761139259 +0100 @@ -0,0 +1,49 @@ +// PR c++/104319 +// { dg-do compile } +// { dg-options "" } + +template struct A {}; +template int z; // { dg-warning "variable templates only available with" "" { target c++11_down } } +template int w; // { dg-warning "variable templates only available with" "" { target c++11_down } } + +void +foo () +{ + z=0; // { dg-error "'>=' should be '> =' to terminate a template argument list" } +} // { dg-error "expected ';' before numeric constant" "" { target *-*-* } .-1 } + +int +bar () +{ + return z>0; // { dg-error "spurious '>>', use '>' to terminate a template argument list" "" { target c++98_only } } +} // { dg-error "expected ';' before numeric constant" "" { target c++98_only } .-1 } + +int +baz () +{ + return z>=0; // { dg-error "'>>=' should be '> >=' to terminate a template argument list" } +} // { dg-error "expected ';' before numeric constant" "" { target *-*-* } .-1 } + +int +qux () +{ + return z>=0; // { dg-error "'>>=' should be '>> =' to terminate a template argument list" "" { target c++11 } } +} // { dg-error "'>>=' should be '> > =' to terminate a template argument list" "" { target c++98_only } .-1 } + // { dg-error "parse error in template argument list" "" { target *-*-* } .-2 } + // { dg-error "template argument 1 is invalid" "" { target *-*-* } .-3 } + +void +quux () +{ + w<5>=0>=6>=8> = 5; +} + +#if __cplusplus >= 201103L +struct B { constexpr bool operator >= (int) { return true; } }; + +void +corge () +{ + w=5> = 5; +} +#endif