@@ -835,11 +835,11 @@ class cppfront
835835 bool violates_initialization_safety = false ;
836836 bool suppress_move_from_last_use = false ;
837837
838- bool generating_assignment_function = false ;
839- bool generating_move_function = false ;
840- bool emitting_that_function = false ;
841- bool emitting_move_that_function = false ;
842- std::vector<token const *> already_moved_that_members = {};
838+ declaration_node const * generating_assignment_from = {} ;
839+ declaration_node const * generating_move_from = {} ;
840+ bool emitting_that_function = false ;
841+ bool emitting_move_that_function = false ;
842+ std::vector<token const *> already_moved_that_members = {};
843843
844844 struct arg_info {
845845 passing_style pass = passing_style::in;
@@ -848,10 +848,18 @@ class cppfront
848848 std::vector<arg_info> current_args = { {} };
849849
850850 struct function_info {
851- token const * pname = {};
851+ declaration_node const * decl = {};
852852 declaration_node::declared_that_funcs declared_that_functions = {};
853853 function_prolog prolog = {};
854854 std::vector<std::string> epilog = {};
855+
856+ function_info (
857+ declaration_node const * decl_,
858+ declaration_node::declared_that_funcs declared_that_functions_
859+ )
860+ : decl{decl_}
861+ , declared_that_functions{declared_that_functions_}
862+ { }
855863 };
856864 std::vector<function_info> current_function_info = { {} };
857865
@@ -1965,7 +1973,7 @@ class cppfront
19651973
19661974 // Return without expression, could be assignment operator
19671975 //
1968- else if (generating_assignment_function )
1976+ else if (generating_assignment_from == current_function_info. back (). decl )
19691977 {
19701978 printer.print_cpp2 (" *this" , n.position ());
19711979 }
@@ -2079,9 +2087,35 @@ class cppfront
20792087
20802088 // Then build the capture list, ignoring duplicated expressions
20812089 auto lambda_intro = std::string (" [" );
2090+ auto num_captures = 0 ;
2091+
2092+ // === NOTE: (see also corresponding note in cpp2util.h)
2093+ //
2094+ // This "&" capture default is to work around 'if constexpr' bugs in Clang and MSVC...
2095+ // If we don't emit a not-actually-used "[&]" here, then both compilers get tripped up
2096+ // if we attempt UFCS for a member function name... the reason is that the UFCS macro
2097+ // generates a lambda that does an 'if constexpr' to call the member function if
2098+ // available, but Clang and MSVC look into the NOT-taken 'if constexpr' branch (which
2099+ // they shouldn't) and see the nonmember function call syntax and think it's trying to
2100+ // use the member function with implicit 'this->' and choke... (see issue #281, and
2101+ // https://godbolt.org/z/M47zzMsoT for a distilled repro)
2102+ //
2103+ // The workaround that seems to be effective is to add TWO actually-unused "&" captures:
2104+ // - here, and
2105+ // - and also in the cpp2util.h UFCS code
2106+ //
2107+ if (
2108+ !current_function_info.empty ()
2109+ && current_function_info.back ().decl ->is_function_with_this ()
2110+ )
2111+ {
2112+ lambda_intro += " &" ;
2113+ ++num_captures;
2114+ }
2115+ // === END NOTE
2116+
20822117 printer.emit_to_string (&lambda_intro);
20832118
2084- auto num = 0 ;
20852119 auto handled = std::vector<std::string>{};
20862120 for (auto & cap : captures.members )
20872121 {
@@ -2092,13 +2126,13 @@ class cppfront
20922126 handled.push_back (cap.str );
20932127
20942128 // And handle it
2095- if (num != 0 ) { // not first
2129+ if (num_captures != 0 ) { // not first
20962130 lambda_intro += " , " ;
20972131 }
2098- cap.cap_sym = " _" +std::to_string (num );
2132+ cap.cap_sym = " _" +std::to_string (num_captures );
20992133 printer.print_cpp2 (cap.cap_sym + " = " + cap.str , pos);
21002134 }
2101- ++num ;
2135+ ++num_captures ;
21022136 }
21032137 printer.emit_to_string ();
21042138 lambda_intro += " ]" ;
@@ -3320,8 +3354,7 @@ class cppfront
33203354 {
33213355 if (
33223356 n.pass != passing_style::out
3323- || !current_function_info.back ().pname
3324- || *current_function_info.back ().pname != " operator="
3357+ || !current_function_info.back ().decl ->has_name (" operator=" )
33253358 )
33263359 {
33273360 errors.emplace_back (
@@ -3725,7 +3758,7 @@ class cppfront
37253758 // Handle a special member function
37263759 if (
37273760 n.is_assignment ()
3728- || generating_assignment_function
3761+ || generating_assignment_from == n. my_decl
37293762 )
37303763 {
37313764 assert (
@@ -3914,7 +3947,7 @@ class cppfront
39143947 assert (func);
39153948
39163949 auto is_assignment =
3917- generating_assignment_function
3950+ generating_assignment_from == &n
39183951 || (*func->parameters )[0 ]->pass == passing_style::inout;
39193952
39203953 if (
@@ -3925,7 +3958,7 @@ class cppfront
39253958 emitting_that_function = true ;
39263959 if (
39273960 (*func->parameters )[1 ]->pass == passing_style::move
3928- || generating_move_function
3961+ || generating_move_from == &n
39293962 )
39303963 {
39313964 emitting_move_that_function = true ;
@@ -4315,7 +4348,7 @@ class cppfront
43154348 assert (func);
43164349
43174350 current_function_info.emplace_back (
4318- n. identifier ? n. name () : nullptr ,
4351+ &n ,
43194352 n.find_parent_declared_that_functions ()
43204353 );
43214354 auto guard = finally ([&]{ current_function_info.pop_back (); });
@@ -4381,7 +4414,7 @@ class cppfront
43814414 if (
43824415 func->is_constructor ()
43834416 && func->parameters ->ssize () == 2
4384- && !generating_assignment_function
4417+ && generating_assignment_from != &n
43854418 )
43864419 {
43874420 prefix += " explicit " ;
@@ -4427,7 +4460,7 @@ class cppfront
44274460 // so recurse to emit related functions if the user didn't write them by hand
44284461 if (
44294462 func->parameters ->ssize () == 2
4430- && !generating_assignment_function
4463+ && generating_assignment_from != &n
44314464 )
44324465 {
44334466 assert (!current_function_info.empty ());
@@ -4469,7 +4502,7 @@ class cppfront
44694502 need_to_generate_assignment = true ;
44704503 }
44714504
4472- if (!generating_move_function ) {
4505+ if (generating_move_from != &n ) {
44734506
44744507 // M) Generate (M)ove from copy,
44754508 // if the user didn't write the move function themselves
@@ -4721,9 +4754,9 @@ class cppfront
47214754
47224755 // Then reposition and do the recursive call
47234756 printer.reset_cpp2_line_to (n.position ().lineno );
4724- generating_assignment_function = true ;
4757+ generating_assignment_from = &n ;
47254758 emit ( n, capture_intro );
4726- generating_assignment_function = false ;
4759+ generating_assignment_from = {} ;
47274760 }
47284761
47294762 // If this was a constructor and we want also want to emit
@@ -4737,9 +4770,9 @@ class cppfront
47374770
47384771 // Then reposition and do the recursive call
47394772 printer.reset_cpp2_line_to (n.position ().lineno );
4740- generating_move_function = true ;
4773+ generating_move_from = &n ;
47414774 emit ( n, capture_intro );
4742- generating_move_function = false ;
4775+ generating_move_from = {} ;
47434776 }
47444777 }
47454778
0 commit comments