www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - isCallable fails

reply frame <frame86 live.com> writes:
What could cause the following error?

Error: expression isCallable!(data) of type void does not have a 
boolean value


Problematic lines are:


// T         = some class
// S         = "data" (data is member of T)
// typeof(S) = some struct U!X, not void

alias S = __traits(getMember, T, property);

static if (isCallable!S) {
     // some other magic
}


So, struct U has an "alias this", not causing any problems except 
if it calls the getter on type X:

struct U(T) {
   T get() {
         static if (is(T : X)) {
             if (value is null) {
                 // load value
             }
         }
         return value;
   }

   T value;
   alias get this;
}


I do not understand why this can affect isCallable as it does 
nothing special. It also compiles if I remove the loading "static 
if" switch.
Jan 19
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
I may know the answer but I can't be sure because you don't provide code 
that reproduces the issue.

I am trying to write code below according to your description but it's 
really tough. Can you reproduce the issue please.

// T         = some class
// T is not a good name because it is usually used template parameters
class T {
   static U!int data = 42;
}

// S         = "data" (data is member of T)
auto S = T.data;
// typeof(S) = some struct U!X, not void

// WAIT! Another S? You're asking for trouble. :D
alias S = __traits(getMember, T, property);

static if (isCallable!S) {
     // some other magic
}

// WAIT! So, this is U?
struct U(T) {
   T get() {
         static if (is(T : X)) {
             if (value is null) {
                 // load value
             }
         }
         return value;
   }

   T value;
   alias get this;
}

void main() {
}

Ali
Jan 19
parent reply frame <frame86 live.com> writes:
On Wednesday, 20 January 2021 at 01:07:15 UTC, Ali Çehreli wrote:
 I may know the answer but I can't be sure because you don't 
 provide code that reproduces the issue.

 I am trying to write code below according to your description 
 but it's really tough. Can you reproduce the issue please.

 // T         = some class
 // T is not a good name because it is usually used template
ok, sorry my bad - I have just shortend the code, the first part is from a template indeed: void something(T, string property)() { // in THIS context is T a class // property = "data" alias S = __traits(getMember, T, property); // typeof(S) = foo!bar static if (isCallable!S) { // some other magic } // ... } And struct of course, has another identfier: struct foo(T) { T get() { static if (is(T : bar)) { if (value is null) { value = fun!T; } } return value; } } There are other properties with foo!T and they are working as expected. The problem occurs if I want to assign value = fun!T.
Jan 19
parent reply Paul Backus <snarwin gmail.com> writes:
On Wednesday, 20 January 2021 at 04:43:12 UTC, frame wrote:
 struct foo(T) {
   T get() {
         static if (is(T : bar)) {
             if (value is null) {
                 value = fun!T;
Error: template instance `fun!T` template `fun` is not defined Please post an example with enough code to actually produce the error you're seeing.
Jan 20
parent reply frame <frame86 live.com> writes:
On Wednesday, 20 January 2021 at 13:11:09 UTC, Paul Backus wrote:
 On Wednesday, 20 January 2021 at 04:43:12 UTC, frame wrote:
 struct foo(T) {
   T get() {
         static if (is(T : bar)) {
             if (value is null) {
                 value = fun!T;
Error: template instance `fun!T` template `fun` is not defined Please post an example with enough code to actually produce the error you're seeing.
I don't know when to stop posting code then :( fun!T just returns bar or null. Manually calling works. but if it helps, here is fun!T: TDobj fun(TDobj, T)(string propertyName, T needle) if (is(TDobj : bar)) { TDobj[] list = loadAllMatched!TDobj(propertyName, needle, 1); return list.length > 0 ? list[0] : null; } and actually called value = fun!T("id", 123)); It also compiles if value = new T
Jan 20
next sibling parent frame <frame86 live.com> writes:
On Wednesday, 20 January 2021 at 19:01:19 UTC, frame wrote:
 It also compiles if

 value = new T
enum R = isCallable!S throws: Error: cannot infer type from template instance isCallable!(data), possible circular dependency So that seems to be the problem. There is a circular dependency somewhere the compiler cannot resolve. I believe it has something to do with "alias get this" so get() is parsed in this context or whatever. Any suggestions?
Jan 20
prev sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Wednesday, 20 January 2021 at 19:01:19 UTC, frame wrote:
 On Wednesday, 20 January 2021 at 13:11:09 UTC, Paul Backus
 Please post an example with enough code to actually produce 
 the error you're seeing.
I don't know when to stop posting code then :(
You should stop (and ideally start) with a Minimal, Complete, Verifiable Example: https://idownvotedbecau.se/nomcve/ Just to illustrate why this matters, let me walk you through the steps it takes me to try and figure out what your issue is from just the posts you've made so far. --- Step 1: go through each of your posts and copy each of the individual code snippets into a run.dlang.io window. Result: https://run.dlang.io/is/nidfug This code compiles--because it's all templates, and you haven't actually provided any example of the code that *uses* these templates. --- Step 2: look at the comments and try to guess what the usage code must look like. Result: https://run.dlang.io/is/P7Pcr1 First, we need to define a class with a member named `data` to pass as an argument to something. And data's type has to be `foo!bar`, so we need a type named `bar` to use for that. What kind of type? Well, if we look at how `bar` is used in `foo`, we can see that it's compared with `null`, so we know it's a reference type--probably a class. Now we can finally attempt to instantiate `something`. What happens when we do? Error: template instance loadAllMatched!TDobj template loadAllMatched is not defined Great, another piece of missing code. --- Step 3: try to stub out loadAllMatched. Result: https://run.dlang.io/is/uRj6HW Error: template instance `isCallable!S` template `isCallable` is not defined Siiiiigh. --- Step 4: add the missing import. Result: https://run.dlang.io/is/2by7sU Oh, look, it compiles with no errors. All that effort, and I *still* couldn't reproduce the issue you described in your original post. Guess I wasted my time for nothing!
Jan 20
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jan 20, 2021 at 09:57:59PM +0000, Paul Backus via Digitalmars-d-learn
wrote:
 On Wednesday, 20 January 2021 at 19:01:19 UTC, frame wrote:
 On Wednesday, 20 January 2021 at 13:11:09 UTC, Paul Backus
 
 Please post an example with enough code to actually produce the
 error you're seeing.
I don't know when to stop posting code then :(
You should stop (and ideally start) with a Minimal, Complete, Verifiable Example: https://idownvotedbecau.se/nomcve/
[...] And if you're not sure how to arrive at such an example, DustMite is one way of doing it: https://github.com/CyberShadow/DustMite/wiki --T
Jan 20
prev sibling parent reply frame <frame86 live.com> writes:
On Wednesday, 20 January 2021 at 21:57:59 UTC, Paul Backus wrote:
 On Wednesday, 20 January 2021 at 19:01:19 UTC, frame wrote:

 Oh, look, it compiles with no errors. All that effort, and I 
 *still* couldn't reproduce the issue you described in your 
 original post. Guess I wasted my time for nothing!
Ok, really thank you for your effort - but I was talking about the error first (did you even read it?). I have explained that isCallable gets valid data and not void. I was asking if someone has encountered this kind of error before and can give my a hint what *could* going on. I was not asking here to re-produce my code or debug since it cannot provide a snippet if I do not know what could cause the error. The example code just should illustrate that isCallable is called on a valid way and "alias this" may cause the problem (as I assumed) - that's all. Sorry, but you "wasting your time" more by unnecessarily commenting which isn't appropriate here. -- So back to topic: The error is a circular dependency (and no, it's not just in loadAllMatched!TDobj, it's something deeper). "alias get this" leads to parsing get() when isCallable is called. Is there any way to avoid running in this issue?
Jan 20
parent reply tsbockman <thomas.bockman gmail.com> writes:
On Thursday, 21 January 2021 at 05:20:27 UTC, frame wrote:
 I was not asking here to re-produce my code or debug since it 
 cannot provide a snippet if I do not know what could cause the 
 error.
Generally, I don't need to know what causes an error in order to produce an MCVE. I have successfully done so on many occasions. It's annoying and slow, but usually very possible. If it seems like it's going to be really difficult for a particular bug, it's fine to try asking others without an MCVE first. But, you should think of MCVE discovery as a valuable skill, not an impossibility. My technique works like this: 0) Make note of the symptoms/message for the specific failure that I am trying to isolate. 1) Make a separate copy of the program's source code which I can freely destroy. 2) Remove, simplify, or mock up/stub out (like for unit tests) some large chunk of the remaining code in the program, from a part of the program that I *think* is unrelated to the error. 3) Re-test the program. a) If the failure goes away, goto (2) and try again by removing a different part of the program. b) If the failure is still present and I think there is more that could be removed or simplified, goto (2). c) If the failure is still present, and I don't see anything else to remove or simplify, then I'm done. Whatever is left of the source code (hopefully something short and non-proprietary) is my MCVE. It's sort of a guess-and-check binary search for the minimum code that triggers the error. This process can be quite tedious, but it often does not require already knowing the cause of the error. (dustmite is a tool that partially automates this.) Sometimes it's worth going through, because having an MCVE is *really* useful, especially for other people who don't know the rest of your project's mostly-unrelated code base and dependencies well.
Jan 21
next sibling parent tsbockman <thomas.bockman gmail.com> writes:
On Friday, 22 January 2021 at 00:59:32 UTC, tsbockman wrote:
 2) Remove, simplify, or mock up/stub out (like for unit tests) 
 some large chunk of the remaining code in the program, from a 
 part of the program that I *think* is unrelated to the error.
 3) Re-test the program.
     a) If the failure goes away, goto (2) and try again by 
 removing a different part of the program.
I should clarify here that when I say "try again" I mean from a backup of the last version of the reduction-in-progress that *did* demonstrate the failure.
Jan 21
prev sibling parent reply frame <frame86 live.com> writes:
On Friday, 22 January 2021 at 00:59:32 UTC, tsbockman wrote:

 Generally, I don't need to know what causes an error in order 
 to produce an MCVE. I have successfully done so on many 
 occasions. It's annoying and slow, but usually very possible.
I know that I have to debug it myself, but if somebody already know this kind of error I would still ask about first since the first message makes no sense. The second error indeed explains whats going on. Anyway. It was a simple question not a prove that there is a bug in the compiler. You guys confuse a forum post question with a public examination. The simple answer if any for somebody can just be: "void" can also be the data type if the compiler has problems resolving a symbol by running into a recursion while parsing.
Jan 21
parent reply tsbockman <thomas.bockman gmail.com> writes:
On Friday, 22 January 2021 at 02:18:11 UTC, frame wrote:
 Anyway. It was a simple question not a prove that there is a 
 bug in the compiler.
isCallable is intended to either evaluate to a boolean, or give a clear compile-time error message. Since it did neither of those things for you, I suspect that something is wrong in the D project's code in addition to, or instead of, your code.
 You guys confuse a forum post question with a public 
 examination.
No, we're just used to helping each other get to the bottom of things like this. No one asked you for anything different from what we generally expect of each other in this context. This specific expectation mostly exists because, often, without a self-contained example we *truly don't know how to help you*. You are of course free to decide that our methods aren't worth your time, but please don't take it personally.
Jan 21
parent reply frame <frame86 live.com> writes:
On Friday, 22 January 2021 at 02:43:44 UTC, tsbockman wrote:

 No, we're just used to helping each other get to the bottom of 
 things like this. No one asked you for anything different from 
 what we generally expect of each other in this context. This 
 specific expectation mostly exists because, often, without a 
 self-contained example we *truly don't know how to help you*.
Please stop. I am very thankful for every hint, links to docs, tools etc. But if somebody writes "wasting his time" than it's personally (at least for me). I know it wasn't intend, somebody can have a bad day too but thats not helpful. It's like asking your mechanican if he can guess what's causing the weird sound in the car and he replies with: why didn't you disassemble your car already? But discusing about such missteps isn't helpful either and I hate it to discover such a post while searching in the web for a simple hint myself, so I will close this mentally. May someone filter out the necessary information about without wasting time.
Jan 21
parent reply tsbockman <thomas.bockman gmail.com> writes:
On Friday, 22 January 2021 at 03:08:23 UTC, frame wrote:
 It's like asking your mechanican if he can guess what's causing 
 the weird sound in the car and he replies with: why didn't you 
 disassemble your car already?
Cars are mass-produced copies of a relatively small number of rather similar designs, and mechanics' customers *pay them* to understand and fix their cars so that the customers don't have to. By contrast, computer programs are tremendously more diverse than car designs, you are not paying anyone here, and we participate here to pass our skills and knowledge on to others, not just to do their work for them. Nevertheless, I will use your analogy: You: My car shakes when I turn left. What do you think is wrong with it? Mechanic: Hard to say. Bring it in to the shop and I'll take a look. You: It's a simple question! I shouldn't have to bring it in to the shop. Mechanic: Fine. If it was my car, here's how I would go about finding the problem... (Detailed explanation follows.) Do that, and if you still can't tell what's wrong, come back and talk to me again. You: That's too much work. This is ridiculous. It's a simple question: just tell me what's wrong with my car! Mechanic: Why do you think I can diagnose the problem with *even less information than you have*? If it's so simple, why do you need help? You: Rude! I'm outa here...
Jan 21
parent frame <frame86 live.com> writes:
On Friday, 22 January 2021 at 03:40:40 UTC, tsbockman wrote:
 Cars are mass-produced copies of a relatively small number of 
 rather similar designs, and mechanics' customers *pay them* to 
 understand and fix their cars so that the customers don't have 
 to.
I knew this argument comes...
 Nevertheless, I will use your analogy:

     You: My car shakes when I turn left. What do you think is 
 wrong with it?

     Mechanic: Hard to say. Bring it in to the shop and I'll 
 take a look.
No. It's more that I did say: The board computer message is weird, it points to one of my tires but does not tell me the correct error. The tire seems to be wrecked. There is a scratch - but what could cause it? Someone with expierence (a mechanic) know this kind of error? I kindly ask. But mechanics do not listen and telling me I need to do an oil change and why it's important. -- If someone is interested: I think it's a bug that the compiler cannot handle this recursion correctly. By calling isCallable!S and parsing the structs "alias this" not the complete get() content need to be parsed as only the return type is relevant here - which is already known: T I have to arrange that the problematic code is not parsed when calling isCallable, so the only solution was a proxy method that switch to get() in runtime to satisfy the compiler, not very pretty, but works: struct foo(T) { alias proxy this; T delegate() p; // called from this(), this(this) void initProxy() { p = delegate() { return get; }; } T get() { // problematic code } T proxy() { return p(); } }
Jan 21