https://gcc.gnu.org/g:56b11629cbe2e8b45c8fa408d3f58c0f5c0f8459
commit r16-4783-g56b11629cbe2e8b45c8fa408d3f58c0f5c0f8459 Author: Arthur Cohen <[email protected]> Date: Wed Aug 13 11:39:58 2025 +0200 gccrs: expand: Add handling for macro expansion in pattern context gcc/rust/ChangeLog: * ast/rust-ast-fragment.cc (Fragment::is_pattern_fragment): New function. (Fragment::take_pattern_fragment): Likewise. (Fragment::assert_single_fragment): Likewise. * ast/rust-ast-fragment.h: Declare them. * ast/rust-ast.cc (SingleASTNode::SingleASTNode): Add new constructor for pattern single AST nodes. (SingleASTNode::operator=): Handle patterns. (SingleASTNode::accept_vis): Likewise. (SingleASTNode::is_error): Likewise. (SingleASTNode::as_string): Likewise. * ast/rust-ast.h: Add get_pattern_ptr() functions. * ast/rust-expr.h: Likewise. * ast/rust-item.h: Likewise. * ast/rust-pattern.h: Likewise. * ast/rust-stmt.h: Likewise. * expand/rust-expand-visitor.cc (derive_item): Use new API enum values. (expand_item_attribute): Likewise. (expand_stmt_attribute): Likewise. (ExpandVisitor::maybe_expand_pattern): New function. (ExpandVisitor::expand_closure_params): Handle patterns. (ExpandVisitor::visit): Add new visitors for patterns and missed exprs. * expand/rust-expand-visitor.h: Declare them. * expand/rust-macro-expand.cc (transcribe_pattern): New function. (transcribe_context): Call it. * expand/rust-macro-expand.h (struct MacroExpander): New Context type. gcc/testsuite/ChangeLog: * rust/compile/issue-3726.rs: New test. * rust/compile/issue-3898.rs: New test. Diff: --- gcc/rust/ast/rust-ast-fragment.cc | 14 ++++ gcc/rust/ast/rust-ast-fragment.h | 2 + gcc/rust/ast/rust-ast.cc | 16 +++++ gcc/rust/ast/rust-ast.h | 12 ++++ gcc/rust/ast/rust-expr.h | 6 ++ gcc/rust/ast/rust-item.h | 12 ++++ gcc/rust/ast/rust-pattern.h | 6 ++ gcc/rust/ast/rust-stmt.h | 6 ++ gcc/rust/expand/rust-expand-visitor.cc | 104 ++++++++++++++++++++++++++-- gcc/rust/expand/rust-expand-visitor.h | 29 +++++--- gcc/rust/expand/rust-macro-expand.cc | 24 +++++++ gcc/rust/expand/rust-macro-expand.h | 1 + gcc/testsuite/rust/compile/issue-3726.rs | 17 +++++ gcc/testsuite/rust/compile/issue-3898.rs | 112 +++++++++++++++++++++++++++++++ 14 files changed, 344 insertions(+), 17 deletions(-) diff --git a/gcc/rust/ast/rust-ast-fragment.cc b/gcc/rust/ast/rust-ast-fragment.cc index 056fcac55b0b..8a547b4bcb55 100644 --- a/gcc/rust/ast/rust-ast-fragment.cc +++ b/gcc/rust/ast/rust-ast-fragment.cc @@ -116,6 +116,12 @@ Fragment::is_type_fragment () const return is_single_fragment_of_kind (SingleASTNode::Kind::Type); } +bool +Fragment::is_pattern_fragment () const +{ + return is_single_fragment_of_kind (SingleASTNode::Kind::Pattern); +} + std::unique_ptr<Expr> Fragment::take_expression_fragment () { @@ -130,6 +136,13 @@ Fragment::take_type_fragment () return nodes[0].take_type (); } +std::unique_ptr<Pattern> +Fragment::take_pattern_fragment () +{ + assert_single_fragment (SingleASTNode::Kind::Pattern); + return nodes[0].take_pattern (); +} + void Fragment::accept_vis (ASTVisitor &vis) { @@ -159,6 +172,7 @@ Fragment::assert_single_fragment (SingleASTNode::Kind expected) const {SingleASTNode::Kind::Expr, "expr"}, {SingleASTNode::Kind::Stmt, "stmt"}, {SingleASTNode::Kind::Extern, "extern"}, + {SingleASTNode::Kind::Pattern, "pattern"}, }; auto actual = nodes[0].get_kind (); diff --git a/gcc/rust/ast/rust-ast-fragment.h b/gcc/rust/ast/rust-ast-fragment.h index d7584d0697c2..23f26d3e6169 100644 --- a/gcc/rust/ast/rust-ast-fragment.h +++ b/gcc/rust/ast/rust-ast-fragment.h @@ -86,9 +86,11 @@ public: bool is_expression_fragment () const; bool is_type_fragment () const; + bool is_pattern_fragment () const; std::unique_ptr<Expr> take_expression_fragment (); std::unique_ptr<Type> take_type_fragment (); + std::unique_ptr<Pattern> take_pattern_fragment (); void accept_vis (ASTVisitor &vis); diff --git a/gcc/rust/ast/rust-ast.cc b/gcc/rust/ast/rust-ast.cc index c9b6fcef9d6c..7feb7a688f7d 100644 --- a/gcc/rust/ast/rust-ast.cc +++ b/gcc/rust/ast/rust-ast.cc @@ -71,6 +71,10 @@ SingleASTNode::SingleASTNode (SingleASTNode const &other) case Kind::Type: type = other.type->clone_type (); break; + + case Kind::Pattern: + pattern = other.pattern->clone_pattern (); + break; } } @@ -103,6 +107,10 @@ SingleASTNode::operator= (SingleASTNode const &other) case Kind::Type: type = other.type->clone_type (); break; + + case Kind::Pattern: + pattern = other.pattern->clone_pattern (); + break; } return *this; } @@ -135,6 +143,10 @@ SingleASTNode::accept_vis (ASTVisitor &vis) case Kind::Type: type->accept_vis (vis); break; + + case Kind::Pattern: + pattern->accept_vis (vis); + break; } } @@ -155,6 +167,8 @@ SingleASTNode::is_error () return assoc_item == nullptr; case Kind::Type: return type == nullptr; + case Kind::Pattern: + return pattern == nullptr; } rust_unreachable (); @@ -178,6 +192,8 @@ SingleASTNode::as_string () const return "Associated Item: " + assoc_item->as_string (); case Kind::Type: return "Type: " + type->as_string (); + case Kind::Pattern: + return "Pattern: " + pattern->as_string (); } rust_unreachable (); diff --git a/gcc/rust/ast/rust-ast.h b/gcc/rust/ast/rust-ast.h index 148e297c5434..a7e62965201f 100644 --- a/gcc/rust/ast/rust-ast.h +++ b/gcc/rust/ast/rust-ast.h @@ -1970,6 +1970,7 @@ public: Extern, Assoc, Type, + Pattern, }; private: @@ -1982,6 +1983,7 @@ private: std::unique_ptr<ExternalItem> external_item; std::unique_ptr<AssociatedItem> assoc_item; std::unique_ptr<Type> type; + std::unique_ptr<Pattern> pattern; public: SingleASTNode (std::unique_ptr<Expr> expr) @@ -2008,6 +2010,10 @@ public: : kind (Kind::Type), type (std::move (type)) {} + SingleASTNode (std::unique_ptr<Pattern> pattern) + : kind (Kind::Pattern), pattern (std::move (pattern)) + {} + SingleASTNode (SingleASTNode const &other); SingleASTNode operator= (SingleASTNode const &other); @@ -2076,6 +2082,12 @@ public: return std::move (type); } + std::unique_ptr<Pattern> take_pattern () + { + rust_assert (!is_error ()); + return std::move (pattern); + } + void accept_vis (ASTVisitor &vis) override; bool is_error (); diff --git a/gcc/rust/ast/rust-expr.h b/gcc/rust/ast/rust-expr.h index ee3919a9a910..94d9ba13f1df 100644 --- a/gcc/rust/ast/rust-expr.h +++ b/gcc/rust/ast/rust-expr.h @@ -2492,6 +2492,12 @@ public: return *pattern; } + std::unique_ptr<Pattern> &get_pattern_ptr () + { + rust_assert (pattern != nullptr); + return pattern; + } + Type &get_type () { rust_assert (has_type_given ()); diff --git a/gcc/rust/ast/rust-item.h b/gcc/rust/ast/rust-item.h index 375f66ddcc1e..3fd49f4b199d 100644 --- a/gcc/rust/ast/rust-item.h +++ b/gcc/rust/ast/rust-item.h @@ -631,6 +631,12 @@ public: return *param_name; } + std::unique_ptr<Pattern> &get_pattern_ptr () + { + rust_assert (param_name != nullptr); + return param_name; + } + const Pattern &get_pattern () const { rust_assert (param_name != nullptr); @@ -714,6 +720,12 @@ public: return *param_name; } + std::unique_ptr<Pattern> &get_pattern_ptr () + { + rust_assert (param_name != nullptr); + return param_name; + } + bool has_name () const { return param_name != nullptr; } // TODO: is this better? Or is a "vis_block" better? diff --git a/gcc/rust/ast/rust-pattern.h b/gcc/rust/ast/rust-pattern.h index 4948159f5376..029a5b36c6a7 100644 --- a/gcc/rust/ast/rust-pattern.h +++ b/gcc/rust/ast/rust-pattern.h @@ -1509,6 +1509,12 @@ public: return *pattern_in_parens; } + std::unique_ptr<Pattern> &get_pattern_in_parens_ptr () + { + rust_assert (pattern_in_parens != nullptr); + return pattern_in_parens; + } + NodeId get_node_id () const override { return node_id; } Pattern::Kind get_pattern_kind () override { return Pattern::Kind::Grouped; } diff --git a/gcc/rust/ast/rust-stmt.h b/gcc/rust/ast/rust-stmt.h index f843a79b3f99..5fb00ef18057 100644 --- a/gcc/rust/ast/rust-stmt.h +++ b/gcc/rust/ast/rust-stmt.h @@ -201,6 +201,12 @@ public: return *variables_pattern; } + std::unique_ptr<Pattern> &get_pattern_ptr () + { + rust_assert (variables_pattern != nullptr); + return variables_pattern; + } + Type &get_type () { rust_assert (has_type ()); diff --git a/gcc/rust/expand/rust-expand-visitor.cc b/gcc/rust/expand/rust-expand-visitor.cc index a53f0640109d..c1833c276c5a 100644 --- a/gcc/rust/expand/rust-expand-visitor.cc +++ b/gcc/rust/expand/rust-expand-visitor.cc @@ -18,6 +18,7 @@ #include "rust-expand-visitor.h" #include "rust-ast-fragment.h" +#include "rust-item.h" #include "rust-proc-macro.h" #include "rust-attributes.h" #include "rust-ast.h" @@ -62,7 +63,7 @@ derive_item (AST::Item &item, AST::SimplePath &to_derive, { switch (node.get_kind ()) { - case AST::SingleASTNode::ITEM: + case AST::SingleASTNode::Kind::Item: result.push_back (node.take_item ()); break; default: @@ -85,7 +86,7 @@ expand_item_attribute (AST::Item &item, AST::SimplePath &name, { switch (node.get_kind ()) { - case AST::SingleASTNode::ITEM: + case AST::SingleASTNode::Kind::Item: result.push_back (node.take_item ()); break; default: @@ -114,7 +115,7 @@ expand_stmt_attribute (T &statement, AST::SimplePath &attribute, { switch (node.get_kind ()) { - case AST::SingleASTNode::STMT: + case AST::SingleASTNode::Kind::Stmt: result.push_back (node.take_stmt ()); break; default: @@ -380,6 +381,23 @@ ExpandVisitor::maybe_expand_type (std::unique_ptr<AST::TypeNoBounds> &type) final_fragment.take_type_fragment (), BUILTINS_LOCATION); } +void +ExpandVisitor::maybe_expand_pattern (std::unique_ptr<AST::Pattern> &pattern) +{ + NodeId old_expect = pattern->get_node_id (); + std::swap (macro_invoc_expect_id, old_expect); + + expander.push_context (MacroExpander::ContextType::PATTERN); + pattern->accept_vis (*this); + expander.pop_context (); + + std::swap (macro_invoc_expect_id, old_expect); + + auto final_fragment = expander.take_expanded_fragment (); + if (final_fragment.should_expand () && final_fragment.is_pattern_fragment ()) + pattern = final_fragment.take_pattern_fragment (); +} + // FIXME: Can this be refactored into a `scoped` method? Which takes a // ContextType as parameter and a lambda? And maybe just an std::vector<T>&? void @@ -452,6 +470,8 @@ ExpandVisitor::expand_closure_params (std::vector<AST::ClosureParam> ¶ms) { for (auto ¶m : params) { + maybe_expand_pattern (param.get_pattern_ptr ()); + if (param.has_type_given ()) maybe_expand_type (param.get_type_ptr ()); } @@ -729,7 +749,7 @@ ExpandVisitor::visit (AST::MatchExpr &expr) auto &arm = match_case.get_arm (); for (auto &pattern : arm.get_patterns ()) - visit (pattern); + maybe_expand_pattern (pattern); if (arm.has_match_arm_guard ()) maybe_expand_expr (arm.get_guard_expr_ptr ()); @@ -738,6 +758,13 @@ ExpandVisitor::visit (AST::MatchExpr &expr) } } +void +ExpandVisitor::visit (AST::TupleExpr &expr) +{ + for (auto &sub : expr.get_tuple_elems ()) + maybe_expand_expr (sub); +} + void ExpandVisitor::visit (AST::TypeParam ¶m) { @@ -1013,13 +1040,70 @@ ExpandVisitor::visit (AST::StructPatternFieldIdent &field) void ExpandVisitor::visit (AST::GroupedPattern &pattern) { - visit (pattern.get_pattern_in_parens ()); + maybe_expand_pattern (pattern.get_pattern_in_parens_ptr ()); +} + +void +ExpandVisitor::visit (AST::SlicePatternItemsNoRest &items) +{ + for (auto &sub : items.get_patterns ()) + maybe_expand_pattern (sub); +} + +void +ExpandVisitor::visit (AST::SlicePatternItemsHasRest &items) +{ + for (auto &sub : items.get_lower_patterns ()) + maybe_expand_pattern (sub); + for (auto &sub : items.get_upper_patterns ()) + maybe_expand_pattern (sub); +} + +void +ExpandVisitor::visit (AST::AltPattern &pattern) +{ + for (auto &alt : pattern.get_alts ()) + maybe_expand_pattern (alt); +} + +void +ExpandVisitor::visit (AST::TupleStructItemsNoRange &tuple_items) +{ + for (auto &sub : tuple_items.get_patterns ()) + maybe_expand_pattern (sub); +} + +void +ExpandVisitor::visit (AST::TupleStructItemsRange &tuple_items) +{ + for (auto &sub : tuple_items.get_lower_patterns ()) + maybe_expand_pattern (sub); + + for (auto &sub : tuple_items.get_upper_patterns ()) + maybe_expand_pattern (sub); +} + +void +ExpandVisitor::visit (AST::TuplePatternItemsMultiple &tuple_items) +{ + for (auto &sub : tuple_items.get_patterns ()) + maybe_expand_pattern (sub); +} + +void +ExpandVisitor::visit (AST::TuplePatternItemsRanged &tuple_items) +{ + for (auto &sub : tuple_items.get_lower_patterns ()) + maybe_expand_pattern (sub); + + for (auto &sub : tuple_items.get_upper_patterns ()) + maybe_expand_pattern (sub); } void ExpandVisitor::visit (AST::LetStmt &stmt) { - visit (stmt.get_pattern ()); + maybe_expand_pattern (stmt.get_pattern_ptr ()); if (stmt.has_type ()) maybe_expand_type (stmt.get_type_ptr ()); @@ -1049,9 +1133,17 @@ ExpandVisitor::visit (AST::BareFunctionType &type) void ExpandVisitor::visit (AST::FunctionParam ¶m) { + maybe_expand_pattern (param.get_pattern_ptr ()); maybe_expand_type (param.get_type_ptr ()); } +void +ExpandVisitor::visit (AST::VariadicParam ¶m) +{ + if (param.has_pattern ()) + maybe_expand_pattern (param.get_pattern_ptr ()); +} + void ExpandVisitor::visit (AST::SelfParam ¶m) { diff --git a/gcc/rust/expand/rust-expand-visitor.h b/gcc/rust/expand/rust-expand-visitor.h index 01a0d1cb2975..8fee291d5959 100644 --- a/gcc/rust/expand/rust-expand-visitor.h +++ b/gcc/rust/expand/rust-expand-visitor.h @@ -20,6 +20,7 @@ #define RUST_EXPAND_VISITOR_H #include "rust-ast-visitor.h" +#include "rust-item.h" #include "rust-macro-expand.h" #include "rust-proc-macro.h" @@ -47,18 +48,15 @@ public: using AST::DefaultASTVisitor::visit; - /* - Maybe expand a macro invocation in lieu of an expression - expr : Core guidelines R33, this function reseat the pointer. - */ - void maybe_expand_expr (std::unique_ptr<AST::Expr> &expr); - - /* - Maybe expand a macro invocation in lieu of a type - type : Core guidelines R33, this function reseat the pointer. + /** + * Maybe expand a macro invocation in lieu of an expression, type or pattern. + * + * @ptr Core guidelines R33, this function reseats the pointer. */ - void maybe_expand_type (std::unique_ptr<AST::Type> &type); + void maybe_expand_expr (std::unique_ptr<AST::Expr> &ptr); + void maybe_expand_type (std::unique_ptr<AST::Type> &ptr); void maybe_expand_type (std::unique_ptr<AST::TypeNoBounds> &type); + void maybe_expand_pattern (std::unique_ptr<AST::Pattern> &ptr); /** * Expand all macro invocations in lieu of types within a vector of struct @@ -239,6 +237,7 @@ public: void visit (AST::IfLetExpr &expr) override; void visit (AST::IfLetExprConseqElse &expr) override; void visit (AST::MatchExpr &expr) override; + void visit (AST::TupleExpr &expr) override; void visit (AST::TypeParam ¶m) override; void visit (AST::LifetimeWhereClauseItem &) override; void visit (AST::TypeBoundWhereClauseItem &item) override; @@ -276,12 +275,20 @@ public: void visit (AST::MetaListNameValueStr &) override; void visit (AST::StructPatternFieldIdent &field) override; void visit (AST::GroupedPattern &pattern) override; + void visit (AST::SlicePatternItemsNoRest &items) override; + void visit (AST::SlicePatternItemsHasRest &items) override; + void visit (AST::AltPattern &pattern) override; + void visit (AST::TupleStructItemsNoRange &tuple_items) override; + void visit (AST::TupleStructItemsRange &tuple_items) override; + void visit (AST::TuplePatternItemsMultiple &tuple_items) override; + void visit (AST::TuplePatternItemsRanged &tuple_items) override; void visit (AST::LetStmt &stmt) override; void visit (AST::ExprStmt &stmt) override; void visit (AST::BareFunctionType &type) override; - void visit (AST::FunctionParam &type) override; + void visit (AST::FunctionParam ¶m) override; + void visit (AST::VariadicParam ¶m) override; void visit (AST::SelfParam &type) override; template <typename T> diff --git a/gcc/rust/expand/rust-macro-expand.cc b/gcc/rust/expand/rust-macro-expand.cc index dfead3acc1d6..edaf8910d063 100644 --- a/gcc/rust/expand/rust-macro-expand.cc +++ b/gcc/rust/expand/rust-macro-expand.cc @@ -1000,6 +1000,27 @@ transcribe_type (Parser<MacroInvocLexer> &parser) return AST::Fragment ({std::move (type)}, lexer.get_token_slice (start, end)); } +/** + * Transcribe one pattern from a macro invocation + * + * @param parser Parser to extract statements from + */ +static AST::Fragment +transcribe_pattern (Parser<MacroInvocLexer> &parser) +{ + auto &lexer = parser.get_token_source (); + auto start = lexer.get_offs (); + + auto pattern = parser.parse_pattern (); + for (auto err : parser.get_errors ()) + err.emit (); + + auto end = lexer.get_offs (); + + return AST::Fragment ({std::move (pattern)}, + lexer.get_token_slice (start, end)); +} + static AST::Fragment transcribe_context (MacroExpander::ContextType ctx, Parser<MacroInvocLexer> &parser, bool semicolon, @@ -1012,6 +1033,7 @@ transcribe_context (MacroExpander::ContextType ctx, // -- Trait --> parser.parse_trait_item(); // -- Impl --> parser.parse_impl_item(); // -- Extern --> parser.parse_extern_item(); + // -- Pattern --> parser.parse_pattern(); // -- None --> [has semicolon?] // -- Yes --> parser.parse_stmt(); // -- No --> [switch invocation.delimiter()] @@ -1040,6 +1062,8 @@ transcribe_context (MacroExpander::ContextType ctx, break; case MacroExpander::ContextType::TYPE: return transcribe_type (parser); + case MacroExpander::ContextType::PATTERN: + return transcribe_pattern (parser); break; case MacroExpander::ContextType::STMT: return transcribe_many_stmts (parser, last_token_id, semicolon); diff --git a/gcc/rust/expand/rust-macro-expand.h b/gcc/rust/expand/rust-macro-expand.h index 360294c6bf9f..901583f5d8a2 100644 --- a/gcc/rust/expand/rust-macro-expand.h +++ b/gcc/rust/expand/rust-macro-expand.h @@ -291,6 +291,7 @@ struct MacroExpander TRAIT, IMPL, TRAIT_IMPL, + PATTERN, }; ExpansionCfg cfg; diff --git a/gcc/testsuite/rust/compile/issue-3726.rs b/gcc/testsuite/rust/compile/issue-3726.rs new file mode 100644 index 000000000000..ced87a57457a --- /dev/null +++ b/gcc/testsuite/rust/compile/issue-3726.rs @@ -0,0 +1,17 @@ +pub enum TypeCtor { + Slice, + Array, +} +pub struct ApplicationTy(TypeCtor); + +macro_rules! ty_app { + ($ctor:pat) => { + ApplicationTy($ctor) + }; +} + +pub fn foo(ty: ApplicationTy) { + match ty { + ty_app!(TypeCtor::Array) => {} + } +} diff --git a/gcc/testsuite/rust/compile/issue-3898.rs b/gcc/testsuite/rust/compile/issue-3898.rs new file mode 100644 index 000000000000..114370cf8815 --- /dev/null +++ b/gcc/testsuite/rust/compile/issue-3898.rs @@ -0,0 +1,112 @@ +// { dg-additional-options "-frust-compile-until=lowering" } + +#[lang = "sized"] +trait Sized {} + +enum Result<T, E> { + Ok(T), + Err(E), +} + +use Result::{Err, Ok}; + +struct Utf8Error; + +const CONT_MASK: u8 = 15; +const TAG_CONT_U8: u8 = 15; + +#[inline(always)] +pub fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { + let mut index = 0; + let len = 64; + + let usize_bytes = 8; + let ascii_block_size = 2 * usize_bytes; + let blocks_end = if len >= ascii_block_size { + len - ascii_block_size + 1 + } else { + 0 + }; + + while index < len { + let old_offset = index; + macro_rules! err { + ($error_len: expr) => { + return Err(Utf8Error) + }; + } + + macro_rules! next { + () => {{ + index += 1; + // we needed data, but there was none: error! + if index >= len { + err!(None) + } + v[index] + }}; + } + + let first = v[index]; + if first >= 128 { + let w = 15; + // 2-byte encoding is for codepoints \u{0080} to \u{07ff} + // first C2 80 last DF BF + // 3-byte encoding is for codepoints \u{0800} to \u{ffff} + // first E0 A0 80 last EF BF BF + // excluding surrogates codepoints \u{d800} to \u{dfff} + // ED A0 80 to ED BF BF + // 4-byte encoding is for codepoints \u{1000}0 to \u{10ff}ff + // first F0 90 80 80 last F4 8F BF BF + // + // Use the UTF-8 syntax from the RFC + // + // https://tools.ietf.org/html/rfc3629 + // UTF8-1 = %x00-7F + // UTF8-2 = %xC2-DF UTF8-tail + // UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / + // %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) + // UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / + // %xF4 %x80-8F 2( UTF8-tail ) + match w { + 2 => { + if next!() & !CONT_MASK != TAG_CONT_U8 { + err!(Some(1)) + } + } + 3 => { + match (first, next!()) { + (0xE0, 0xA0..=0xBF) + | (0xE1..=0xEC, 0x80..=0xBF) + | (0xED, 0x80..=0x9F) + | (0xEE..=0xEF, 0x80..=0xBF) => {} + _ => err!(Some(1)), + } + if next!() & !CONT_MASK != TAG_CONT_U8 { + err!(Some(2)) + } + } + 4 => { + match (first, next!()) { + (0xF0, 0x90..=0xBF) | (0xF1..=0xF3, 0x80..=0xBF) | (0xF4, 0x80..=0x8F) => {} + _ => err!(Some(1)), + } + if next!() & !CONT_MASK != TAG_CONT_U8 { + err!(Some(2)) + } + if next!() & !CONT_MASK != TAG_CONT_U8 { + err!(Some(3)) + } + } + _ => err!(Some(1)), + } + index += 1; + } else { + index += 1; + } + } + + Ok(()) +} + +fn main() {}
