Left shift

  • Thread starter Christian Christmann
  • Start date
J

Jerry Coffin

[email protected] says... said:
In fact, the following compiles (gcc):

int foo() {
int *pi;
pi = 1 << 100;
}

It seems to me that any approach that did not perform the
left shift until runtime would not be a correct implementation.

I'm rather lost as to how you figure that. It's undefined behavior. How
can _any_ implementation of it be incorrect?
 
S

Steve Pope

I'm rather lost as to how you figure that. It's undefined
behavior. How can _any_ implementation of it be incorrect?

Here's my opinion: a correct implementation must reduce constant zero
expressions assigned to pointers at compile time.

I don't have the standard in front of me but this seems pretty
clear from Stroustrup.

Steve
 
S

Steve Pope

Bo Persson said:
"Steve Pope" <[email protected]> skrev i meddelandet
Do you mean that compile time constant expressions should be evaluated
differently than non-constant expressions? Shouldn't we expect the
same result?

A compile-time constant expression of value zero and type integer
can be assigned to a pointer, but a variable expression of
the same type and value cannot, so yes there's a language difference.

Steve
 
J

Jerry Coffin

Here's my opinion: a correct implementation must reduce constant zero
expressions assigned to pointers at compile time.

There are constant expressions, and there are some specific requirements
on those expressions. Among others, when the value of such an expression
is zero, the expression must be convertible to a pointer and result in a
null pointer.

Since you didn't quote it, I'll note that the expression being discussed
is "1<<100", and we're assuming that it's on an implementation in which
type int has 100 or fewer bits.

That's entirely irrelevant though, because the expression in question is
NOT a constant expression with a value of zero -- it's an expression
with undefined behavior. I've already pointed that out in what you've
quoted above. Maybe you don't believe that. In that case, here's what
the standard says (section 5.8/1) [expr.shift]:

The behavior is undefined if the right operand is negative,
or greater than or equal to the length in bits of the
promoted left operand.

In case you don't undertand what undefined behavior means (from section
1.3.12):

behavior, such as might arise upon use of an erroneous
program construct or erroneous data, for which this
International Standard imposes no requirements.

The standard places NO REQUIREMENT on the result of the expression in
question. Please see:

http://dialspace.dial.pipex.com/prod/dialspace/town/green/gfd34/art/

and click on "bloopers" for information about roughly what you should
expect when you depend on undefined behavior.
 
S

Steve Pope

Jerry Coffin said:
(e-mail address removed) says...
There are constant expressions, and there are some specific
requirements on those expressions. Among others, when the
value of such an expression is zero, the expression must be
convertible to a pointer and result in a null pointer.
Since you didn't quote it, I'll note that the expression
being discussed is "1<<100", and we're assuming that it's on
an implementation in which type int has 100 or fewer bits.
That's entirely irrelevant though, because the expression
in question is NOT a constant expression with a value of zero
-- it's an expression with undefined behavior. I've already
pointed that out in what you've quoted above. Maybe you don't
believe that.

You're drifting sideways on this, I believe.

Do you agree or disagree that a constant expression being
assigned to a pointer must be evaluated at compile time?
If this is true, and I think it is, then it is still true whether
or not the result of that evaluation is undefined.
In case you don't undertand what undefined behavior means

Are you trying to dodge the question here, or what?

Steve
 
K

Kai-Uwe Bux

Jerry said:
Here's my opinion: a correct implementation must reduce constant zero
expressions assigned to pointers at compile time.

There are constant expressions, and there are some specific requirements
on those expressions. Among others, when the value of such an expression
is zero, the expression must be convertible to a pointer and result in a
null pointer.

Since you didn't quote it, I'll note that the expression being discussed
is "1<<100", and we're assuming that it's on an implementation in which
type int has 100 or fewer bits.

That's entirely irrelevant though, because the expression in question is
NOT a constant expression with a value of zero -- it's an expression
with undefined behavior. I've already pointed that out in what you've
quoted above. Maybe you don't believe that. In that case, here's what
the standard says (section 5.8/1) [expr.shift]:

The behavior is undefined if the right operand is negative,
or greater than or equal to the length in bits of the
promoted left operand.

I tend to disagree: the code snippet in question is:

int foo() {
int *pi;
pi = 1 << 100;
}

The assignment pi = 1 << 100 requires the right hand side to be a null
pointer constant, i.e., "an integral constant expression (5.19) rvalue of
integer type that evaluates to zero" [4.10/1]. Since an integral constant
expression is required in the statement, [3.2/2] applies, telling us that
this expression is *not* potentially evaluated. This implies that the
abstract machine is not involved. Instead the compiler is supposed to
magically know the value of the expression.

Also, notice that 1 << 100 by itself is a constant integral expression,
whence [5/5] applies:

If during the evaluation of an expression, the result is not
mathematically defined or not in the range of representable values for its
type, the behavior is undefined, unless such an expression is a constant
expression (5.19), in which case the program is ill-formed.

That implies that the above snippets do not involve undefined behavior.
Instead, they might be ill-formed.


Best

Kai-Uwe Bux
 
J

Jerry Coffin

[email protected] says... said:
You're drifting sideways on this, I believe.

You believe incorrectly.
Do you agree or disagree that a constant expression being
assigned to a pointer must be evaluated at compile time?

No, it does not have to be evaluated at compile time. That is not a
requirement and never has been.

You're grossly misinterpreting things -- the restrictions on constant
expressions ALLOW them to be evaluated at compile time. You're taking
that allowance and attempting to treat it as a requirement, which is
simpy wrong.
If this is true, and I think it is, then it is still true whether
or not the result of that evaluation is undefined.

First, it's not true under any circumstances. Second, even if it was
true under some other circumstances, it still wouldn't be true in the
case of undefined behavior. The standard is very clear -- any case of
undefined behavior bypasses everything else and immediately removes all
requirements of all kinds.
Are you trying to dodge the question here, or what?

I only see one question here: how can anybody possibly read "no
requirements" as meaning "there are some requirements anyway"? I'm not
the one dodging the question; I'm the one asking it!
 
S

Steve Pope

No, it does not have to be evaluated at compile time. That is not a
requirement and never has been.

"A constant expression that evaluates to zero can be implicitly
converted to any pointer or pointer to member type." (Stroustrup)

I see no way of implementing this in a safe fashion
unless the expression is evaluated at compile time. The only
alternative would seem to be liberally allowing non-zero
constant integer expressions to be assigned to pointers,
which seems a very bad alternative.

Am I missing something?

Steve
 
A

Alf P. Steinbach

* Steve Pope:
"A constant expression that evaluates to zero can be implicitly
converted to any pointer or pointer to member type." (Stroustrup)

I see no way of implementing this in a safe fashion
unless the expression is evaluated at compile time. The only
alternative would seem to be liberally allowing non-zero
constant integer expressions to be assigned to pointers,
which seems a very bad alternative.

Am I missing something?

Yes: your left shift expression

pi = 1 << 100;

does /not/ necessarily evaluate to zero.

If an int has 101 or more significant bits, the value will be non-zero.

If it has less than 101 bits, which is probably the case for all current
commercial C++ implementations, then the expression yields Undefined
Behavior, meaning that if it produces a value (which it needs not do
under UB), that value can be anyhing whatsoever.
 
A

Alf P. Steinbach

* Steve Pope:
Yes, in my statement above I was speaking about the general
case, not this particular constant expression, but I may not
have made that clear.

While we're at it, I think Kai-Uwe has a point that it's an ill-formed
program instead of UB. Although cases of compile time UB do exist. So
I shouldn't have written UB. :-(
 
S

Steve Pope

Alf P. Steinbach said:
While we're at it, I think Kai-Uwe has a point that it's an
ill-formed program instead of UB. Although cases of compile
time UB do exist. So I shouldn't have written UB. :-(

Nods. Kai-Uwe, thanks for providing a clear answer here...

Steve
 
J

Jerry Coffin

"A constant expression that evaluates to zero can be implicitly
converted to any pointer or pointer to member type." (Stroustrup)

True, but 100% irrelevant. For the umpteenth time: this is NOT a
constant expression with the value zero. In fact, it doesn't have any
defined value at all. It has undefined behavior, nothing less and
nothing more.
I see no way of implementing this in a safe fashion
unless the expression is evaluated at compile time. The only
alternative would seem to be liberally allowing non-zero
constant integer expressions to be assigned to pointers,
which seems a very bad alternative.

Am I missing something?

You're missing the fact that this is NOT an integer constant expression
with the value zero. It's an expression that has undefined behavior.
Since it's not defined (as zero or anything else) the compiler can do
pretty much anything it wants -- it can evaluate it at runtime, or it
can evaluate it at compile time, or it can simply refuse to accept the
code at all. That's the nature of undefined behavior.
 
J

Jerry Coffin

[ ... ]
I tend to disagree: the code snippet in question is:

int foo() {
int *pi;
pi = 1 << 100;
}

The assignment pi = 1 << 100 requires the right hand side to be a null
pointer constant, i.e., "an integral constant expression (5.19) rvalue of
integer type that evaluates to zero" [4.10/1]. Since an integral constant
expression is required in the statement, [3.2/2] applies, telling us that
this expression is *not* potentially evaluated. This implies that the
abstract machine is not involved. Instead the compiler is supposed to
magically know the value of the expression.

Also, notice that 1 << 100 by itself is a constant integral expression,
whence [5/5] applies:

If during the evaluation of an expression, the result is not
mathematically defined or not in the range of representable values for its
type, the behavior is undefined, unless such an expression is a constant
expression (5.19), in which case the program is ill-formed.

That implies that the above snippets do not involve undefined behavior.
Instead, they might be ill-formed.

This, at least, strikes me as a reasonable and rational argument, though
I can't say I really agree with it. As I see it, the difference is that
with most operators, the description of that operator simply doesn't
attempt to describe the result when/if there is overflow. By contrast,
in the case of a shift operator, the definition of the operator itself
explicitly does say the result is undefined if the value of the right
operand is greater than the size of the promoted left operand.

IMO, this should be treated much like a program -- if there are
definitions at two different scopes, the definition to use is the one at
the more local scope. As such, I think the statement at 5/5 about
whether or not something is undefined or ill-formed should not be
applied when it is explicitly stated to be undefined behavior in the
definition of the shift opreator itself.

OTOH, I'll grant that I don't know of anything in the standard that
explicitly resolves such a situation, so I can't say it would really be
wrong to resolve it in the way you suggest.
 
S

Steve Pope

Jerry Coffin said:
(e-mail address removed) says...
True, but 100% irrelevant.

It's completely relevant to my original statement in this thread --
that the code generated does not contain a left-shift instruction
because the constant expression is reduced by the compiler.
You've taken the position that this isn't necessarily true.
I've taken the position that it is necessarily true in the
case of a constant expression being assigned to a pointer.
For the umpteenth time: this is NOT a constant expression
with the value zero. In fact, it doesn't have any defined value
at all.

I'm perfectly aware of this.
You're missing the fact that this is NOT an integer constant
expression with the value zero.

You've lost me at this point. That I can tell you haven't suggested
a safe, alternate implementation.

Steve
 
J

Jerry Coffin

[email protected] says... said:
It's completely relevant to my original statement in this thread --
that the code generated does not contain a left-shift instruction
because the constant expression is reduced by the compiler.

As I've pointed out, while it can be, there's no requirement for that to
be true.
You've taken the position that this isn't necessarily true.

I haven't taken the position -- I've simply pointed out the position
taken by the standard.
I've taken the position that it is necessarily true in the
case of a constant expression being assigned to a pointer.

If you think it's necessarily true, please point to part of the standard
that says or at least implies that it should be. I've already quoted the
parts that say it places no requirement on this code whatsoever.
You've lost me at this point. That I can tell you haven't suggested
a safe, alternate implementation.

There's no requirement for a safe alternate implementation. For
undefined behavior, the compiler is free to produce absolutely as unsafe
of code as it wants.
 
M

Michiel.Salters

Bo said:
"Steve Pope" <[email protected]> skrev i meddelandet

Do you mean that compile time constant expressions should be evaluated
differently than non-constant expressions? Shouldn't we expect the
same result?

No, this is well-known. Adding the requirement would make life truly
hard
for cross-compilers, especially with regard to non-integral constant
expressions.
But even for the special case of ICEs it's not considered worth the
trouble.
To be more technical, the standard doesn't talk about compilers at
all - we could very well have an interpreter, or some other kind of
translator. And *if* a compiler does a compile time evaluation, is it
not supposed to use the hardware instructions itself, internally?

No - as it probably is running on a different CPU. It may be the same
CPU family, in which case we wouldn't call it a cross-compiler, but
even
then the results might differ (Pentium FDIV anyone?). Considering that,
you can't talk about *the* hardware instructions aymore.

Regards,
Michiel Salters
 
M

Michiel.Salters

Victor said:
Whatever conclusions you're trying to make from whatever knowledge you
possess about the hardware are nonconsequential; the Standard says that
the behaviour ... is *undefined*.

True, I know. I'm giving a rationale here *why* it's undefined. And
that is because
reasonable hardware might be unable to support it.

Regards,
Michiel Salters
 
S

Steve Pope

If you think it's necessarily true, please point to part of
the standard that says or at least implies that it should be.

Section 4: "An expression e can be implicitly converted to
a type T if and only if the declaration T t = e is well-formed".

Consider

int *p = 2 - 2;
int *q = 2 - 1;

It's required that an implementation accept the first
of these two statements, but not the second. Having the
second merely result in undefined behavior is not a correct
implementation because it performed an implicit conversion
that is disallowed -- violating the "only if" part of the
above spec.

However, one could argue that evaluating the constant expression at
runtime and issuing a runtime error ("dynamic type mismatch", or
some such) would be a correct implementation. I'm not sure
if that's a valid argument, but it's certainly an unattractive
solution.

Steve
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top