diff mbox

Fix PR c++/69736

Message ID alpine.DEB.2.20.9.1602231053180.1231@idea
State New
Headers show

Commit Message

Patrick Palka Feb. 23, 2016, 3:58 p.m. UTC
On Tue, 23 Feb 2016, Marek Polacek wrote:

> On Tue, Feb 23, 2016 at 09:58:41AM -0500, Patrick Palka wrote:
>> finish_call_expr thinks that a call to a function which has been
>> obfuscated by force_paren_expr is a call to an unknown function.  This
>> eventually leads us to not make use of the function's default arguments
>> when processing the argument list.  So a function call like f() may
>> compile and yet (f)() may not, if f has defaulted arguments.
>>
>> This patch fixes this inconsistency by making finish_call_expr undo the
>> obfuscation performed by force_paren_expr.
>
> Thanks for the fix.
>
>> new file mode 100644
>> index 0000000..12462be
>> --- /dev/null
>> +++ b/gcc/testsuite/g++.dg/cpp1y/paren2.C
>> @@ -0,0 +1,25 @@
>> +// PR c++/69736
>> +
>
> I'd expect
> // { dg-do compile { target c++14 } }
> here.

Okay.

>
>> +void fn1(bool = true)
>> +{
>> +  (fn1)();
>> +}
>> +
>> +template <typename>
>> +void fn2()
>> +{
>> +  (fn1)();
>> +}
>
> The test seems to fail here though because of
> testsuite/g++.dg/cpp1y/paren2.C:11:9: error: too few arguments to function
> Why's that?

Oops... The call to maybe_undo_parenthesized_ref managed to mysteriously
move itself to the wrong place.  It should be called before the
processing_template_decl logic, before FN gets wrapped in a
NON_DEPENDENT_EXPR.

Here's the updated patch, which I'm going to retest just in case.

-- >8 --

gcc/cp/ChangeLog:

 	PR c++/69736
 	* cp-tree.h (REF_PARENTHESIZED_P): Adjust documentation.
 	(maybe_undo_parenthesized_ref): Declare.
 	* semantics.c (maybe_undo_parenthesized_ref): Split out from
 	check_return_expr.
 	(finish_call_expr): Use it.
 	* typeck.c (check_return_expr): Use it.

gcc/testsuite/ChangeLog:

 	PR c++/69736
 	* g++.dg/cpp1y/paren2.C: New test.
---
  gcc/cp/cp-tree.h                    |  3 ++-
  gcc/cp/semantics.c                  | 28 ++++++++++++++++++++++++++++
  gcc/cp/typeck.c                     | 12 +-----------
  gcc/testsuite/g++.dg/cpp1y/paren2.C | 26 ++++++++++++++++++++++++++
  4 files changed, 57 insertions(+), 12 deletions(-)
  create mode 100644 gcc/testsuite/g++.dg/cpp1y/paren2.C

Comments

Patrick Palka Feb. 23, 2016, 4:24 p.m. UTC | #1
On Tue, 23 Feb 2016, Patrick Palka wrote:

> On Tue, 23 Feb 2016, Marek Polacek wrote:
>
>> On Tue, Feb 23, 2016 at 09:58:41AM -0500, Patrick Palka wrote:
>>> finish_call_expr thinks that a call to a function which has been
>>> obfuscated by force_paren_expr is a call to an unknown function.  This
>>> eventually leads us to not make use of the function's default arguments
>>> when processing the argument list.  So a function call like f() may
>>> compile and yet (f)() may not, if f has defaulted arguments.
>>> 
>>> This patch fixes this inconsistency by making finish_call_expr undo the
>>> obfuscation performed by force_paren_expr.
>> 
>> Thanks for the fix.
>> 
>>> new file mode 100644
>>> index 0000000..12462be
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/cpp1y/paren2.C
>>> @@ -0,0 +1,25 @@
>>> +// PR c++/69736
>>> +
>> 
>> I'd expect
>> // { dg-do compile { target c++14 } }
>> here.
>
> Okay.
>
>> 
>>> +void fn1(bool = true)
>>> +{
>>> +  (fn1)();
>>> +}
>>> +
>>> +template <typename>
>>> +void fn2()
>>> +{
>>> +  (fn1)();
>>> +}
>> 
>> The test seems to fail here though because of
>> testsuite/g++.dg/cpp1y/paren2.C:11:9: error: too few arguments to function
>> Why's that?
>
> Oops... The call to maybe_undo_parenthesized_ref managed to mysteriously
> move itself to the wrong place.  It should be called before the
> processing_template_decl logic, before FN gets wrapped in a
> NON_DEPENDENT_EXPR.
>
> Here's the updated patch, which I'm going to retest just in case.
>

Actually, this patch is also wrong since we later reject the function
call (fn1)() when we attempt to instantiate the enclosing template function
fn2<>.  Example:

     template <typename T>
     void fn2(T a)
     {
       (fn1)();
     }

     void foo ()
     {
       fn2<bool> (true);
     }

This happens because when processing_template_decl, in finish_call_expr we
build a non-dependent CALL_EXPR using the obfuscated orig_fn.  Then during
instantiation, when tsubst_copy_and_build rebuilds this INDIRECT_REF (which is
the orig_fn) it does not copy the REF_PARENTHESIZED_P flag to the newly built
INDIRECT_REF.  So in the subsequent call to finish_call_expr FN is no longer
considered to be a parenthesized ref so maybe_undo_parenthesized_ref becomes a
no-op.

So this can be fixed either by

1. making tsubst_copy_and_build retain the REF_PARENTHESIZED_P flag when
processing an INDIRECT_REF, or by

2. moving the call to maybe_undo_parenthesized_ref in finish_call_expr before
the assignment of orig_fn so that orig_fn will be un-obfuscated as well, or by

3. making tsubst_copy_and_build call maybe_undo_parenthesized_ref when
processing a CALL_EXPR.

I don't know which solution is better.
Jason Merrill Feb. 24, 2016, 1:51 p.m. UTC | #2
On 02/23/2016 11:24 AM, Patrick Palka wrote:
> 1. making tsubst_copy_and_build retain the REF_PARENTHESIZED_P flag when
> processing an INDIRECT_REF, or by

This should happen in any case.

> 2. moving the call to maybe_undo_parenthesized_ref in finish_call_expr before
> the assignment of orig_fn so that orig_fn will be un-obfuscated as well, or by

This would also be OK; at this point we know the expression isn't the 
operand of decltype.

Jason
diff mbox

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 3c23a83a..88c6367 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -3393,7 +3393,7 @@  extern void decl_shadowed_for_var_insert (tree, tree);
    TREE_LANG_FLAG_0 (STRING_CST_CHECK (NODE))

  /* Indicates whether a COMPONENT_REF has been parenthesized, or an
-   INDIRECT_REF comes from parenthesizing a VAR_DECL.  Currently only set
+   INDIRECT_REF comes from parenthesizing a _DECL.  Currently only set
     some of the time in C++14 mode.  */

  #define REF_PARENTHESIZED_P(NODE) \
@@ -6361,6 +6361,7 @@  extern tree finish_label_stmt			(tree);
  extern void finish_label_decl			(tree);
  extern cp_expr finish_parenthesized_expr	(cp_expr);
  extern tree force_paren_expr			(tree);
+extern tree maybe_undo_parenthesized_ref	(tree);
  extern tree finish_non_static_data_member       (tree, tree, tree);
  extern tree begin_stmt_expr			(void);
  extern tree finish_stmt_expr_expr		(tree, tree);
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 38c7516..e5ecf48 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -1673,6 +1673,30 @@  force_paren_expr (tree expr)
    return expr;
  }

+/* If T is an id-expression obfuscated by force_paren_expr, undo the
+   obfuscation and return the underlying id-expression.  Otherwise
+   return T.  */
+
+tree
+maybe_undo_parenthesized_ref (tree t)
+{
+  if (cxx_dialect >= cxx14
+      && INDIRECT_REF_P (t)
+      && REF_PARENTHESIZED_P (t))
+    {
+      t = TREE_OPERAND (t, 0);
+      while (TREE_CODE (t) == NON_LVALUE_EXPR
+	     || TREE_CODE (t) == NOP_EXPR)
+	t = TREE_OPERAND (t, 0);
+
+      gcc_assert (TREE_CODE (t) == ADDR_EXPR
+		  || TREE_CODE (t) == STATIC_CAST_EXPR);
+      t = TREE_OPERAND (t, 0);
+    }
+
+  return t;
+}
+
  /* Finish a parenthesized expression EXPR.  */

  cp_expr
@@ -2265,6 +2289,10 @@  finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,

    orig_fn = fn;

+  /* If FN is a FUNCTION_DECL obfuscated by force_paren_expr, undo
+     it so that we can tell this is a call to a known function.  */
+  fn = maybe_undo_parenthesized_ref (fn);
+
    if (processing_template_decl)
      {
        /* If the call expression is dependent, build a CALL_EXPR node
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index d7ce327..3da6ea1 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -8929,17 +8929,7 @@  check_return_expr (tree retval, bool *no_warning)

        /* If we had an id-expression obfuscated by force_paren_expr, we need
  	 to undo it so we can try to treat it as an rvalue below.  */
-      if (cxx_dialect >= cxx14
-	  && INDIRECT_REF_P (retval)
-	  && REF_PARENTHESIZED_P (retval))
-	{
-	  retval = TREE_OPERAND (retval, 0);
-	  while (TREE_CODE (retval) == NON_LVALUE_EXPR
-		 || TREE_CODE (retval) == NOP_EXPR)
-	    retval = TREE_OPERAND (retval, 0);
-	  gcc_assert (TREE_CODE (retval) == ADDR_EXPR);
-	  retval = TREE_OPERAND (retval, 0);
-	}
+      retval = maybe_undo_parenthesized_ref (retval);

        /* Under C++11 [12.8/32 class.copy], a returned lvalue is sometimes
  	 treated as an rvalue for the purposes of overload resolution to
diff --git a/gcc/testsuite/g++.dg/cpp1y/paren2.C b/gcc/testsuite/g++.dg/cpp1y/paren2.C
new file mode 100644
index 0000000..56736b5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/paren2.C
@@ -0,0 +1,26 @@ 
+// PR c++/69736
+// { dg-do compile { target c++14 } }
+
+void fn1(bool = true)
+{
+  (fn1)();
+}
+
+template <typename>
+void fn2()
+{
+  (fn1)();
+}
+
+struct X
+{
+  static void fn3(bool = true)
+  {
+    (X::fn3)();
+  }
+
+  void fn4(bool = true)
+  {
+    (X::fn4)();
+  }
+};