From: Owen Avery Date: Sun, 28 May 2023 17:33:52 +0000 (-0400) Subject: gccrs: Avoid invalid metavariable substitutions X-Git-Tag: basepoints/gcc-15~2519 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=57c5d234792b6bc00f3ef29148733617db8c86d2;p=thirdparty%2Fgcc.git gccrs: Avoid invalid metavariable substitutions gcc/rust/ChangeLog: * expand/rust-macro-substitute-ctx.cc (SubstituteCtx::substitute_metavar): Allow substitution failure. (SubstituteCtx::substitute_token): Handle substitution failure, include dollar sign in count of tokens to substitute. (SubstituteCtx::substitute_tokens): Include dollar sign in count of tokens to substitute. * expand/rust-macro-substitute-ctx.h (SubstituteCtx::substitute_metavar): Adjust signature and document new substitution failure handling. gcc/testsuite/ChangeLog: * rust/compile/issue-2225.rs: New test. Signed-off-by: Owen Avery --- diff --git a/gcc/rust/expand/rust-macro-substitute-ctx.cc b/gcc/rust/expand/rust-macro-substitute-ctx.cc index 85c9d7e01764..84df2c2623e6 100644 --- a/gcc/rust/expand/rust-macro-substitute-ctx.cc +++ b/gcc/rust/expand/rust-macro-substitute-ctx.cc @@ -2,17 +2,18 @@ namespace Rust { -std::vector> -SubstituteCtx::substitute_metavar (std::unique_ptr &metavar) +bool +SubstituteCtx::substitute_metavar ( + std::unique_ptr &metavar, + std::vector> &expanded) { auto metavar_name = metavar->get_str (); - std::vector> expanded; auto it = fragments.find (metavar_name); if (it == fragments.end ()) { - // Return a copy of the original token - expanded.push_back (metavar->clone_token ()); + // fail to substitute + return false; } else { @@ -28,7 +29,7 @@ SubstituteCtx::substitute_metavar (std::unique_ptr &metavar) metavar->get_locus (), "you probably forgot the repetition operator: %<%s%s%s%>", "$(", metavar->as_string ().c_str (), ")*"); - return expanded; + return true; } // We only care about the vector when expanding repetitions. @@ -42,7 +43,7 @@ SubstituteCtx::substitute_metavar (std::unique_ptr &metavar) } } - return expanded; + return true; } bool @@ -209,14 +210,17 @@ SubstituteCtx::substitute_token (size_t token_idx) if (token_id_is_keyword (token->get_id ())) { case IDENTIFIER: + std::vector> expanded; + rust_debug ("expanding metavar: %s", token->get_str ().c_str ()); - return {substitute_metavar (token), 1}; + + if (substitute_metavar (token, expanded)) + return {std::move (expanded), 2}; } - rust_error_at (token->get_locus (), - "unexpected token in macro transcribe: expected " - "%<(%> or identifier after %<$%>, got %<%s%>", - get_token_description (token->get_id ())); - break; + + // don't substitute, dollar sign is alone/metavar is unknown + return {std::vector> (), 0}; + case LEFT_PAREN: { // We need to parse up until the closing delimiter and expand this // fragment->n times. @@ -285,17 +289,11 @@ SubstituteCtx::substitute_token (size_t token_idx) return {substitute_repetition (pattern_start, pattern_end, std::move (separator_token)), - pattern_end - pattern_start + to_skip}; + pattern_end - pattern_start + to_skip + 1}; } - // TODO: We need to check if the $ was alone. In that case, do - // not error out: Simply act as if there was an empty identifier - // with no associated fragment and paste the dollar sign in the - // transcription. Unsure how to do that since we always have at - // least the closing curly brace after an empty $... } - // FIXME: gcc_unreachable() error case? - return {std::vector> (), 0}; + gcc_unreachable (); } std::vector> @@ -304,7 +302,7 @@ SubstituteCtx::substitute_tokens () std::vector> replaced_tokens; rust_debug ("expanding tokens"); - for (size_t i = 0; i < macro.size (); i++) + for (size_t i = 0; i < macro.size ();) { auto &tok = macro.at (i); if (tok->get_id () == DOLLAR_SIGN) @@ -315,6 +313,12 @@ SubstituteCtx::substitute_tokens () auto expanded = std::move (p.first); auto tok_to_skip = p.second; + if (!tok_to_skip) + { + replaced_tokens.emplace_back (tok->clone_token ()); + tok_to_skip++; + } + i += tok_to_skip; for (auto &token : expanded) @@ -323,6 +327,7 @@ SubstituteCtx::substitute_tokens () else { replaced_tokens.emplace_back (tok->clone_token ()); + i++; } } diff --git a/gcc/rust/expand/rust-macro-substitute-ctx.h b/gcc/rust/expand/rust-macro-substitute-ctx.h index 86dddb2a7fd0..dd49bb1ddca1 100644 --- a/gcc/rust/expand/rust-macro-substitute-ctx.h +++ b/gcc/rust/expand/rust-macro-substitute-ctx.h @@ -49,12 +49,13 @@ public: * i.e. replacing $var with the associated fragment. * * @param metavar Metavariable to try and replace + * @param expanded Reference to a vector upon which expanded tokens will be + * pushed * - * @return A token containing the associated fragment expanded into tokens if - * any, or the cloned token if no fragment was associated + * @return True iff the substitution succeeded */ - std::vector> - substitute_metavar (std::unique_ptr &metavar); + bool substitute_metavar (std::unique_ptr &metavar, + std::vector> &expanded); /** * Substitute a macro repetition by its given fragments diff --git a/gcc/testsuite/rust/compile/issue-2225.rs b/gcc/testsuite/rust/compile/issue-2225.rs new file mode 100644 index 000000000000..53757c1bae66 --- /dev/null +++ b/gcc/testsuite/rust/compile/issue-2225.rs @@ -0,0 +1,14 @@ +macro_rules! foo { + ($_:tt) => {}; + () => {foo!($);}; +} + +macro_rules! bar { + () => {let $_a = 12;} // { dg-error "unrecognised token" } +} + +pub fn main() -> i32 { + foo!(); + bar!(); + 0 +}