FreeBSD/contrib/gcc/cp/cvt.c
/* When an expression is used in a void context, its value is discarded and
   no lvalue-rvalue and similar conversions happen [expr.static.cast/4,
   stmt.expr/1, expr.comma/1].  This permits dereferencing an incomplete type
   in a void context. The C++ standard does not define what an `access' to an
   object is, but there is reason to believe that it is the lvalue to rvalue
   conversion -- if it were not, `*&*p = 1' would violate [expr]/4 in that it
   accesses `*p' not to calculate the value to be stored. But, dcl.type.cv/8
   indicates that volatile semantics should be the same between C and C++
   where ever possible. C leaves it implementation defined as to what
   constitutes an access to a volatile. So, we interpret `*vp' as a read of
   the volatile object `vp' points to, unless that is an incomplete type. For
   volatile references we do not do this interpretation, because that would
   make it impossible to ignore the reference return value from functions. We
   issue warnings in the confusing cases.

   IMPLICIT is tells us the context of an implicit void conversion.  */
When an expression is used in a void context, its value is discarded and
no lvalue-rvalue and similar conversions happen [expr.static.cast/4,
stmt.expr/1, expr.comma/1].  This permits dereferencing an incomplete type
in a void context. The C++ standard does not define what an `access' to an
object is, but there is reason to believe that it is the lvalue to rvalue
conversion -- if it were not, `*&*p = 1' would violate [expr]/4 in that it
accesses `*p' not to calculate the value to be stored. But, dcl.type.cv/8
indicates that volatile semantics should be the same between C and C
where ever possible. C leaves it implementation defined as to what
constitutes an access to a volatile. So, we interpret `*vp' as a read of
the volatile object `vp' points to, unless that is an incomplete type. For
volatile references we do not do this interpretation, because that would
make it impossible to ignore the reference return value from functions. We
issue warnings in the confusing cases.

IMPLICIT is tells us the context of an implicit void conversion.
When an expression is used in a void context its value is discarded and no lvalue - rvalue and similar conversions happen [ expr . static . cast / 4 stmt . expr / 1 expr . comma / 1 ] . This permits dereferencing an incomplete type in a void context . The C ++ standard does not define what an ` access ' to an object is but there is reason to believe that it is the lvalue to rvalue conversion -- if it were not ` * & * p = 1 ' would violate [ expr ] / 4 in that it accesses ` * p ' not to calculate the value to be stored . But dcl . type . cv / 8 indicates that volatile semantics should be the same between C and C where ever possible . C leaves it implementation defined as to what constitutes an access to a volatile . So we interpret ` * vp ' as a read of the volatile object ` vp ' points to unless that is an incomplete type . For volatile references we do not do this interpretation because that would make it impossible to ignore the reference return value from functions . We issue warnings in the confusing cases . IMPLICIT is tells us the context of an implicit void conversion .
tree
convert_to_void (tree expr, const char *implicit)
{
  if (expr == error_mark_node
      || TREE_TYPE (expr) == error_mark_node)
    return error_mark_node;
  if (!TREE_TYPE (expr))
    return expr;
  if (invalid_nonstatic_memfn_p (expr))
    return error_mark_node;
  if (TREE_CODE (expr) == PSEUDO_DTOR_EXPR)
    {
      error ("pseudo-destructor is not called");
      return error_mark_node;
    }
  if (VOID_TYPE_P (TREE_TYPE (expr)))
    return expr;
  switch (TREE_CODE (expr))
    {
    case COND_EXPR:
      {
	tree op1 = TREE_OPERAND (expr,1);
	tree op2 = TREE_OPERAND (expr,2);
	tree new_op1 = convert_to_void
	  (op1, (implicit && !TREE_SIDE_EFFECTS (op2)
		 ? "second operand of conditional" : NULL));
	tree new_op2 = convert_to_void
	  (op2, (implicit && !TREE_SIDE_EFFECTS (op1)
		 ? "third operand of conditional" : NULL));
	expr = build3 (COND_EXPR, TREE_TYPE (new_op1),
		       TREE_OPERAND (expr, 0), new_op1, new_op2);
	break;
      }
    case COMPOUND_EXPR:
      {
	tree op1 = TREE_OPERAND (expr,1);
	tree new_op1 = convert_to_void
	  (op1, (implicit && !TREE_NO_WARNING (expr)
		 ? "right-hand operand of comma" : NULL));
	if (new_op1 != op1)
	  {
	    tree t = build2 (COMPOUND_EXPR, TREE_TYPE (new_op1),
			     TREE_OPERAND (expr, 0), new_op1);
	    expr = t;
	  }
	break;
      }
    case NON_LVALUE_EXPR:
    case NOP_EXPR:
      break;
    case CALL_EXPR:   
      break;
    case INDIRECT_REF:
      {
	tree type = TREE_TYPE (expr);
	int is_reference = TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0)))
			   == REFERENCE_TYPE;
	int is_volatile = TYPE_VOLATILE (type);
	int is_complete = COMPLETE_TYPE_P (complete_type (type));
	if (is_volatile && !is_complete)
	  warning (0, "object of incomplete type %qT will not be accessed in %s",
		   type, implicit ? implicit : "void context");
	else if (is_volatile && (is_reference || TREE_ADDRESSABLE (type)))
	  warning (0, "object of type %qT will not be accessed in %s",
		   TREE_TYPE (TREE_OPERAND (expr, 0)),
		   implicit ? implicit : "void context");
	if (is_reference || !is_volatile || !is_complete || TREE_ADDRESSABLE (type))
	  expr = TREE_OPERAND (expr, 0);
	break;
      }
    case VAR_DECL:
      {
	tree type = TREE_TYPE (expr);
	int is_complete = COMPLETE_TYPE_P (complete_type (type));
	if (TYPE_VOLATILE (type) && !is_complete)
	  warning (0, "object %qE of incomplete type %qT will not be accessed in %s",
		   expr, type, implicit ? implicit : "void context");
	break;
      }
    case TARGET_EXPR:
      if (TARGET_EXPR_IMPLICIT_P (expr)
	  && TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (expr)))
	{
	  tree init = TARGET_EXPR_INITIAL (expr);
	  if (TREE_CODE (init) == AGGR_INIT_EXPR
	      && !AGGR_INIT_VIA_CTOR_P (init))
	    {
	      tree fn = TREE_OPERAND (init, 0);
	      expr = build3 (CALL_EXPR, TREE_TYPE (TREE_TYPE (TREE_TYPE (fn))),
			     fn, TREE_OPERAND (init, 1), NULL_TREE);
	    }
	}
      break;
    default:;
    }
  {
    tree probe = expr;
    if (TREE_CODE (probe) == ADDR_EXPR)
      probe = TREE_OPERAND (expr, 0);
    if (type_unknown_p (probe))
      {
	pedwarn ("%s cannot resolve address of overloaded function",
		    implicit ? implicit : "void cast");
	expr = void_zero_node;
      }
    else if (implicit && probe == expr && is_overloaded_fn (probe))
      {
	warning (OPT_Waddress, "%s is a reference, not call, to function %qE",
		 implicit, expr);
	if (TREE_CODE (expr) == COMPONENT_REF)
	  expr = TREE_OPERAND (expr, 0);
      }
  }
  if (expr != error_mark_node && !VOID_TYPE_P (TREE_TYPE (expr)))
    {
      if (implicit
	  && warn_unused_value
	  && !TREE_NO_WARNING (expr)
	  && !processing_template_decl)
	{
	  if (!TREE_SIDE_EFFECTS (expr))
	    warning (OPT_Wunused_value, "%s has no effect", implicit);
	  else
	    {
	      tree e;
	      enum tree_code code;
	      enum tree_code_class class;
	      e = expr;
	      while (TREE_CODE (e) == NOP_EXPR)
		e = TREE_OPERAND (e, 0);
	      code = TREE_CODE (e);
	      class = TREE_CODE_CLASS (code);
	      if (class == tcc_comparison
		   || class == tcc_unary
		   || (class == tcc_binary
		       && !(code == MODIFY_EXPR
			    || code == INIT_EXPR
			    || code == PREDECREMENT_EXPR
			    || code == PREINCREMENT_EXPR
			    || code == POSTDECREMENT_EXPR
			    || code == POSTINCREMENT_EXPR)))
		warning (OPT_Wunused_value, "value computed is not used");
	    }
	}
      expr = build1 (CONVERT_EXPR, void_type_node, expr);
    }
  if (! TREE_SIDE_EFFECTS (expr))
    expr = void_zero_node;
  return expr;
}
tree convert_to_void ( tree expr , const char * implicit ) { if ( expr == error_mark_node || TREE_TYPE ( expr ) == error_mark_node ) return error_mark_node ; if ( ! TREE_TYPE ( expr ) ) return expr ; if ( invalid_nonstatic_memfn_p ( expr ) ) return error_mark_node ; if ( TREE_CODE ( expr ) == PSEUDO_DTOR_EXPR ) { error ( " " ) ; return error_mark_node ; } if ( VOID_TYPE_P ( TREE_TYPE ( expr ) ) ) return expr ; switch ( TREE_CODE ( expr ) ) { case COND_EXPR : { tree op1 = TREE_OPERAND ( expr , 1 ) ; tree op2 = TREE_OPERAND ( expr , 2 ) ; tree new_op1 = convert_to_void ( op1 , ( implicit && ! TREE_SIDE_EFFECTS ( op2 ) ? " " : NULL ) ) ; tree new_op2 = convert_to_void ( op2 , ( implicit && ! TREE_SIDE_EFFECTS ( op1 ) ? " " : NULL ) ) ; expr = build3 ( COND_EXPR , TREE_TYPE ( new_op1 ) , TREE_OPERAND ( expr , 0 ) , new_op1 , new_op2 ) ; break ; } case COMPOUND_EXPR : { tree op1 = TREE_OPERAND ( expr , 1 ) ; tree new_op1 = convert_to_void ( op1 , ( implicit && ! TREE_NO_WARNING ( expr ) ? " " : NULL ) ) ; if ( new_op1 != op1 ) { tree t = build2 ( COMPOUND_EXPR , TREE_TYPE ( new_op1 ) , TREE_OPERAND ( expr , 0 ) , new_op1 ) ; expr = t ; } break ; } case NON_LVALUE_EXPR : case NOP_EXPR : break ; case CALL_EXPR : break ; case INDIRECT_REF : { tree type = TREE_TYPE ( expr ) ; int is_reference = TREE_CODE ( TREE_TYPE ( TREE_OPERAND ( expr , 0 ) ) ) == REFERENCE_TYPE ; int is_volatile = TYPE_VOLATILE ( type ) ; int is_complete = COMPLETE_TYPE_P ( complete_type ( type ) ) ; if ( is_volatile && ! is_complete ) warning ( 0 , " " , type , implicit ? implicit : " " ) ; else if ( is_volatile && ( is_reference || TREE_ADDRESSABLE ( type ) ) ) warning ( 0 , " " , TREE_TYPE ( TREE_OPERAND ( expr , 0 ) ) , implicit ? implicit : " " ) ; if ( is_reference || ! is_volatile || ! is_complete || TREE_ADDRESSABLE ( type ) ) expr = TREE_OPERAND ( expr , 0 ) ; break ; } case VAR_DECL : { tree type = TREE_TYPE ( expr ) ; int is_complete = COMPLETE_TYPE_P ( complete_type ( type ) ) ; if ( TYPE_VOLATILE ( type ) && ! is_complete ) warning ( 0 , " " , expr , type , implicit ? implicit : " " ) ; break ; } case TARGET_EXPR : if ( TARGET_EXPR_IMPLICIT_P ( expr ) && TYPE_HAS_TRIVIAL_DESTRUCTOR ( TREE_TYPE ( expr ) ) ) { tree init = TARGET_EXPR_INITIAL ( expr ) ; if ( TREE_CODE ( init ) == AGGR_INIT_EXPR && ! AGGR_INIT_VIA_CTOR_P ( init ) ) { tree fn = TREE_OPERAND ( init , 0 ) ; expr = build3 ( CALL_EXPR , TREE_TYPE ( TREE_TYPE ( TREE_TYPE ( fn ) ) ) , fn , TREE_OPERAND ( init , 1 ) , NULL_TREE ) ; } } break ; default : ; } { tree probe = expr ; if ( TREE_CODE ( probe ) == ADDR_EXPR ) probe = TREE_OPERAND ( expr , 0 ) ; if ( type_unknown_p ( probe ) ) { pedwarn ( " " , implicit ? implicit : " " ) ; expr = void_zero_node ; } else if ( implicit && probe == expr && is_overloaded_fn ( probe ) ) { warning ( OPT_Waddress , " " , implicit , expr ) ; if ( TREE_CODE ( expr ) == COMPONENT_REF ) expr = TREE_OPERAND ( expr , 0 ) ; } } if ( expr != error_mark_node && ! VOID_TYPE_P ( TREE_TYPE ( expr ) ) ) { if ( implicit && warn_unused_value && ! TREE_NO_WARNING ( expr ) && ! processing_template_decl ) { if ( ! TREE_SIDE_EFFECTS ( expr ) ) warning ( OPT_Wunused_value , " " , implicit ) ; else { tree e ; enum tree_code code ; enum tree_code_class class ; e = expr ; while ( TREE_CODE ( e ) == NOP_EXPR ) e = TREE_OPERAND ( e , 0 ) ; code = TREE_CODE ( e ) ; class = TREE_CODE_CLASS ( code ) ; if ( class == tcc_comparison || class == tcc_unary || ( class == tcc_binary && ! ( code == MODIFY_EXPR || code == INIT_EXPR || code == PREDECREMENT_EXPR || code == PREINCREMENT_EXPR || code == POSTDECREMENT_EXPR || code == POSTINCREMENT_EXPR ) ) ) warning ( OPT_Wunused_value , " " ) ; } } expr = build1 ( CONVERT_EXPR , void_type_node , expr ) ; } if ( ! TREE_SIDE_EFFECTS ( expr ) ) expr = void_zero_node ; return expr ; }
When an expression is used in a void context, its value is discarded and
no lvalue-rvalue and similar conversions happen [expr.static.cast/4,
stmt.expr/1, expr.comma/1].
When an expression is used in a void context its value is discarded and no lvalue - rvalue and similar conversions happen [ expr . static . cast / 4 stmt . expr / 1 expr . comma / 1 ] .
/* The two parts of a cond expr might be separate lvalues.  */ /* The second part of a compound expr contains the value.  */ /* These have already decayed to rvalue.  */ /* We have a special meaning for volatile void fn().  */ /* Can't load the value if we don't know the type.  */ /* Don't load the value if this is an implicit dereference, or if
	   the type needs to be handled by ctors/dtors.  */ /* External variables might be incomplete.  */ /* Don't bother with the temporary object returned from a function if
	 we don't use it and don't need to destroy it.  We'll still
	 allocate space for it in expand_call or declare_return_variable,
	 but we don't need to track it through all the tree phases.  */ /* [over.over] enumerates the places where we can take the address
	   of an overloaded function, and this is not one of them.  */ /* Only warn when there is no &.  */ /* The middle end does not warn about expressions that have
	     been explicitly cast to void, so we must do so here.  */ /* We might like to warn about (say) "(int) f()", as the
		 cast has no effect, but the compiler itself will
		 generate implicit conversions under some
		 circumstances.  (For example a block copy will be
		 turned into a call to "__builtin_memcpy", with a
		 conversion of the return value to an appropriate
		 type.)  So, to avoid false positives, we strip
		 conversions.  Do not use STRIP_NOPs because it will
		 not strip conversions to "void", as that is not a
		 mode-preserving conversion.  */