Memory/Allocation attributes for variables
Elmar
chrehme at gmx.de
Thu Jun 3 17:26:03 UTC 2021
Thank you for answering.
On Tuesday, 1 June 2021 at 06:12:05 UTC, Ola Fosheim Grostad
wrote:
...
>> The thing I'd like to gain with those attributes is a
>> guarantee, that the referenced value wasn't allocated in a
>> certain address region/scope and lives in a
>> lifetime-compatible scope which can be detected by checking
>> the pointer value against an interval or a range of intervals.
>> For example a returned reference to an integer could have been
>> created with "malloc" or even a C++ allocator or interfacing
>> functions could annotate parameters with such attributes.
>
> Well, I guess you are new, but Walter will refuse having many
> pointer types. Even the simple distinction between gc and raw
> pointers will be refused. The reason being that it would lead
> to a combinatorial explosion of function instances and prevent
> separate compilation.
The separate compilation is a good point. Binary compatibility is
a common property considered for security safeguards. But at
least static checking with attributes would need no memory
addresses at all (also if the compiler can infer the attribute
for every value-typed variable automatically from where it is
defined). Dynamic checks of pointers accross binary interfaces
are difficult. It would work flawlessly with library-internal
memory regions but for outside pointer values it can only rely on
runtime information (memory regions used by allocators) or cannot
perform checks at all (because it doesn't know the address ranges
to check against). Or it would work better if binaries would
support relocations for application-related memory addresses
which are filled at link time. Static checks strike the balance
here.
> I personally have in the past argued that it would be an
> interesting experiment to make all functions templates and
> template pointer parameter types.
>
> That you can do with library pointer types, as a proof of
> concept, yourself. Then you will see what the effect is.
Okay, that's fine. Pointers in D are not debatable, I would not
try. I think, any new language should remove the concept of
pointers entirely rather than introducing new pointers. Pointers
from C should be treated as reference variables, pointers to C as
either an unbounded slice (if bounded, there should be another
`size_t` argument to the function) or it passes addresses
obtained from variables. As a C programmer I'd say that C's
pointer concept was never needed as it stands, it just was
created to be an unsafe reference variable + a reference + an
iterator all-in-one-solution as the simplest generic thing which
beats it all (without knowing the use case by looking at the
pointer type).
Attributes only would check properness of pointer value
assignments without code duplication of the function as `auto
ref` is doing. (One can still interprete it as part of the type.)
On Tuesday, 1 June 2021 at 06:12:05 UTC, Ola Fosheim Grostad
wrote:
>> lifetime region with equal lifetime. The comparison between
>> stack addresses assumes that an address deeper in the stack
>> has a higher or equal lifetime. The caller could also provide
>> it's stack frame bounds which allows to consider this interval
>> as one single lifetime.
>
> How about coroutines? Now you have multiple stacks.
Thanks, I missed that, at least true coroutines have. Other
things also can dissect stack-frame memory (function-specific
allocators in the stack-region). But in our case, it's already a
question whether such special stack frames still should be
allocated in the stack-region, statically (as I implemented it
once for C) or in a heap region (like stack frames of
continuations). You could at least place coroutine stack frames
in some allocator region in static memory.
A probably less fragile but more costly solution (when checking
stack-addresses) for stack address scope would be storing the
stack depth of an address in the upper k-bit portion of a wide
pointer value (for a simple check) but this is only a further
unrelated idea.
> Dynamic checks are unlikely to be accepted, I suggest you do
> this as a library.
Right, if nobody tried it so far I'd like myself. Then I can firm
my D experience with further practice. I'd compare the nature of
static and dynamic attribute checks to the nature of C++
`static_cast` and `dynamic_cast` of class pointers. I was
thinking, such a user library could use `__traits` with templated
operator overloads.
>> Where the feature shines most is function signatures because
>> they separate code and create intransparency which can be
>> countered by memory attributes for return type and argument
>> types.
>
> Unfortunately, this is also why it will be rejected.
So, is that D's tenor that function signatures are thought to
create *in*transparency and should continue to do so? Does the
community think, allocation and memory transparency is a bad
thing or just not needed? IMO, allocation and memory transparency
is relevant to being a serious Systems programming language (even
though C doesn't have it, C++ doesn't have it and C# is no
Systems Programming :-D ). Isn't the missing memory transparency
from outside of functions the reason why global variables are
frowned upon by many? Related to referential transparency (side
effects), less transparency makes programs harder to debug,
decouple and APIs harder to use right. (Just the single `map`
issue with fixed-size arrays...)
>> Okay, I didn't define aliasing. With "aliasing" I mean that
>> "aliasing references" (or pointers) either point to the exact
>> same address or that the immediately pointed class/struct
>> (pointed to by the reference/pointer) does not overlap. I
>> would consider anything else more complicated than necessary.
>
> Insufficient for D with library container types and library
> smart pointers.
Yeah. It makes no sense if we consider the pointer layers between
the exposed pointer and the actual data (I assume, smart pointers
in D are implemented with such a middle layer in between). But if
it only means the first payload data layer that represents the
actual root node of any graph-like data structure, is it still
flawed? At least, if I can annotate all pointer variables in my
data structures and if checks are done for every single
reference/pointer assignment with any access so that no pointer
value range in the entire structure ever becomes violated, isn't
it closer to memory safety than without? Of course, I could still
pass references to those pointers to a binary which write into it
without knowing any type information but that's a deliberate risk
which static type checking cannot mitigate, only dynamic value
checking of the pointed data after function return. (Probably
another useful safety feature for my idea.)
Of course attributes are optional, nobody has to annotate
anything with the risk of obtaining falsely scoped pointer values.
But would you agree, it would be better than not having it? Of
course, it doesn't make everything safe, particularly if one can
omit it but annotating variables with attributes could help with
ownership (I think in a better design than Walter's proposal of
yet another function attribute @live instead of a variable
attribute). With ownership I mean to prevent leakage of
(sensible) data out of a function (not just reference values as
with `scope`) and could provide some sanity checks and even
provide more transparency for API use (because then I can see
what kind of allocated memory I can expect for parameters and
return value). I think, it could improve interfacing with C++ as
well.
At the end, I only want certainty about the references and
pointers when I look into a function signature.
I probably should (try to) implement it myself as a proof of
concept.
Regards, Elmar
More information about the Digitalmars-d
mailing list