From d0ba30675b481ab7d314bf8e3ee34b7911e70233 Mon Sep 17 00:00:00 2001 From: Steven Schveighoffer Date: Tue, 14 Jun 2016 09:09:47 -0400 Subject: [PATCH 1/4] Add mechanism for pointers that prevents dereferencing, but tries to act like pointers for pointer comparison and math. All usable with @safe code. --- mak/COPY | 1 + mak/SRCS | 1 + src/core/internal/array.d | 147 +++++++++++++++++++++++++++++++++++++ src/core/internal/traits.d | 15 ++++ src/rt/util/array.d | 17 ++--- 5 files changed, 170 insertions(+), 11 deletions(-) create mode 100644 src/core/internal/array.d diff --git a/mak/COPY b/mak/COPY index 9d8d75ca2a..f0129ea133 100644 --- a/mak/COPY +++ b/mak/COPY @@ -16,6 +16,7 @@ COPY=\ $(IMPDIR)\core\vararg.d \ \ $(IMPDIR)\core\internal\abort.d \ + $(IMPDIR)\core\internal\array.d \ $(IMPDIR)\core\internal\convert.d \ $(IMPDIR)\core\internal\hash.d \ $(IMPDIR)\core\internal\spinlock.d \ diff --git a/mak/SRCS b/mak/SRCS index d1a9ba9725..122a5d692e 100644 --- a/mak/SRCS +++ b/mak/SRCS @@ -17,6 +17,7 @@ SRCS=\ src\core\vararg.d \ \ src\core\internal\abort.d \ + src\core\internal\array.d \ src\core\internal\convert.d \ src\core\internal\hash.d \ src\core\internal\spinlock.d \ diff --git a/src/core/internal/array.d b/src/core/internal/array.d new file mode 100644 index 0000000000..b06fcad32c --- /dev/null +++ b/src/core/internal/array.d @@ -0,0 +1,147 @@ +module core.internal.array; +import core.stdc.stdint : uintptr_t; + +/* + * This allows safe use of a pointer without dereferencing. Useful when you + * want to declare that all you care about it the pointer value itself. Keeps + * the pointer as a pointer to allow GC to properly track things. + * + * Note that PtrVal has all the same characteristics of a normal pointer, + * except it doesn't allow dereferencing, and doesn't allow pointer math + * without jumping into simple integer types. + */ +struct PtrVal(T) +{ + private T *_ptr; + + /* + * Get the pointer as a non-dereferencable unsigned integer. + */ + uintptr_t value() const @trusted + { + return cast(uintptr_t)_ptr; + } + + /* + * Keep the mechanism that returns a signed pointer difference between pointers. + */ + ptrdiff_t opBinary(string op : "-")(const(PtrVal) other) const @trusted + { + return _ptr - other._ptr; + } + + // ditto + ptrdiff_t opBinary(string op : "-")(const(T*) other) const @trusted + { + return _ptr - other; + } + + // ditto + ptrdiff_t opBinaryRight(string op : "-")(const(T*) other) const @trusted + { + return other - _ptr; + } + + // disable subtraction between undefined types -- we don't want to + // accidentally subtract between the value() and some unknown type. + ptrdiff_t opBinary(string op : "-", X)(X) const @safe + { + static assert(0, "Cannot subtract between " ~ typeof(this).stringof ~ " and " ~ X.stringof); + } + + /* + * Use this if you are in system code and wish to get back to unsafe + * pointer-land. + */ + inout(T) *ptr() inout @system + { + return _ptr; + } + + /* + * Devolves to simple unsigned integer if any other operations are used + */ + alias value this; + + + /* + * Cast to void pointer type. TODO: see if there is a way to make this + * implicit. + */ + auto toVoid() inout @trusted + { + static if(is(const(T)* == const(void)*)) + { + // already a void pointer + return this; + } + else + { + // need to transfer mutability modifier of _ptr + import core.internal.traits: ModifyTypePreservingTQ; + alias voidify(M) = void; + alias VType = ModifyTypePreservingTQ!(voidify, T); + return inout(.PtrVal!(VType))(_ptr); + } + } +} + +/* + * Factory method + */ +inout(PtrVal!T) ptrval(T)(inout(T)* ptr) @safe +{ + return inout(PtrVal!T)(ptr); +} + +/* + * Allow safe access to array.ptr as a PtrVal. + */ +inout(PtrVal!T) ptrval(T)(inout(T)[] arr) @trusted +{ + return arr.ptr.ptrval; +} + +unittest +{ + import core.internal.traits: TypeTuple; + void testItSafe(T)(T[] t1, T[] t2) @safe + { + auto p1 = t1.ptrval; + auto p2 = t2.ptrval; + + assert(p2 - p1 == 5, T.stringof); + assert(p1 - p2 == -5, T.stringof); + assert(p2.toVoid - p1.toVoid == 5 * T.sizeof, T.stringof); + assert(p1.toVoid - p2.toVoid == -5 * T.sizeof, T.stringof); + + auto p3 = &t1[0]; + auto p4 = &t2[0]; + + assert(p1 - p3 == 0, T.stringof); + assert(p4 - p2 == 0, T.stringof); + } + + void testIt(T)(inout int = 0) + { + T[] arr = new T[10]; + testItSafe(arr[0 .. 5], arr[5 .. $]); + + // test getting pointer back from PtrVal. + auto p1 = arr.ptrval; + auto p2 = arr[5 .. $].ptrval; + + assert(p1.ptr + 5 == p2.ptr, T.stringof); + assert(p1.ptr == arr.ptr, T.stringof); + } + + class C {} + struct S {ubyte x;} + foreach(T; TypeTuple!(ubyte, byte, ushort, short, uint, int, ulong, long, float, double, real, C, S)) + { + testIt!(T)(); + testIt!(const(T))(); + testIt!(immutable(T))(); + testIt!(inout(T))(); + } +} diff --git a/src/core/internal/traits.d b/src/core/internal/traits.d index 8142f90c51..aa1d5f2d74 100644 --- a/src/core/internal/traits.d +++ b/src/core/internal/traits.d @@ -187,3 +187,18 @@ template hasElaborateCopyConstructor(T...) else enum bool hasElaborateCopyConstructor = false; } + +// (from phobos) +package template ModifyTypePreservingTQ(alias Modifier, T) +{ + static if (is(T U == immutable U)) alias ModifyTypePreservingTQ = immutable Modifier!U; + else static if (is(T U == shared inout const U)) alias ModifyTypePreservingTQ = shared inout const Modifier!U; + else static if (is(T U == shared inout U)) alias ModifyTypePreservingTQ = shared inout Modifier!U; + else static if (is(T U == shared const U)) alias ModifyTypePreservingTQ = shared const Modifier!U; + else static if (is(T U == shared U)) alias ModifyTypePreservingTQ = shared Modifier!U; + else static if (is(T U == inout const U)) alias ModifyTypePreservingTQ = inout const Modifier!U; + else static if (is(T U == inout U)) alias ModifyTypePreservingTQ = inout Modifier!U; + else static if (is(T U == const U)) alias ModifyTypePreservingTQ = const Modifier!U; + else alias ModifyTypePreservingTQ = Modifier!T; +} + diff --git a/src/rt/util/array.d b/src/rt/util/array.d index 8060338733..461dfc7337 100644 --- a/src/rt/util/array.d +++ b/src/rt/util/array.d @@ -10,6 +10,7 @@ module rt.util.array; import core.internal.string; +import core.internal.array; import core.stdc.stdint; @@ -20,7 +21,7 @@ void enforceTypedArraysConformable(T)(const char[] action, { _enforceSameLength(action, a1.length, a2.length); if(!allowOverlap) - _enforceNoOverlap(action, arrayToPtr(a1), arrayToPtr(a2), T.sizeof * a1.length); + _enforceNoOverlap(action, a1.ptrval, a2.ptrval, T.sizeof * a1.length); } void enforceRawArraysConformable(const char[] action, in size_t elementSize, @@ -28,7 +29,7 @@ void enforceRawArraysConformable(const char[] action, in size_t elementSize, { _enforceSameLength(action, a1.length, a2.length); if(!allowOverlap) - _enforceNoOverlap(action, arrayToPtr(a1), arrayToPtr(a2), elementSize * a1.length); + _enforceNoOverlap(action, a1.ptrval, a2.ptrval, elementSize * a1.length); } private void _enforceSameLength(const char[] action, @@ -47,10 +48,10 @@ private void _enforceSameLength(const char[] action, throw new Error(msg); } -private void _enforceNoOverlap(const char[] action, - uintptr_t ptr1, uintptr_t ptr2, in size_t bytes) +private void _enforceNoOverlap(T)(const char[] action, + const PtrVal!(T) ptr1, const PtrVal!(T) ptr2, in size_t bytes) { - const d = ptr1 > ptr2 ? ptr1 - ptr2 : ptr2 - ptr1; + const d = ptr1 > ptr2 ? ptr1.toVoid - ptr2.toVoid : ptr2.toVoid - ptr1.toVoid; if(d >= bytes) return; const overlappedBytes = bytes - d; @@ -64,9 +65,3 @@ private void _enforceNoOverlap(const char[] action, msg ~= bytes.unsignedToTempString(tmpBuff, 10); throw new Error(msg); } - -private uintptr_t arrayToPtr(const void[] array) @trusted -{ - // Ok because the user will never dereference the pointer - return cast(uintptr_t)array.ptr; -} From 8af80e6490fd4046d209aeb3c34243cdd72cff31 Mon Sep 17 00:00:00 2001 From: Steven Schveighoffer Date: Tue, 14 Jun 2016 09:45:29 -0400 Subject: [PATCH 2/4] Fix windows build --- win32.mak | 3 +++ win64.mak | 3 +++ 2 files changed, 6 insertions(+) diff --git a/win32.mak b/win32.mak index a1996f3f27..cc381a302b 100644 --- a/win32.mak +++ b/win32.mak @@ -263,6 +263,9 @@ $(IMPDIR)\core\vararg.d : src\core\vararg.d $(IMPDIR)\core\internal\abort.d : src\core\internal\abort.d copy $** $@ +$(IMPDIR)\core\internal\array.d : src\core\internal\array.d + copy $** $@ + $(IMPDIR)\core\internal\convert.d : src\core\internal\convert.d copy $** $@ diff --git a/win64.mak b/win64.mak index 84c8838933..53a9f693db 100644 --- a/win64.mak +++ b/win64.mak @@ -271,6 +271,9 @@ $(IMPDIR)\core\vararg.d : src\core\vararg.d $(IMPDIR)\core\internal\abort.d : src\core\internal\abort.d copy $** $@ +$(IMPDIR)\core\internal\array.d : src\core\internal\array.d + copy $** $@ + $(IMPDIR)\core\internal\convert.d : src\core\internal\convert.d copy $** $@ From 33f775bf5c977ffb7ff05f56b2b719a596c17f38 Mon Sep 17 00:00:00 2001 From: Steven Schveighoffer Date: Tue, 14 Jun 2016 11:30:51 -0400 Subject: [PATCH 3/4] fix typo --- src/core/internal/array.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/internal/array.d b/src/core/internal/array.d index b06fcad32c..5acf16b513 100644 --- a/src/core/internal/array.d +++ b/src/core/internal/array.d @@ -3,7 +3,7 @@ import core.stdc.stdint : uintptr_t; /* * This allows safe use of a pointer without dereferencing. Useful when you - * want to declare that all you care about it the pointer value itself. Keeps + * want to declare that all you care about is the pointer value itself. Keeps * the pointer as a pointer to allow GC to properly track things. * * Note that PtrVal has all the same characteristics of a normal pointer, From f6a2d9747620e1f0cdf1af147dd6b61ac811ab80 Mon Sep 17 00:00:00 2001 From: Steven Schveighoffer Date: Thu, 16 Jun 2016 09:31:18 -0400 Subject: [PATCH 4/4] Make unittest pure/nothrow, avoid extra imports --- src/core/internal/array.d | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/internal/array.d b/src/core/internal/array.d index 5acf16b513..aa86dc6133 100644 --- a/src/core/internal/array.d +++ b/src/core/internal/array.d @@ -67,8 +67,10 @@ struct PtrVal(T) /* * Cast to void pointer type. TODO: see if there is a way to make this * implicit. + * + * Templated to avoid extra imports if not used. */ - auto toVoid() inout @trusted + auto toVoid()() inout @trusted { static if(is(const(T)* == const(void)*)) { @@ -102,7 +104,7 @@ inout(PtrVal!T) ptrval(T)(inout(T)[] arr) @trusted return arr.ptr.ptrval; } -unittest +pure nothrow unittest { import core.internal.traits: TypeTuple; void testItSafe(T)(T[] t1, T[] t2) @safe