digitalmars.D - Casting away const and invariant
- Janice Caron (11/11) Sep 09 2007 I've noticed that you can do this in D2.0, without knowing the type of p...
- Matti Niemenmaa (11/28) Sep 09 2007 Casting away const leads to undefined behaviour and should not be done. ...
- Janice Caron (20/20) Sep 09 2007 You have misunderstood my suggestion. Please stop implying that I have
- Janice Caron (7/7) Sep 09 2007 Actually, forget the specific example. What I'm getting at is that,
- Matti Niemenmaa (15/24) Sep 09 2007 The major point about C++'s const_cast<> is that it is defined behaviour...
- Janice Caron (4/4) Sep 09 2007 Oh sorry - I started another thread before I read this.
- James Dennett (11/23) Sep 09 2007 Only if the underlying object was not declared const in
- Janice Caron (17/20) Sep 09 2007 Of course.
- James Dennett (4/28) Sep 09 2007 It may well be necessary; if it is, the behaviour must be defined
- Lutger (4/8) Sep 09 2007 I thought that is the case? Maybe it should be stated (more?) explicitly...
- Janice Caron (14/16) Sep 09 2007 No, it's undefined for a reason. It should stay undefined.
- Lutger (4/8) Sep 09 2007 Just to be clear: I assume the behavior of casting away const and then
- Janice Caron (4/7) Sep 09 2007 Yes. It's undefined for all sort of reasons ... either because it
- James Dennett (14/33) Sep 09 2007 You may be missing my point. I'll try to explain more
- Janice Caron (3/4) Sep 09 2007 D's single cast syntax dates from a time before there was const. If
- Daniel Keep (10/15) Sep 09 2007 Actually, there's one other deficiency in only having a single cast that
- James Dennett (17/22) Sep 09 2007 There are other problems with a single cast notation; constness
- Janice Caron (13/13) Sep 10 2007 Content-Disposition: inline
- Jeff Nowakowski (8/20) Sep 10 2007 And then what happens when you use a new version of the library, where
- Janice Caron (8/12) Sep 10 2007 Now you're just trying to cause trouble. :-)
I've noticed that you can do this in D2.0, without knowing the type of p. auto q = cast(const) p; The type of q is now the type of p, but with added constness. However, if you want to take the constness away, you must explicitly specify the full type, as in: auto r = cast(int *) q; Seems to me, this is a good place for coding errors to creep in, and it's the exact reason that const_cast<> was introduced into C++. So, how about allowing auto r = cast(!const) q; /* currently will not compile */ which would eliminate that sort of error.
Sep 09 2007
Janice Caron wrote:I've noticed that you can do this in D2.0, without knowing the type of p. auto q = cast(const) p; The type of q is now the type of p, but with added constness. However, if you want to take the constness away, you must explicitly specify the full type, as in: auto r = cast(int *) q; Seems to me, this is a good place for coding errors to creep in, and it's the exact reason that const_cast<> was introduced into C++. So, how about allowing auto r = cast(!const) q; /* currently will not compile */ which would eliminate that sort of error.Casting away const leads to undefined behaviour and should not be done. In general, whenever you cast anything, you need to look carefully at exactly what it is you're casting from, to, and why. The whole reason why C++'s const is completely useless for optimization purposes is that you can cast it away at any time. One goal of D const was to allow optimization. Read the arguments against C++'s const here and think carefully about whether you really want a const_cast in D: http://www.digitalmars.com/d/const.html -- E-mail address: matti.niemenmaa+news, domain is iki (DOT) fi
Sep 09 2007
You have misunderstood my suggestion. Please stop implying that I have not read Walter's documentation. It is a little insulting. Suppose there exists a function with a prototype something like void makeUppercase(ubyte[] s); It's declared as ubyte[], not char[], correctly, because it's intended for some encoding other than UTF-8. It does its conversion in place. Now suppose that a user of that library at first writes this: string s; makeUppercase(s); The compiler will correctly complain that you can't cast from string to ubyte[]. So now, to fix that, suppose that the writer of that code changes it to: string s; makeUppercase(cast(ubyte[])s); Currently, that will compile. What *I am suggesting* is to make it /not/ compile. This makes things /safer/, not less safe. Do you see now? By insisting that the /only/ way to remove const be with an explicit cast(!const), the world becomes a safer place. (Well - maybe that last claim was too bold! :-) )
Sep 09 2007
Actually, forget the specific example. What I'm getting at is that, right now, it's possible to cast away const /accidentally/. My change would ensure that that was no longer the case. And you miss one of the major points about C++'s const_cast<>. Yes, we all agree that it's a BAD THING to do. But the point about C++'s const_cast<> is not that it will make constness go away, it's that static_cast<> /won't/.
Sep 09 2007
Janice Caron wrote:Actually, forget the specific example. What I'm getting at is that, right now, it's possible to cast away const /accidentally/. My change would ensure that that was no longer the case. And you miss one of the major points about C++'s const_cast<>. Yes, we all agree that it's a BAD THING to do. But the point about C++'s const_cast<> is not that it will make constness go away, it's that static_cast<> /won't/.The major point about C++'s const_cast<> is that it is defined behaviour. In D, if you cast away const, no matter how, it is undefined behaviour, and something you shouldn't do. I can understand disallowing casting away const, and I'm fine with that. But adding a mechanism specifically for that purpose (your "cast(!const)") is something I don't think is a good idea, because it breaks one of the principles on which D's new const semantics were built. It appears that what you're actually after is for the compiler to be smarter about catching errors which lead to undefined behaviour, which is fine. In this case, you want a warning/error on casting from a const type to a non-const type. But fact is, the DMD frontend isn't very good at such things, and probably won't improve significantly any time soon. -- E-mail address: matti.niemenmaa+news, domain is iki (DOT) fi
Sep 09 2007
Oh sorry - I started another thread before I read this. Yes, I want an error on casting from a const type to a non-const type. I guess we should use the new thread now that I've started it, coz it's more straight-to-the-point.
Sep 09 2007
Matti Niemenmaa wrote:Janice Caron wrote:Only if the underlying object was not declared const in the first place. (This is also a point that Walter generally elides when he critizes C++ const as not being useful for optimization, and he coins the term "storage class" to apply to those uses of const in C++ which *do* permit optimization, though const is not a storage class in C++ terminology.) If casting away const in D always involves undefined behavior (and not only when the resulting object is modified) then it should certainly be detected and disallowed at compile time. -- JamesActually, forget the specific example. What I'm getting at is that, right now, it's possible to cast away const /accidentally/. My change would ensure that that was no longer the case. And you miss one of the major points about C++'s const_cast<>. Yes, we all agree that it's a BAD THING to do. But the point about C++'s const_cast<> is not that it will make constness go away, it's that static_cast<> /won't/.The major point about C++'s const_cast<> is that it is defined behaviour.
Sep 09 2007
If casting away const in D always involves undefined behavior (and not only when the resulting object is modified) then it should certainly be detected and disallowed at compile time.Of course. However, according to Walter, I quote: "Because a static type system can be a straitjacket, there needs to be a way to circumvent it for special cases." and... "The ability to cast away invariant-correctness is necessary in some cases where the static typing is incorrect and not fixable, such as when referencing code in a library one cannot change. Casting is, as always, a blunt and effective instrument, and when using it to cast away invariant-correctness, one must assume the responsibility to ensure the invariantness of the data, as the compiler will no longer be able to statically do so." Both of those quotes indicate that D requires a way to do the unthinkable. Therefore, as stated above, and (somewhat better) on another thread, I argue that the solution is to disallow it - except with some special, unambiguous syntax, which makes plain you are deliberately breaking the rules. Preferably something greppable. See other thread.
Sep 09 2007
Janice Caron wrote:It may well be necessary; if it is, the behaviour must be defined so that it _works_. -- JamesIf casting away const in D always involves undefined behavior (and not only when the resulting object is modified) then it should certainly be detected and disallowed at compile time.Of course. However, according to Walter, I quote: "Because a static type system can be a straitjacket, there needs to be a way to circumvent it for special cases." and... "The ability to cast away invariant-correctness is necessary in some cases where the static typing is incorrect and not fixable, such as when referencing code in a library one cannot change. Casting is, as always, a blunt and effective instrument, and when using it to cast away invariant-correctness, one must assume the responsibility to ensure the invariantness of the data, as the compiler will no longer be able to statically do so." Both of those quotes indicate that D requires a way to do the unthinkable. Therefore, as stated above, and (somewhat better) on another thread, I argue that the solution is to disallow it - except with some special, unambiguous syntax, which makes plain you are deliberately breaking the rules. Preferably something greppable. See other thread.
Sep 09 2007
James Dennett wrote: ...It may well be necessary; if it is, the behaviour must be defined so that it _works_. -- JamesI thought that is the case? Maybe it should be stated (more?) explicitly though.
Sep 09 2007
It may well be necessary; if it is, the behaviour must be defined so that it _works_.No, it's undefined for a reason. It should stay undefined. Circumvention is necessary so that you can call library functions which are incorrectly declared. For example, if a library function is declared as int strlen(char[] s); /* erroneous declaration - function does not modify the bytes of s */ then circumventing the normal casting rules would be harmless, because no undefined behaviour is being invoked. The key point here is that you would have to /know/, with absolute certainty, that strlen() was not going to modify the bytes of your string. If you made the wrong call ... well, then that's when the undefined behaviour would kick in. No, Walter is absolutely right to make it undefined. And also absolutely right to allow circumvention. My only argument is that circumvention should require a different syntax.
Sep 09 2007
Janice Caron wrote:Just to be clear: I assume the behavior of casting away const and then reading the ex-const variable is defined, otherwise it doesn't make much sense. The only thing undefined is writing a const casted variable.It may well be necessary; if it is, the behaviour must be defined so that it _works_.No, it's undefined for a reason. It should stay undefined.
Sep 09 2007
Just to be clear: I assume the behavior of casting away const and then reading the ex-const variable is defined, otherwise it doesn't make much sense. The only thing undefined is writing a const casted variable.Yes. It's undefined for all sort of reasons ... either because it might not be /possible/ to write it (because it might be in ROM); or because changing might violate thread-safety; etc.. But you can always read it, whether it's const or not.
Sep 09 2007
Janice Caron wrote:You may be missing my point. I'll try to explain more clearly below. The key is *what* is defined/undefined.It may well be necessary; if it is, the behaviour must be defined so that it _works_.No, it's undefined for a reason. It should stay undefined.Circumvention is necessary so that you can call library functions which are incorrectly declared. For example, if a library function is declared as int strlen(char[] s); /* erroneous declaration - function does not modify the bytes of s */ then circumventing the normal casting rules would be harmless, because no undefined behaviour is being invoked. The key point here is that you would have to /know/, with absolute certainty, that strlen() was not going to modify the bytes of your string. If you made the wrong call ... well, then that's when the undefined behaviour would kick in.So the cast was *not* undefined at all -- only the act of modifying after that is undefined. That's fine.No, Walter is absolutely right to make it undefined. And also absolutely right to allow circumvention.You can't do both. The behavior of the cast must be well defined, otherwise it does not allow circumvention. The result of modification likely should be undefined, but that is a different matter.My only argument is that circumvention should require a different syntax.I'd agree with that; it's something C++ has almost right (though backwards with C means that you can const_cast without being explicit). D's single cast syntax is rather a step backwards from C++. -- James
Sep 09 2007
D's single cast syntax is rather a step backwards from C++.D's single cast syntax dates from a time before there was const. If there's no const, you don't need a const_cast. Only with the advent of const in 2.0 do we start to encounter problems.
Sep 09 2007
Janice Caron wrote:Actually, there's one other deficiency in only having a single cast that trips up newbies from time to time: Why does cast(int) 42.31 == 42, but cast(int[]) [42.31] fail? Because one is doing a *cast* and the other is doing a *conversion.* Once you involve things like opCast and opImplicitCast, it gets even harder to tell which it's doing. Of course, I'm *dying* to get my hands on opImplicitCast, so I'll just be quiet now... :3 -- DanielD's single cast syntax is rather a step backwards from C++.D's single cast syntax dates from a time before there was const. If there's no const, you don't need a const_cast. Only with the advent of const in 2.0 do we start to encounter problems.
Sep 09 2007
Janice Caron wrote:There are other problems with a single cast notation; constness is not the only one. Casts on numeric types fall into various categories; pure widening casts are entirely safe, narrowing casts between integrals types can be safe except for truncation, casts from some integral types to suitable floating point types are value preserving, casts from floating point values to integral types tend to lose information and risk range errors. Casting up a class hierarchy (in the presence of single inheritance only) is safe; downcasts can fail. Converting between pointers and integral types can often be problematic. Even C++'s classification of different types of cast is not as granular as I'd like, and I've found an implicit_cast<T> to be useful while a lossless_cast<T> has theoretical appeal. (The absence of MI from D means that there is less need for something akin to a full-blown dynamic_cast: there's no cross-casting in a single-inheritance chain.) -- JamesD's single cast syntax is rather a step backwards from C++.D's single cast syntax dates from a time before there was const. If there's no const, you don't need a const_cast. Only with the advent of const in 2.0 do we start to encounter problems.
Sep 09 2007
Content-Disposition: inline From the sneaky tricks department... You know, is it actually possible to circumvent const/invariant correctness *without using a cast at all*. Of course, as with cirucumvention by any other means, this will lead to undefined behaviour, but, just for a laugh... string s = "cat"; /* invariant */ /* now let's modify the string, without using cast */ char * p; p += &s[0] - null; /* drum roll... */ *p = 'b'; writefln(s); /* writes "bat" on Windows */ Never, ever do this! :-)
Sep 10 2007
Janice Caron wrote:Circumvention is necessary so that you can call library functions which are incorrectly declared. For example, if a library function is declared as int strlen(char[] s); /* erroneous declaration - function does not modify the bytes of s */ then circumventing the normal casting rules would be harmless, because no undefined behaviour is being invoked. The key point here is that you would have to /know/, with absolute certainty, that strlen() was not going to modify the bytes of your string. If you made the wrong call ... well, then that's when the undefined behaviour would kick in.And then what happens when you use a new version of the library, where your assumption is no longer valid? Will the vast majority of libraries be diligent in specifying const? Is const going to clutter the code everywhere? What's the *good* reason that const isn't the default, at least for function parameters? -Jeff
Sep 10 2007
On 9/10/07, Jeff Nowakowski <jeff dilacero.org> wrote:And then what happens when you use a new version of the library, where your assumption is no longer valid?Now you're just trying to cause trouble. :-) A new version of strlen that modifies your string? Come on! If /that/ assumption is no longer valid then you are the victim of a malicious attack. You can't blame D for that, and nor can you protect against it. A malicious attacker could declare the function as strlen(const(char)[]) and /still/ modify the string.What's the *good* reason that const isn't the default, at least for function parameters?Passing classes around, for one.
Sep 10 2007