Bug 54046 - [4.6/4.7 Regression] wrong control reaches end of non-void function for switch case with throw and default
Summary: [4.6/4.7 Regression] wrong control reaches end of non-void function for switc...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.8.0
: P2 normal
Target Milestone: 4.6.4
Assignee: Jakub Jelinek
URL:
Keywords: diagnostic
Depends on:
Blocks:
 
Reported: 2012-07-20 11:49 UTC by Dennis Lubert
Modified: 2013-02-01 14:26 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work: 3.4.3, 4.7.3, 4.8.0
Known to fail: 4.1.2, 4.4.3, 4.5.2, 4.6.3, 4.7.1
Last reconfirmed: 2012-07-20 00:00:00


Attachments
gcc48-pr54046.patch (2.18 KB, patch)
2012-11-20 17:27 UTC, Jakub Jelinek
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Dennis Lubert 2012-07-20 11:49:08 UTC
Hi, somehow gcc gets confused in its control flow analysis when a default case in a switch has a throw and break, and a variable with user defined dtor.

struct A
{
        ~A() { }
};

bool check( int x )
{
        A z;
        switch( x )
        {
                case 0:
                        return false;
                default:
                        throw "X";
                        break;
        }
}

When I either remove the break or the variable z, the warning will not issued anymore.
Comment 1 Jonathan Wakely 2012-07-20 12:14:52 UTC
This is a regression, there's no warning from 3.4
Comment 2 Jakub Jelinek 2012-07-20 12:39:30 UTC
void foo (void) __attribute__((noreturn));

struct A
{
  ~A () {}
};

bool
check (int x)
{
  A z;
  switch (x)
    {
    case 0:
      return false;
    default:
      foo ();
      break;
    }
}

warns the same.
Comment 3 Jakub Jelinek 2012-07-20 12:57:42 UTC
Or:
void foo (void) __attribute__((noreturn));
void bar (void);

struct A
{
  ~A () {}
};

bool
check (int x)
{
  A z;
  switch (x)
    {
    case 0:
      return false;
    default:
      foo ();
      bar ();
    }
}

The problem is related to pass ordering.  EH lowering is done before CFG cleanups, so if there is code after a noreturn spot (whether throw or noreturn function, endless loop etc. doesn't matter), when trying to lower try .. finally EH will see gimple_seq_may_fallthru to be true because of the extra code after it hiding that it in fact can't fall thru.  We end up then with:
  switch (x) <default: <D.2213>, case 0: <D.2212>>
  <D.2212>:
  D.2216 = 0;
  finally_tmp.0 = 0;
  goto <D.2220>;
  <D.2213>:
  foo ();
  bar ();
  finally_tmp.0 = 1;
  <D.2220>:
  A::~A (&z);
  switch (finally_tmp.0) <default: <D.2223>, default: <D.2223>, case 1: <D.2221>>

where D.2221 falls thru to exit without returning a value, while D.2223 doesn't.
Then cfg cleanup during cfg pass cleans that up to:
  finally_tmp.0 = 0;
  A::~A (&z);
  switch (finally_tmp.0) <default: <L12>, case 1: <L11>>
after finding out that foo is noreturn and can't fallthru.  But there is no constant propagation and switch optimization pass that would simplify
switch (0) <default: something; case 1: somethingelse> into
goto something; scheduled before the pass_warn_function_return is scheduled.
As we aren't in SSA form at that point, doing it wouldn't be very easy.
Comment 4 Eric Botcazou 2012-07-20 15:14:41 UTC
There is a machinery in gimple-low.c for this (and a pending patch at
  http://gcc.gnu.org/ml/gcc-patches/2012-07/msg00817.html
for another issue).  Maybe it can be enhanced to handle this as well.
Comment 5 Jakub Jelinek 2012-11-20 16:02:33 UTC
I don't see how.  The thing is, e.g. lower_stmt resets data->cannot_fallthru on most of the statements, even if it got changed to reset it only on GIMPLE_LABELs (or few selected others), such that say a noreturn call which sets data->cannot_fallthru followed by assignment or another call would keep cannot_fallthru set even when it is currently cleared, on GIMPLE_LABELs we'd need to reset anyway, as we don't have the CFG yet and don't have info how many gotos or other control transfer stmts to each GIMPLE_LABEL there are (and the values of cannot_fallthru at those points).  So even just the break; after the __cxa_throw which got gimplified into goto <somelabel>; <somelabel>: would reset cannot_fallthru.  And the gimplifier doesn't see break; but already the goto
Comment 6 Jakub Jelinek 2012-11-20 17:27:03 UTC
Created attachment 28744 [details]
gcc48-pr54046.patch

Actually, with a langhook we can already at the C++ FE level get rid of the warning for most of the cases, similarly how we don't warn for
g++.dg/warn/Wreturn-type-7.C (PR20681).
Comment 7 Jakub Jelinek 2012-11-23 16:04:14 UTC
Author: jakub
Date: Fri Nov 23 16:04:03 2012
New Revision: 193762

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=193762
Log:
	PR c++/54046
	* Makefile.in (gimple-low.o): Depend on langhooks.h.
	* gimple-low.c: Include langhooks.c.
	(block_may_fallthru): Handle TARGET_EXPR and ERROR_MARK,
	by default call lang_hooks.block_may_fallthru.
	* langhooks.h (struct lang_hooks): Add block_may_fallthru
	langhook.
	* langhooks-def.h (LANG_HOOKS_BLOCK_MAY_FALLTHRU): Define.
	(LANG_HOOKS_INITIALIZER): Use it.

	* cp-objcp-common.h (LANG_HOOKS_BLOCK_MAY_FALLTHRU): Redefine.
	* cp-objcp-common.c (cxx_block_may_fallthru): New function.
	* cp-tree.h (cxx_block_may_fallthru): New prototype.

	* g++.dg/warn/Wreturn-type-8.C: New test.

Added:
    trunk/gcc/testsuite/g++.dg/warn/Wreturn-type-8.C
Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/Makefile.in
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/cp-objcp-common.c
    trunk/gcc/cp/cp-objcp-common.h
    trunk/gcc/cp/cp-tree.h
    trunk/gcc/gimple-low.c
    trunk/gcc/langhooks-def.h
    trunk/gcc/langhooks.h
    trunk/gcc/testsuite/ChangeLog
Comment 8 Jakub Jelinek 2013-02-01 14:02:41 UTC
Author: jakub
Date: Fri Feb  1 14:02:33 2013
New Revision: 195651

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=195651
Log:
	Backported from mainline
	2012-11-23  Jakub Jelinek  <jakub@redhat.com>

	PR c++/54046
	* Makefile.in (gimple-low.o): Depend on langhooks.h.
	* gimple-low.c: Include langhooks.c.
	(block_may_fallthru): Handle TARGET_EXPR and ERROR_MARK,
	by default call lang_hooks.block_may_fallthru.
	* langhooks.h (struct lang_hooks): Add block_may_fallthru
	langhook.
	* langhooks-def.h (LANG_HOOKS_BLOCK_MAY_FALLTHRU): Define.
	(LANG_HOOKS_INITIALIZER): Use it.

	* cp-objcp-common.h (LANG_HOOKS_BLOCK_MAY_FALLTHRU): Redefine.
	* cp-objcp-common.c (cxx_block_may_fallthru): New function.
	* cp-tree.h (cxx_block_may_fallthru): New prototype.

	* g++.dg/warn/Wreturn-type-8.C: New test.

Added:
    branches/gcc-4_7-branch/gcc/testsuite/g++.dg/warn/Wreturn-type-8.C
Modified:
    branches/gcc-4_7-branch/gcc/ChangeLog
    branches/gcc-4_7-branch/gcc/Makefile.in
    branches/gcc-4_7-branch/gcc/cp/ChangeLog
    branches/gcc-4_7-branch/gcc/cp/cp-objcp-common.c
    branches/gcc-4_7-branch/gcc/cp/cp-objcp-common.h
    branches/gcc-4_7-branch/gcc/cp/cp-tree.h
    branches/gcc-4_7-branch/gcc/gimple-low.c
    branches/gcc-4_7-branch/gcc/langhooks-def.h
    branches/gcc-4_7-branch/gcc/langhooks.h
    branches/gcc-4_7-branch/gcc/testsuite/ChangeLog
Comment 9 Jakub Jelinek 2013-02-01 14:26:02 UTC
Fixed for 4.7.3+.