www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Compile time opAssign/ property constraints

reply Jacob Shtokolov <jacob.100205 gmail.com> writes:
Hi,

I'd like to implement some compile time constraints for a struct 
(though not sure if that's possible).

I already tried to place "static assert" or any kind of static 
condition into a body of  property and opAssign(), but every time 
it shows the error "variable cannot be read at compile time".

Is there any way to catch and validate assignments or struct 
initialization at compile time?

Thanks,
Jacob
Jan 04
next sibling parent reply Basile.B <b2.temp gmx.com> writes:
On Friday, 4 January 2019 at 09:54:25 UTC, Jacob Shtokolov wrote:
 Hi,

 I'd like to implement some compile time constraints for a 
 struct (though not sure if that's possible).

 I already tried to place "static assert" or any kind of static 
 condition into a body of  property and opAssign(), but every 
 time it shows the error "variable cannot be read at compile 
 time".

 Is there any way to catch and validate assignments or struct 
 initialization at compile time?

 Thanks,
 Jacob
What you want is definitively possible but you must have made an error somewhere. Show us some code.
Jan 04
parent reply Jacob Shtokolov <jacob.100205 gmail.com> writes:
On Friday, 4 January 2019 at 10:34:07 UTC, Basile.B wrote:
 Show us some code.
Here is the simple example: https://run.dlang.io/gist/1a06dd703bea5548ee72b4713a7ce5f6 The thing I'm trying to do is to make an experimental port (for education purposes) of https://github.com/fthomas/refined library for Scala, which allows to set constraints on basic types like numeric, bool, string, etc. For example, you can force an integer variable to take a range between 0 and 15. And if constraint is not satisfied, you get a compile time error. There is no predicate in my example, but even if I add one (using alias template parameter), it shows the same error. So is that possible in D?
Jan 04
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
On Friday, 4 January 2019 at 11:45:24 UTC, Jacob Shtokolov wrote:
 On Friday, 4 January 2019 at 10:34:07 UTC, Basile.B wrote:
 Show us some code.
Here is the simple example: https://run.dlang.io/gist/1a06dd703bea5548ee72b4713a7ce5f6 The thing I'm trying to do is to make an experimental port (for education purposes) of https://github.com/fthomas/refined library for Scala, which allows to set constraints on basic types like numeric, bool, string, etc. For example, you can force an integer variable to take a range between 0 and 15. And if constraint is not satisfied, you get a compile time error. There is no predicate in my example, but even if I add one (using alias template parameter), it shows the same error. So is that possible in D?
You have'd to use a template to "construct" your variables; struct ConstrainedInt { int val; alias val this; } template makeConstrainedInt(int Value) { static assert(Value <= 15 && Value >= 0); enum makeConstrainedInt = ConstrainedInt(Value); } However this relies on your virtue not to call constraintInt constructor directly. and always use the template.
Jan 04
prev sibling parent reply Jacob Shtokolov <jacob.100205 gmail.com> writes:
On Friday, 4 January 2019 at 11:45:24 UTC, Jacob Shtokolov wrote:
 Here is the simple example:

 https://run.dlang.io/gist/1a06dd703bea5548ee72b4713a7ce5f6
Sorry, invalid link. Here is a new one: https://run.dlang.io/is/QZ5hLV
Jan 04
parent reply Mike Parker <aldacron gmail.com> writes:
On Friday, 4 January 2019 at 11:53:41 UTC, Jacob Shtokolov wrote:
 On Friday, 4 January 2019 at 11:45:24 UTC, Jacob Shtokolov 
 wrote:
 Here is the simple example:

 https://run.dlang.io/gist/1a06dd703bea5548ee72b4713a7ce5f6
Sorry, invalid link. Here is a new one: https://run.dlang.io/is/QZ5hLV
``` property val(T v) { static assert(v > 0); value = v; } ``` v is a run-time value, not available at compile time.
Jan 04
parent Jacob Shtokolov <jacob.100205 gmail.com> writes:
On Friday, 4 January 2019 at 14:36:16 UTC, Mike Parker wrote:
 v is a run-time value, not available at compile time.
Sorry about that, looks like if I edit the text in the run.dlang.io editor, the link also gets updated. I was using "void opAssign(T)(T v)" in the initial example, but it seems that I got the idea. So even if I'd write opAssign or property as a template function, I won't be able to get their arguments at compile time because every template takes different set of arguments: for compile time and for run time. And due to the fact that D is calling opAssign as obj.opAssign(arg) and not as obj.opAssign!(arg)(arg), this is not possible to get the runtime arguments. On Saturday, 5 January 2019 at 01:38:43 UTC, Jonathan M Davis wrote:
 I suggest that you read

 https://wiki.dlang.org/User:Quickfur/Compile-time_vs._compile-time

 IIRC, it's still a work in progress, but it should give you a 
 much clearer idea of how CTFE fits into things.
Many thanks for this article! Now I understand it much better. So it seems that the only "true way" is to use the struct invariant feature, but this will work only at run time. It turned out that I just want some compile-time mechanism (static analyzer?) that will validate (solve) all reachable constraints in the program. Then I'd like to reformulate the question: is there any tools or compiler features that are capable of validating asserts at compile time? Thanks!
Jan 05
prev sibling parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Friday, 4 January 2019 at 09:54:25 UTC, Jacob Shtokolov wrote:
 Hi,

 I'd like to implement some compile time constraints for a 
 struct (though not sure if that's possible).

 I already tried to place "static assert" or any kind of static 
 condition into a body of  property and opAssign(), but every 
 time it shows the error "variable cannot be read at compile 
 time".

 Is there any way to catch and validate assignments or struct 
 initialization at compile time?
Well, yes and no. The error message you're getting seems to indicate you're trying to do something impossible, but it could be you simply haven't understood the limits of what can and cannot be done. If I were to venture a guess, I'd say you're trying to disallow certain values - something along the lines of an int with a limited range, like Ada's integer ranges. An example would be percentages: struct Percentage { int value; void opAssign(int v) { assert(v >= 0, "Value is too low!"); assert(v <= 100, "Value is too high!"); value = v; } } D does not let you limit the set of valid values like this at compile-time, instead the tests must be implemented at run-time, like above. Attempting to use static assert above would give the exact error message you mention. There are many things that can be tested at compile-time, so if your use case is not analogous with the above, it may well be possible to implement compile-time testing of it. The thing is, compile-time tests like static if and static assert can only test values that are known at compile-time, and are for the most part useful only in templates. -- Simen
Jan 04
parent reply Jacob Shtokolov <jacob.100205 gmail.com> writes:
On Friday, 4 January 2019 at 11:41:59 UTC, Simen Kjærås wrote:
 The thing is, compile-time tests like static if and static 
 assert can only test values that are known at compile-time, and 
 are for the most part useful only in templates.
Thanks for this answer! That's sad to hear. But, is there anything to do with CTFE? Can it help somehow in such situation?
Jan 04
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Friday, January 4, 2019 4:50:30 AM MST Jacob Shtokolov via Digitalmars-d-
learn wrote:
 On Friday, 4 January 2019 at 11:41:59 UTC, Simen Kjrs wrote:
 The thing is, compile-time tests like static if and static
 assert can only test values that are known at compile-time, and
 are for the most part useful only in templates.
Thanks for this answer! That's sad to hear. But, is there anything to do with CTFE? Can it help somehow in such situation?
CTFE is the compile-time evaluation of functions. You're calling a function at compile time. How the function works is basically the same as how it works at runtime. There are some caveats because of how the CTFE engine works (e.g. pointer arithmetic isn't legal at compile time), but the function itself is called normally. Something like a static assertion is run when the function itself is compiled - which must happen before that function is used during CTFE. So, you can do something like auto foo(int i) { static assert(someFunc() == 42); ... } but you can't do something like auto foo(int i) { static assert(someFunc() == i); ... } because that would be mixing compile time and runtime stuff. Even if foo is called at compile time, it's compiled before it's called, and the static assertion is part of its compilation process, not part of running it, and runtime variables aren't available when the function is being compiled. So, if you did auto foo(int i) { assert(someFunc() == i); ... } and then called foo with CTFE, then that assertion would be run as part of running foo just like it would be at runtime, but a static assertion wouldn't make sense. CTFE doesn't fundamentally change what is a compile-time and what is a runtime constructs. It just allows functions to be called at compile time so that you can do stuff like initialize values that are generated at compile time. Unfortunately, the wiki seems be down right now, but once it's back up, I suggest that you read https://wiki.dlang.org/User:Quickfur/Compile-time_vs._compile-time IIRC, it's still a work in progress, but it should give you a much clearer idea of how CTFE fits into things. - Jonathan M Davis
Jan 04