Code with Undefined Behavior in C#

Code with undefined behavior in C#

As others have mentioned, pretty much anything in the "unsafe" block can yield implementation-defined behaviour; abuse of unsafe blocks allows you to change the bytes of code that make up the runtime itself, and therefore all bets are off.

The division int.MinValue/-1 has an implementation-defined behaviour.

Throwing an exception and never catching it causes implementation-defined behaviour -- terminate the process, start a debugger, and so on.

There are a number of other situations in C# where we are forced to emit code which has implementation-determined behaviour. For example, this situation:

https://learn.microsoft.com/en-us/archive/blogs/ericlippert/odious-ambiguous-overloads-part-two

However, the situations in which a safe, well-behaved C# program has implementation-defined behaviour should be quite rare.

C# NumSharp undefined behavior when using `object` parameter compared to explicit type parameter

The reason is double is implicitly convertible to NDArray (by implicit operator defined in NDArray). So when you do:

arr.SetData(value, i);

and value is of type double - the overload being called is SetData(NDArray, int[), because it's the best fit given that double is convertible to NDArray.

However when value is of type object or even of unresolved generic type (T) - then overload being called is SetData(object, int[]), so things go wild (because those overloads do quite different things IF value you pass as object is not NDArray, and it's not - it is double). Overload is chosen at compile time, and there is no way compiler can choose the NDArray overload even in generic case (since overload chosen should fit for any possible generic types you can pass).

In your case you can "solve" this by using NDArray value in your extension method instead of object or generic:

public static class Extensions
{
public static void SetMultiData(this NDArray arr, NDArray value, int[] indices)
{
foreach (var i in indices)
arr.SetData(value, i);
}
}

Behaviour and Order of evaluation in C#

The key here is the table in "1.4 Expressions", and "7.3.1 Operator precedence and associativity". I won't duplicate the table from 1.4, but to quote 7.3.1:

  • Except for the assignment operators,
    all binary operators are
    left-associative, meaning that
    operations are performed from left to
    right. For example, x + y + z is
    evaluated as (x + y) + z.
  • The
    assignment operators and the
    conditional operator (?:) are
    right-associative, meaning that
    operations are performed from right to
    left. For example, x = y = z is
    evaluated as x = (y = z).

The first is logically expanded (or: use the associativity rules) as:

i = i + ++i;

here, the order (from the table) is pre-increment, then additive, then assignment - so we should expect i to double plus one. And indeed, with i=6, we get 13 as expected.

a[++i] = i;

again from the table, order should be array access, pre-increment, assignment - so I would expect the i+1'th value to be i+1. And indeed, checking:

    int[] a = { 0, 0, 0, 0, 0 };
int i = 2;
a[++i] = i;

we do indeed get {0, 0, 0, 3, 0}.

With the last, method invocation takes priority over subtraction, then it is left-to-right; so it should be fun(), gun(), -, assignment.

Pre & post increment operator behavior in C, C++, Java, & C#

Java and C# evaluate expressions from left to right, and the side-effects are visible immediately.

In C++, the order of evaluation of subexpressions is unspecified, and modifying the same object twice without an intervening sequence point is undefined behavior.

Is it legal for source code containing undefined behavior to crash the compiler?

The normative definition of undefined behavior is as follows:

[defns.undefined]

behavior for which this International Standard imposes no requirements

[ Note: Undefined behavior may be expected when this International
Standard omits any explicit definition of behavior or when a program
uses an erroneous construct or erroneous data. Permissible undefined
behavior ranges from ignoring the situation completely with
unpredictable results, to behaving during translation or program
execution in a documented manner characteristic of the environment
(with or without the issuance of a diagnostic message), to terminating
a translation or execution (with the issuance of a diagnostic
message). Many erroneous program constructs do not engender undefined
behavior; they are required to be diagnosed. Evaluation of a constant
expression never exhibits behavior explicitly specified as undefined.
 — end note ]

While the note itself is not normative, it does describe a range of behaviors implementations are known to exhibit. So crashing the compiler (which is translation terminating abruptly), is legitimate according to that note. But really, as the normative text says, the standard doesn't place any bounds for either execution or translation. If an implementation steals your passwords, it's not a violation of any contract laid forth in the standard.



Related Topics



Leave a reply



Submit