From d664d76d00b19a61afec13425a9421a62e437505 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Fri, 23 Sep 2016 19:38:29 +0200 Subject: [PATCH] Implement P0138R2, C++17 construction rules for enum class values Implement P0138R2, C++17 construction rules for enum class values * cp-tree.h (is_direct_enum_init): Declare. * decl.c (is_direct_enum_init): New function. (reshape_init): Use it. * typeck.c (convert_for_assignment): Likewise. * g++.dg/cpp1z/direct-enum-init1.C: New test. From-SVN: r240449 --- gcc/cp/ChangeLog | 6 + gcc/cp/cp-tree.h | 1 + gcc/cp/decl.c | 27 ++ gcc/cp/typeck.c | 11 + gcc/testsuite/ChangeLog | 5 + .../g++.dg/cpp1z/direct-enum-init1.C | 237 ++++++++++++++++++ 6 files changed, 287 insertions(+) create mode 100644 gcc/testsuite/g++.dg/cpp1z/direct-enum-init1.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 7109684c21b..a3430bf3ad5 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,11 @@ 2016-09-23 Jakub Jelinek + Implement P0138R2, C++17 construction rules for enum class values + * cp-tree.h (is_direct_enum_init): Declare. + * decl.c (is_direct_enum_init): New function. + (reshape_init): Use it. + * typeck.c (convert_for_assignment): Likewise. + * Make-lang.in (check-c++1z): Pass RUNTESTFLAGS down to make check-g++. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index f40334092a0..c7a38faa10f 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5829,6 +5829,7 @@ extern tree check_elaborated_type_specifier (enum tag_types, tree, bool); extern void warn_extern_redeclared_static (tree, tree); extern tree cxx_comdat_group (tree); extern bool cp_missing_noreturn_ok_p (tree); +extern bool is_direct_enum_init (tree, tree); extern void initialize_artificial_var (tree, vec *); extern tree check_var_type (tree, tree); extern tree reshape_init (tree, tree, tsubst_flags_t); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 24f71ce4f72..3dfa0423522 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -5581,6 +5581,22 @@ next_initializable_field (tree field) return field; } +/* Return true for [dcl.init.list] direct-list-initialization from + single element of enumeration with a fixed underlying type. */ + +bool +is_direct_enum_init (tree type, tree init) +{ + if (cxx_dialect >= cxx1z + && TREE_CODE (type) == ENUMERAL_TYPE + && ENUM_FIXED_UNDERLYING_TYPE_P (type) + && TREE_CODE (init) == CONSTRUCTOR + && CONSTRUCTOR_IS_DIRECT_INIT (init) + && CONSTRUCTOR_NELTS (init) == 1) + return true; + return false; +} + /* Subroutine of reshape_init_array and reshape_init_vector, which does the actual work. ELT_TYPE is the element type of the array. MAX_INDEX is an INTEGER_CST representing the size of the array minus one (the maximum index), @@ -6026,6 +6042,17 @@ reshape_init (tree type, tree init, tsubst_flags_t complain) if (vec_safe_is_empty (v)) return init; + /* Handle [dcl.init.list] direct-list-initialization from + single element of enumeration with a fixed underlying type. */ + if (is_direct_enum_init (type, init)) + { + tree elt = CONSTRUCTOR_ELT (init, 0)->value; + if (check_narrowing (ENUM_UNDERLYING_TYPE (type), elt, complain)) + return cp_build_c_cast (type, elt, tf_warning_or_error); + else + return error_mark_node; + } + /* Recurse on this CONSTRUCTOR. */ d.cur = &(*v)[0]; d.end = d.cur + v->length (); diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index c53a85a16e0..b2af615d89e 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -8266,6 +8266,17 @@ convert_for_assignment (tree type, tree rhs, if (TREE_CODE (rhs) == NON_LVALUE_EXPR) rhs = TREE_OPERAND (rhs, 0); + /* Handle [dcl.init.list] direct-list-initialization from + single element of enumeration with a fixed underlying type. */ + if (is_direct_enum_init (type, rhs)) + { + tree elt = CONSTRUCTOR_ELT (rhs, 0)->value; + if (check_narrowing (ENUM_UNDERLYING_TYPE (type), elt, complain)) + rhs = cp_build_c_cast (type, elt, complain); + else + rhs = error_mark_node; + } + rhstype = TREE_TYPE (rhs); coder = TREE_CODE (rhstype); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 7a853dc33e0..080e55604ec 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2016-09-23 Jakub Jelinek + + Implement P0138R2, C++17 construction rules for enum class values + * g++.dg/cpp1z/direct-enum-init1.C: New test. + 2016-09-23 David Malcolm PR preprocessor/77672 diff --git a/gcc/testsuite/g++.dg/cpp1z/direct-enum-init1.C b/gcc/testsuite/g++.dg/cpp1z/direct-enum-init1.C new file mode 100644 index 00000000000..b473ed553c9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/direct-enum-init1.C @@ -0,0 +1,237 @@ +// P0138R2 - direct-list-initialization of enums +// { dg-do compile { target c++11 } } + +enum A { G = 26 }; +enum B : short {}; +enum class C {}; +enum struct D : long {}; +enum class E : unsigned char { e = 7 }; +struct S { operator C () { return C (s); } int s; } s; +struct T { operator long () { return t; } long t; } t; +struct V { E v; }; +long l; +long long ll; +short c; +void bar (E); + +void +foo () +{ + A a1 { 5 }; // { dg-error "invalid conversion from 'int' to 'A'" } + B b1 { 7 }; // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } } + C c1 { s }; + D d1 { D(t) }; // { dg-error "invalid cast from type 'T' to type 'D'" } + D d2 { t }; // { dg-error "cannot convert 'T' to 'D' in initialization" "" { target c++14_down } } + // { dg-error "invalid cast from type 'T' to type 'D'" "" { target c++1z } .-1 } + D d3 { 9 }; // { dg-error "cannot convert 'int' to 'D' in initialization" "" { target c++14_down } } + D d4 { l }; // { dg-error "cannot convert 'long int' to 'D' in initialization" "" { target c++14_down } } + D d5 { D(l) }; + D d6 { G }; // { dg-error "cannot convert 'A' to 'D' in initialization" "" { target c++14_down } } + E e1 { 5 }; // { dg-error "cannot convert 'int' to 'E' in initialization" "" { target c++14_down } } + E e2 { -1 }; // { dg-error "cannot convert 'int' to 'E' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '-1' from 'int' to 'unsigned char' inside" "" { target c++1z } .-1 } + E e3 { 5.0 }; // { dg-error "cannot convert 'double' to 'E' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '5.0e.0' from 'double' to 'unsigned char' inside" "" { target c++1z } .-1 } + E e4 { 5.2 }; // { dg-error "cannot convert 'double' to 'E' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '5.\[0-9]*e.0' from 'double' to 'unsigned char' inside" "" { target c++1z } .-1 } + B b2 = { 7 }; // { dg-error "invalid conversion from 'int' to 'B'" } + C c2 = { C { 8 } }; // { dg-error "cannot convert 'int' to 'C' in initialization" "" { target c++14_down } } + + D *d7 = new D { 9 }; // { dg-error "cannot convert \[^\n\r]* to 'D' in initialization" "" { target c++14_down } } + E *e5 = new E { -4 }; // { dg-error "cannot convert \[^\n\r]* to 'E' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '-4' from 'int' to 'unsigned char' inside" "" { target c++1z } .-1 } + bar ({ 10 }); // { dg-error "cannot convert \[^\n\r]* to 'E' for argument" } + bar (E { 9 }); // { dg-error "cannot convert 'int' to 'E' in initialization" "" { target c++14_down } } + V v1 = { { 11 } }; // { dg-error "braces around scalar initializer for type 'E'" } + V v2 = { E { 12 } }; // { dg-error "cannot convert 'int' to 'E' in initialization" "" { target c++14_down } } + V v3 = { E { 5.0 } }; // { dg-error "cannot convert 'double' to 'E' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '5.0e.0' from 'double' to 'unsigned char' inside" "" { target c++1z } .-1 } + V v4 = { 13 }; // { dg-error "cannot convert 'int' to 'E' in initialization" } + if (B b3 { 5 }) // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } } + ; + if (B b4 { 4.0 }) // { dg-error "cannot convert 'double' to 'B' in initialization" "" { target c++14_down } } + ; // { dg-error "narrowing conversion of '4.0e.0' from 'double' to 'short int' inside" "" { target c++1z } .-1 } + C c3 { 8L }; // { dg-error "cannot convert 'long int' to 'C' in initialization" "" { target c++14_down } } + B b4 {short (c + 5)}; // { dg-error "invalid conversion from 'short int' to 'B'" "" { target c++14_down } } + B b5 {c + 5}; // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } } + // { dg-error "narrowing conversion of \[^\n\r]* from 'int' to 'short int' inside" "" { target c++1z } .-1 } + C c4 { ll }; // { dg-error "cannot convert 'long long int' to 'C' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of 'll' from 'long long int' to 'int' inside" "" { target c++1z } .-1 } + C c5 {short (c + 5)}; // { dg-error "cannot convert 'short int' to 'C' in initialization" "" { target c++14_down } } + C c6 {c + 5}; // { dg-error "cannot convert 'int' to 'C' in initialization" "" { target c++14_down } } +} + +struct U +{ + U () : e { 5 } {} // { dg-error "cannot convert \[^\n\r]* to 'E' in initialization" "" { target c++14_down } } + U (int) : e { 5.0 } {}// { dg-error "cannot convert \[^\n\r]* to 'E' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '5.0e.0' from 'double' to 'unsigned char' inside" "" { target c++1z } .-1 } + U (float) : e({ 6 }) {}// { dg-error "list-initializer for non-class type must not be parenthesized" } + // { dg-error "cannot convert \[^\n\r]* to 'E' in initialization" "" { target *-*-* } .-1 } + E e; +}; + +struct W +{ + A a { 5 }; // { dg-error "invalid conversion from 'int' to 'A'" } + B b { 6 }; // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } } + C c { 3.0f }; // { dg-error "cannot convert \[^\n\r]* to 'C' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '3.0e.0f' from 'float' to 'int' inside" "" { target c++1z } .-1 } + D d = { 7 }; // { dg-error "cannot convert \[^\n\r]* to 'D' in initialization" } +}; + +template +void +foo2 () +{ + A a1 { 5 }; // { dg-error "invalid conversion from 'int' to 'A'" } + B b1 { 7 }; // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } } + C c1 { s }; + D d1 { D(t) }; // { dg-error "invalid cast from type 'T' to type 'D'" } + D d2 { t }; // { dg-error "cannot convert 'T' to 'D' in initialization" "" { target c++14_down } } + // { dg-error "invalid cast from type 'T' to type 'D'" "" { target c++1z } .-1 } + D d3 { 9 }; // { dg-error "cannot convert 'int' to 'D' in initialization" "" { target c++14_down } } + D d4 { l }; // { dg-error "cannot convert 'long int' to 'D' in initialization" "" { target c++14_down } } + D d5 { D(l) }; + D d6 { G }; // { dg-error "cannot convert 'A' to 'D' in initialization" "" { target c++14_down } } + E e1 { 5 }; // { dg-error "cannot convert 'int' to 'E' in initialization" "" { target c++14_down } } + E e2 { -1 }; // { dg-error "cannot convert 'int' to 'E' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '-1' from 'int' to 'unsigned char' inside" "" { target c++1z } .-1 } + E e3 { 5.0 }; // { dg-error "cannot convert 'double' to 'E' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '5.0e.0' from 'double' to 'unsigned char' inside" "" { target c++1z } .-1 } + E e4 { 5.2 }; // { dg-error "cannot convert 'double' to 'E' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '5.\[0-9]*e.0' from 'double' to 'unsigned char' inside" "" { target c++1z } .-1 } + B b2 = { 7 }; // { dg-error "invalid conversion from 'int' to 'B'" } + C c2 = { C { 8 } }; // { dg-error "cannot convert 'int' to 'C' in initialization" "" { target c++14_down } } + D *d7 = new D { 9 }; // { dg-error "cannot convert \[^\n\r]* to 'D' in initialization" "" { target c++14_down } } + E *e5 = new E { -4 }; // { dg-error "cannot convert \[^\n\r]* to 'E' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '-4' from 'int' to 'unsigned char' inside" "" { target c++1z } .-1 } + bar ({ 10 }); // { dg-error "cannot convert \[^\n\r]* to 'E' for argument" } + bar (E { 9 }); // { dg-error "cannot convert 'int' to 'E' in initialization" "" { target c++14_down } } + V v1 = { { 11 } }; // { dg-error "braces around scalar initializer for type 'E'" } + V v2 = { E { 12 } }; // { dg-error "cannot convert 'int' to 'E' in initialization" "" { target c++14_down } } + V v3 = { E { 5.0 } }; // { dg-error "cannot convert 'double' to 'E' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '5.0e.0' from 'double' to 'unsigned char' inside" "" { target c++1z } .-1 } + V v4 = { 13 }; // { dg-error "cannot convert 'int' to 'E' in initialization" } + if (B b3 { 5 }) // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } } + ; + if (B b4 { 4.0 }) // { dg-error "cannot convert 'double' to 'B' in initialization" "" { target c++14_down } } + ; // { dg-error "narrowing conversion of '4.0e.0' from 'double' to 'short int' inside" "" { target c++1z } .-1 } + C c3 { 8L }; // { dg-error "cannot convert 'long int' to 'C' in initialization" "" { target c++14_down } } + B b4 {short (c + 5)}; // { dg-error "invalid conversion from 'short int' to 'B'" "" { target c++14_down } } + B b5 {c + 5}; // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } } + // { dg-error "narrowing conversion of \[^\n\r]* from 'int' to 'short int' inside" "" { target c++1z } .-1 } + C c4 { ll }; // { dg-error "cannot convert 'long long int' to 'C' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of 'll' from 'long long int' to 'int' inside" "" { target c++1z } .-1 } + C c5 {short (c + 5)}; // { dg-error "cannot convert 'short int' to 'C' in initialization" "" { target c++14_down } } + C c6 {c + 5}; // { dg-error "cannot convert 'int' to 'C' in initialization" "" { target c++14_down } } +} + +template +struct U2 +{ + U2 () : e { 5 } {} // { dg-error "cannot convert \[^\n\r]* to 'E' in initialization" "" { target c++14_down } } + U2 (int) : e { 5.0 } {}// { dg-error "cannot convert \[^\n\r]* to 'E' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '5.0e.0' from 'double' to 'unsigned char' inside" "" { target c++1z } .-1 } + U2 (float) : e({ 6 }) {} + E e; +}; + +template +struct W2 +{ + A a { 5 }; // { dg-error "invalid conversion from 'int' to 'A'" "" { target *-*-* } .-2 } + B b { 6 }; // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } .-3 } + C c { 3.0f }; // { dg-error "cannot convert \[^\n\r]* to 'C' in initialization" "" { target c++14_down } .-4 } + // { dg-error "narrowing conversion of '3.0e.0f' from 'float' to 'int' inside" "" { target c++1z } .-5 } + D d = { 7 }; // { dg-error "cannot convert \[^\n\r]* to 'D' in initialization" "" { target *-*-* } .-6 } +}; + +template +void +foo3 () +{ + void bar3 (L); + H a1 { 5 }; // { dg-error "invalid conversion from 'int' to 'A'" } + I b1 { 7 }; // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } } + J c1 { s }; + K d1 { K(t) }; // { dg-error "invalid cast from type 'T' to type 'D'" } + K d2 { t }; // { dg-error "cannot convert 'T' to 'D' in initialization" "" { target c++14_down } } + // { dg-error "invalid cast from type 'T' to type 'D'" "" { target c++1z } .-1 } + K d3 { 9 }; // { dg-error "cannot convert 'int' to 'D' in initialization" "" { target c++14_down } } + K d4 { l }; // { dg-error "cannot convert 'long int' to 'D' in initialization" "" { target c++14_down } } + K d5 { K(l) }; + K d6 { G }; // { dg-error "cannot convert 'A' to 'D' in initialization" "" { target c++14_down } } + L e1 { 5 }; // { dg-error "cannot convert 'int' to 'E' in initialization" "" { target c++14_down } } + L e2 { -1 }; // { dg-error "cannot convert 'int' to 'E' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '-1' from 'int' to 'unsigned char' inside" "" { target c++1z } .-1 } + L e3 { 5.0 }; // { dg-error "cannot convert 'double' to 'E' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '5.0e.0' from 'double' to 'unsigned char' inside" "" { target c++1z } .-1 } + L e4 { 5.2 }; // { dg-error "cannot convert 'double' to 'E' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '5.\[0-9]*e.0' from 'double' to 'unsigned char' inside" "" { target c++1z } .-1 } + I b2 = { 7 }; // { dg-error "invalid conversion from 'int' to 'B'" } + J c2 = { J { 8 } }; // { dg-error "cannot convert 'int' to 'C' in initialization" "" { target c++14_down } } + K *d7 = new K { 9 }; // { dg-error "cannot convert \[^\n\r]* to 'D' in initialization" "" { target c++14_down } } + L *e5 = new L { -4 }; // { dg-error "cannot convert \[^\n\r]* to 'E' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '-4' from 'int' to 'unsigned char' inside" "" { target c++1z } .-1 } + bar3 ({ 10 }); // { dg-error "cannot convert \[^\n\r]* to 'E' for argument" } + bar3 (E { 9 }); // { dg-error "cannot convert 'int' to 'E' in initialization" "" { target c++14_down } } + M v1 = { { 11 } }; // { dg-error "braces around scalar initializer for type 'E'" } + M v2 = { L { 12 } }; // { dg-error "cannot convert 'int' to 'E' in initialization" "" { target c++14_down } } + M v3 = { L { 5.0 } }; // { dg-error "cannot convert 'double' to 'E' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '5.0e.0' from 'double' to 'unsigned char' inside" "" { target c++1z } .-1 } + M v4 = { 13 }; // { dg-error "cannot convert 'int' to 'E' in initialization" } + if (I b3 { 5 }) // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } } + ; + if (I b4 { 4.0 }) // { dg-error "cannot convert 'double' to 'B' in initialization" "" { target c++14_down } } + ; // { dg-error "narrowing conversion of '4.0e.0' from 'double' to 'short int' inside" "" { target c++1z } .-1 } + J c3 { 8L }; // { dg-error "cannot convert 'long int' to 'C' in initialization" "" { target c++14_down } } + I b4 {short (c + 5)}; // { dg-error "invalid conversion from 'short int' to 'B'" "" { target c++14_down } } + I b5 {c + 5}; // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } } + // { dg-error "narrowing conversion of \[^\n\r]* from 'int' to 'short int' inside" "" { target c++1z } .-1 } + J c4 { ll }; // { dg-error "cannot convert 'long long int' to 'C' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of 'll' from 'long long int' to 'int' inside" "" { target c++1z } .-1 } + J c5 {short (c + 5)}; // { dg-error "cannot convert 'short int' to 'C' in initialization" "" { target c++14_down } } + J c6 {c + 5}; // { dg-error "cannot convert 'int' to 'C' in initialization" "" { target c++14_down } } +} + +template +struct U3 +{ + U3 () : e { 5 } {} // { dg-error "cannot convert \[^\n\r]* to 'E' in initialization" "" { target c++14_down } } + U3 (int) : e { 5.0 } {}// { dg-error "cannot convert \[^\n\r]* to 'E' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '5.0e.0' from 'double' to 'unsigned char' inside" "" { target c++1z } .-1 } + U3 (float) : e({ 6 }) {} + L e; +}; + +template +struct W3 +{ + H a { 5 }; // { dg-error "invalid conversion from 'int' to 'A'" "" { target *-*-* } .-2 } + I b { 6 }; // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } .-3 } + J c { 3.0f }; // { dg-error "cannot convert \[^\n\r]* to 'C' in initialization" "" { target c++14_down } .-4 } + // { dg-error "narrowing conversion of '3.0e.0f' from 'float' to 'int' inside" "" { target c++1z } .-5 } + K d = { 7 }; // { dg-error "cannot convert \[^\n\r]* to 'D' in initialization" "" { target *-*-* } .-6 } +}; + +void +test () +{ + foo2<0> (); + U2<0> u20; + U2<1> u21 (5); + W2<0> w2; // { dg-error "invalid conversion from 'int' to 'A'" } + // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } .-1 } + // { dg-error "cannot convert \[^\n\r]* to 'C' in initialization" "" { target c++14_down } .-2 } + // { dg-error "narrowing conversion of '3.0e.0f' from 'float' to 'int' inside" "" { target c++1z } .-3 } + // { dg-error "cannot convert \[^\n\r]* to 'D' in initialization" "" { target *-*-* } .-4 } + foo3 (); + U3 u30; + U3 u31 (5); + W3 w3; // { dg-error "invalid conversion from 'int' to 'A'" } + // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } .-1 } + // { dg-error "cannot convert \[^\n\r]* to 'C' in initialization" "" { target c++14_down } .-2 } + // { dg-error "narrowing conversion of '3.0e.0f' from 'float' to 'int' inside" "" { target c++1z } .-3 } + // { dg-error "cannot convert \[^\n\r]* to 'D' in initialization" "" { target *-*-* } .-4 } +} -- 2.30.2