Parentheses Altering Semantics of Function Call Result

Parentheses altering semantics of function call result

This behavior could be classified as bug, so you should definitely not rely on it.

The (simplified) conditions for the message not to be thrown on a function call are as follows (see the definition of the opcode ZEND_SEND_VAR_NO_REF):

  • the argument is not a function call (or if it is, it returns by reference), and
  • the argument is either a reference or it has reference count 1 (if it has reference count 1, it's turned into a reference).

Let's analyze these in more detail.

First point is true (not a function call)

Due to the additional parentheses, PHP no longer detects that the argument is a function call.

When parsing a non empty function argument list there are three possibilities for PHP:

  • An expr_without_variable
  • A variable
  • (A & followed by a variable, for the removed call-time pass by reference feature)

When writing just get_array() PHP sees this as a variable.

(get_array()) on the other hand does not qualify as a variable. It is an expr_without_variable.

This ultimately affects the way the code compiles, namely the extended value of the opcode SEND_VAR_NO_REF will no longer include the flag ZEND_ARG_SEND_FUNCTION, which is the way the function call is detected in the opcode implementation.

Second point is true (the reference count is 1)

At several points, the Zend Engine allows non-references with reference count 1 where references are expected. These details should not be exposed to the user, but unfortunately they are here.

In your example you're returning an array that's not referenced from anywhere else. If it were, you would still get the message, i.e. this second point would not be true.

So the following very similar example does not work:

<?php

$a = array();
function get_array() {
return $GLOBALS['a'];
}

return reset((get_array()));

PHP: Why do surrounding function calls with parentheses prevent 'pass by reference' notices?

This works out to an implementation detail, as discussed elsewhere on StackOverflow.

Summarizing: this behavior should in fact be considered a bug.Do not rely on it. In fact, thanks to this RFC, this trick will no longer work once PHP 7 is released.

Are parentheses around the result significant in a return statement?


As of C++14, they often are.

C++14 adds a fringe case where parentheses around a return value may alter the semantics. This code snippet shows two functions being declared. The only difference is parentheses around the return value.

int var1 = 42;
decltype(auto) func1() { return var1; } // return type is int, same as decltype(var1)
decltype(auto) func1() { return(var1); } // return type is int&, same as decltype((var1))

In the first func1 returns an int and in the second one func1 returns an int& . The difference in semantics is directly related to the surrounding parentheses.

The auto specifier in its latest form was introduced in C++11. In the C++ Language Spec it is described as:

Specifies that the type of the variable that is being declared will be automatically
deduced from its initializer. For functions, specifies that the return type is a trailing
return type or will be deduced from its return statements (since C++14)

As well C++11 introduced the decltype specifier which is described in the C++ Language Spec:

Inspects the declared type of an entity or queries the return type of an expression.

[snip]

  1. If the argument is either the unparenthesised name of an object/function, or is a member access expression (object.member or pointer->member), then the decltype specifies the declared type of the entity specified by this expression.

  2. If the argument is any other expression of type T, then

    a) if the value category of expression is xvalue, then the decltype specifies T&&

    b) if the value category of expression is lvalue, then the decltype specifies T&

    c) otherwise, decltype specifies T


[snip]

Note that if the name of an object is parenthesised, it becomes an lvalue expression, thus decltype(arg) and decltype((arg)) are often different types.

In C++14 the ability to use decltype(auto) was allowed for function return types. The original examples are where the semantic difference with parentheses comes into play. Revisiting the original examples:

int var1 = 42;
decltype(auto) func1() { return var1; } // return type is int, same as decltype(var1)
decltype(auto) func1() { return(var1); } // return type is int&, same as decltype((var1))

decltype(auto) allows the trailing return type in the function to be deduced from the entity/expression on the return statement. In the first version return var1; is effectively the same as returning the type decltype(var1) (an int return type by rule 1 above) and in the second case return (var1); it's effectively the same as decltype((var1)) (an int & return type by rule 2b).

The parentheses make the return type int& instead of int, thus a change in semantics. Moral of the story - "Not all parentheses on a return type are created equal"

What are the semantics of function pointers with empty parentheses in each C standard?

N1570 6.11.6:

The use of function declarators with empty parentheses (not
prototype-format parameter type declarators) is an obsolescent
feature.

This same wording appears in the 1990, 1999, and 2011 editions of the ISO C standard. There has been no change. The word obsolescent says that the feature may be removed in a future edition of the Standard, but so far the committee has not done so. (Function pointer declarations are just one of several contexts where function declarators can appear.)

The Introduction section of the C standard explains what obsolescent means:

Certain features are obsolescent, which means that they may be
considered for withdrawal in future revisions of this International
Standard. They are retained because of their widespread use, but their
use in new implementations (for implementation features) or new
programs (for language [6.11] or library features [7.31]) is
discouraged.

A call to a function declared with an old-style declarator is still required to pass the correct number and type(s) of arguments (after promotion) as defined by the function's actual definition. A call with incorrect arguments has undefined behavior, which means that the compiler is not required to diagnose the error; the burden is entirely on the programmer.

This is why prototypes were introduced, so that the compiler could check correctness of arguments.

On an i386 system with GCC, “extra” arguments passed in a call to an
empty-parentheses-type’d function pointer are ignored, because of how
stack frames work ...

Yes, that's well within the bounds of undefined behavior. The worst symptom of undefined behavior is having the program work exactly as you expect it to. It means that you have a bug that hasn't exhibited itself yet, and it will be difficult to track it down.

You should not depend on that unless you have a very good reason to do so.

If you change

int (*fp)() = foo;

to

int (*fp)(int) = foo;

the compiler will diagnose the incorrect call.

bash functions: enclosing the body in braces vs. parentheses


Why are braces used by default to enclose the function body instead of parentheses?

The body of a function can be any compound command. This is typically { list; }, but three other forms of compound commands are technically allowed: (list), ((expression)), and [[ expression ]].

C and languages in the C family like C++, Java, C#, and JavaScript all use curly braces to delimit function bodies. Curly braces are the most natural syntax for programmers familiar with those languages.

Are there other major downsides (*) to using parentheses instead of braces (which might explain why braces seem to be preferred)?

Yes. There are numerous things you can't do from a sub-shell, including:

  • Change global variables. Variables changes will not propagate to the parent shell.
  • Exit the script. An exit statement will exit only the sub-shell.

Starting a sub-shell can also be a serious performance hit. You're launching a new process each time you call the function.

You might also get weird behavior if your script is killed. The signals the parent and child shells receive will change. It's a subtle effect but if you have trap handlers or you kill your script those parts not work the way you want.

When shall I use curly braces to enclose the function body, and when is it advisable to switch to parentheses?

I would advise you to always use curly braces. If you want an explicit sub-shell, then add a set of parentheses inside the curly braces. Using just parentheses is highly unusual syntax and would confuse many people reading your script.

foo() {
(
subshell commands;
)
}

Scala parenthesis semantics

The accepted answer is actually wrong.

The reason you don't get tupling for Map.+= is that the method is overloaded with a second method that takes two or more args.

The compiler will only try tupling if the number of args is wrong. But if you give it two args, and there's a method that takes two, that's what it chooses, even if it fails to type check.

It doesn't start trying all possible combinations until something works because that would be fraught with obscurity. (Cf implicits.)

scala> def f(p: Pair[String, Int]) = { val (s,i) = p; s.toInt + i }
f: (p: Pair[String,Int])Int

scala> f("2",3) // ok to tuple
res0: Int = 5

scala> trait F { def +=(p: Pair[String, Int]) = f(p) }
defined trait F

scala> val foo = new F {}
foo: F = $anon$1@6bc77f62

scala> foo += ("2",3) // ok to tuple
res1: Int = 5

scala> trait G { def +=(p: Pair[String, Int]) = f(p); def +=(p:(String,Int),q:(String,Int),r:(String,Int)*) = f(p)+f(q)+(r map f).sum }
defined trait G

scala> val goo = new G {}
goo: G = $anon$1@183aeac3

scala> goo += ("2",3) // sorry
<console>:12: error: type mismatch;
found : String("2")
required: (String, Int)
goo += ("2",3)
^

scala> goo += (("2",3),("4",5),("6",7))
res3: Int = 27

I'd be remiss not to mention your friend and mine, -Xlint, which will warn about untoward arg adaptations:

apm@mara:~/tmp$ skala -Xlint
Welcome to Scala version 2.11.0-20130811-132927-95a4d6e987 (OpenJDK 64-Bit Server VM, Java 1.7.0_25).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def f(p: Pair[String, Int]) = { val (s,i) = p; s.toInt + i }
f: (p: Pair[String,Int])Int

scala> f("2",3)
<console>:9: warning: Adapting argument list by creating a 2-tuple: this may not be what you want.
signature: f(p: Pair[String,Int]): Int
given arguments: "2", 3
after adaptation: f(("2", 3): (String, Int))
f("2",3)
^
res0: Int = 5

scala> List(1).toSet()
<console>:8: warning: Adapting argument list by inserting (): this is unlikely to be what you want.
signature: GenSetLike.apply(elem: A): Boolean
given arguments: <none>
after adaptation: GenSetLike((): Unit)
List(1).toSet()
^
res3: Boolean = false

On the perils of adaptation, see the Adaptive Reasoning puzzler and this new one that is pretty common because we learn that the presence or absence of parens is largely a matter of style, and when parens really matter, using them wrong results in a type error.

Adapting a tuple in the presence of overloading:

scala> class Foo {
| def f[A](a: A) = 1 // A can be (Int,Int,Int)
| def f[A](a: A, a2: A) = 2
| }
defined class Foo

scala> val foo = new Foo
foo: Foo = Foo@2645d22d

scala> foo.f(0,0,0)
<console>:10: warning: Adapting argument list by creating a 3-tuple: this may not be what you want.
signature: Foo.f[A](a: A): Int
given arguments: 0, 0, 0
after adaptation: Foo.f((0, 0, 0): (Int, Int, Int))
foo.f(0,0,0)
^
res9: Int = 1

Python Decorators & Nested Functions return statement. Proper usage with parenthesis?

In a decorator you should not do return inner()

That would mean you are replacing the decorated func with the result of calling the wrapped func

Your code will immediately print the modified message, without you even trying to call func()!!

**************************************************
Decorate this message.
**************************************************

But that is not what is intended when you decorate a function.

You intend to modify the behaviour of the function, but you still expect to have to call the function manually.

That is why the code should be:

def stars(func):
def inner():
print("*" * 50)
func()
print("*" * 50)
return inner

@stars
def func():
print('Decorate this message.')

func()
**************************************************
Decorate this message.
**************************************************

To explain further:

When you use the decorator syntax

@stars
def func():

You are telling python to do the following:

func = stars(func)

In other words, def func but replace the function with the result of calling stars(func)

You might find it less confusing if you don't reuse the name func in the code. For example:

def stars(func):
def inner():
print("*" * 50)
func()
print("*" * 50)
return inner

@stars
def print_a_message():
print('Decorate this message.')

print_a_message()
**************************************************
Decorate this message.
**************************************************

Maybe it's clearer now that when we do return inner from the decorator we are telling Python to replace print_a_message with inner

Why am I getting Only variables should be passed by reference error?

The function end() expects a real variable and not a function returning an array, but if you put the function return inside double parentheses PHP does not report a strict standards notice:

$last = end( ( explode( '/', $someString ) ) );

Can we omit parentheses when creating an object using the new operator?

Quoting David Flanagan1:

As a special case, for the new operator only, JavaScript simplifies the grammar by allowing the parenthesis to be omitted if there are no arguments in the function call. Here are some examples using the new operator:

o = new Object;  // Optional parenthesis omitted here
d = new Date();

...

Personally, I always use the parenthesis, even when the constructor takes no arguments.

In addition, JSLint may hurt your feelings if you omit the parenthesis. It reports Missing '()' invoking a constructor, and there doesn't seem to be an option for the tool to tolerate parenthesis omission.


1 David Flanagan: JavaScript the Definitive Guide: 4th Edition (page 75)



Related Topics



Leave a reply



Submit