Thread overview
Compile time opAssign/@property constraints
Jan 04, 2019
Jacob Shtokolov
Jan 04, 2019
Basile.B
Jan 04, 2019
Jacob Shtokolov
Jan 04, 2019
Stefan Koch
Jan 04, 2019
Jacob Shtokolov
Jan 04, 2019
Mike Parker
Jan 05, 2019
Jacob Shtokolov
Jan 04, 2019
Simen Kjærås
Jan 04, 2019
Jacob Shtokolov
Jan 05, 2019
Jonathan M Davis
January 04, 2019
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
January 04, 2019
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.
January 04, 2019
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
January 04, 2019
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?
January 04, 2019
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?
January 04, 2019
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.
January 04, 2019
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

January 04, 2019
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.
January 04, 2019
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 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?

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




January 05, 2019
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!