Jump to page: 1 2 3
Thread overview
Cannot compare object.opEquals is not nogc
Jul 23, 2016
Rufus Smith
Jul 23, 2016
Lodovico Giaretta
Jul 23, 2016
Rufus Smith
Jul 23, 2016
Lodovico Giaretta
Jul 23, 2016
Jonathan Marler
Jul 23, 2016
Jonathan Marler
Jul 23, 2016
Lodovico Giaretta
Jul 23, 2016
Rufus Smith
Jul 23, 2016
Lodovico Giaretta
Jul 23, 2016
Lodovico Giaretta
Jul 24, 2016
Rufus Smith
Jul 24, 2016
Jonathan Marler
Jul 24, 2016
Lodovico Giaretta
Jul 24, 2016
Jonathan Marler
Jul 24, 2016
Lodovico Giaretta
Jul 24, 2016
Jonathan Marler
Jul 24, 2016
Lodovico Giaretta
Jul 24, 2016
Jonathan Marler
Jul 24, 2016
lqjglkqjsg
Jul 24, 2016
Lodovico Giaretta
Jul 31, 2016
Rufus Smith
Jul 23, 2016
Jonathan M Davis
Jul 23, 2016
Marco Leise
Jul 23, 2016
Rufus Smith
Jul 23, 2016
Jonathan M Davis
Jul 31, 2016
Rufus Smith
Jul 31, 2016
ag0aep6g
Aug 02, 2016
Mark "J" Twain
July 23, 2016
Trying to compare a *ptr value with a value in nogc code results in the error:

Error: @nogc function '...' cannot call non-@nogc function 'object.opEquals'		

Shouldn't object opEquals be marked?
July 23, 2016
On Saturday, 23 July 2016 at 13:18:03 UTC, Rufus Smith wrote:
> Trying to compare a *ptr value with a value in nogc code results in the error:
>
> Error: @nogc function '...' cannot call non-@nogc function 'object.opEquals'		
>
> Shouldn't object opEquals be marked?

If object.opEquals is marked @nogc, than all D classes must implement it as @nogc, because (of course) you cannot override a @nogc method with a not-@nogc one (while the opposite is possible, of course).
So marking it @nogc is not only a big breaking change, but also very limiting.
July 23, 2016
On Saturday, 23 July 2016 at 14:15:03 UTC, Lodovico Giaretta wrote:
> On Saturday, 23 July 2016 at 13:18:03 UTC, Rufus Smith wrote:
>> Trying to compare a *ptr value with a value in nogc code results in the error:
>>
>> Error: @nogc function '...' cannot call non-@nogc function 'object.opEquals'		
>>
>> Shouldn't object opEquals be marked?
>
> If object.opEquals is marked @nogc, than all D classes must implement it as @nogc, because (of course) you cannot override a @nogc method with a not-@nogc one (while the opposite is possible, of course).
> So marking it @nogc is not only a big breaking change, but also very limiting.

Um, this isn't right. GC code can always call non-gc code.

If you mark opEquals nogc, it breaks nothing except implementations of opEquals that use the GC. GC code can still call it nogc opequals, it only enforces opEquals code to avoid the GC itself, which isn't a terrible thing.

What is terrible is that nogc code can never have any equality comparisons! It is impossible unless one manually tests them, but how? Every method would be brittle. Do a memory test? compare element by element? One can't predict what to do.

So, you are trying off laziness to break nogc. As it stands, if nogc code can't compare equality, it is broken and useless. Why put it in the language then?

struct S(T)
{
   @nogc void test(T t1, T t2)
   {
      if (t1 == t2) return true;
      return false;
   }
}

Broke! Even if opEquals of T does not use any GC we can't write test to be nogc, which means we can't have S be nogc or anything that depends on S that is nogc. This must be a dirty trick played by the implementors of nogc to keep everyone on the gc nipple?

Equality comparison is more fundamental than the GC in programming. There is no fundamental reason why equality comparison depends on the GC.

All I can hope for now is that there is a robust way to compare that I can use that is marked nogc. Else all my nogc code is broke. I guess one has this problem with all opOp's!





July 23, 2016
On Saturday, 23 July 2016 at 14:53:49 UTC, Rufus Smith wrote:
> Um, this isn't right. GC code can always call non-gc code.
>
> If you mark opEquals nogc, it breaks nothing except implementations of opEquals that use the GC. GC code can still call it nogc opequals, it only enforces opEquals code to avoid the GC itself, which isn't a terrible thing.

That's what I meant. Sorry for being not clear. IMHO, it is very limiting to forbid the use of the GC in opEquals. And it would definitely break a lot of code.

> What is terrible is that nogc code can never have any equality comparisons! It is impossible unless one manually tests them, but how? Every method would be brittle. Do a memory test? compare element by element? One can't predict what to do.

@nogc code can have @nogc comparisons, as long as it does not use other objects whose comparison requires the GC. See more below.

> So, you are trying off laziness to break nogc. As it stands, if nogc code can't compare equality, it is broken and useless. Why put it in the language then?
>
> struct S(T)
> {
>    @nogc void test(T t1, T t2)
>    {
>       if (t1 == t2) return true;
>       return false;
>    }
> }
>
> Broke! Even if opEquals of T does not use any GC we can't write test to be nogc, which means we can't have S be nogc or anything that depends on S that is nogc. This must be a dirty trick played by the implementors of nogc to keep everyone on the gc nipple?

If opEquals of T does not use the GC, then the compiler will infer the attribute @nogc for your test function. Remember: templated functions will be inferred @nogc whenever possible. Just, don't put @nogc explicitly, so the code will be @nogc if T.opEquals is @nogc and otherwise it will be not-@nogc.

> Equality comparison is more fundamental than the GC in programming. There is no fundamental reason why equality comparison depends on the GC.
>
> All I can hope for now is that there is a robust way to compare that I can use that is marked nogc. Else all my nogc code is broke. I guess one has this problem with all opOp's!

With templates (as you show in your code) you have no problem, just let the compiler infer the attributes, as I said.
With runtime OOP it is more of a trouble, but in D runtime OOP is not used that much, and it is rarely used in critical @nogc code.
July 23, 2016
On 7/23/16 10:53 AM, Rufus Smith wrote:
> On Saturday, 23 July 2016 at 14:15:03 UTC, Lodovico Giaretta wrote:
>> On Saturday, 23 July 2016 at 13:18:03 UTC, Rufus Smith wrote:
>>> Trying to compare a *ptr value with a value in nogc code results in
>>> the error:
>>>
>>> Error: @nogc function '...' cannot call non-@nogc function
>>> 'object.opEquals'
>>>
>>> Shouldn't object opEquals be marked?
>>
>> If object.opEquals is marked @nogc, than all D classes must implement
>> it as @nogc, because (of course) you cannot override a @nogc method
>> with a not-@nogc one (while the opposite is possible, of course).
>> So marking it @nogc is not only a big breaking change, but also very
>> limiting.
>
> Um, this isn't right. GC code can always call non-gc code.

The issue is that for *classes*, the proper way to add an opEquals is to override the base version. The base version existed LONG before @nogc did, and so it's not appropriately marked.

Not only that, but @nogc is too limiting (if I want to use GC in opEquals, I should be able to).

The real problem here is that there is a base method at all. We have been striving to remove it at some point, but it is very difficult due to all the legacy code which is written.

Almost all the Object base methods need to be removed IMO. You can add them at a higher level if you need them, and then specify your requirements for derived classes.

Including opHash, opCmp, toString, etc.

> If you mark opEquals nogc, it breaks nothing except implementations of
> opEquals that use the GC. GC code can still call it nogc opequals, it
> only enforces opEquals code to avoid the GC itself, which isn't a
> terrible thing.

It breaks all classes which use GC in opEquals. Note that you can't really compare two immutable or const objects either! (well, actually you can, but that's because the runtime just casts away const and lets you have undefined behavior).

> What is terrible is that nogc code can never have any equality
> comparisons! It is impossible unless one manually tests them, but how?
> Every method would be brittle. Do a memory test? compare element by
> element? One can't predict what to do.

It is unfortunate. I hope we can fix it. I'd rather not add another exception like we have for comparing const objects.

> So, you are trying off laziness to break nogc. As it stands, if nogc
> code can't compare equality, it is broken and useless. Why put it in the
> language then?

@nogc is not useless, it just cannot handle Objects at the moment.

> Broke! Even if opEquals of T does not use any GC we can't write test to
> be nogc, which means we can't have S be nogc or anything that depends on
> S that is nogc. This must be a dirty trick played by the implementors of
> nogc to keep everyone on the gc nipple?

I assure you, it's not a trick. It's legacy. It needs fixing, but the fix isn't painless or easy.

-Steve
July 23, 2016
On Saturday, 23 July 2016 at 15:25:02 UTC, Steven Schveighoffer wrote:
> On 7/23/16 10:53 AM, Rufus Smith wrote:
>> On Saturday, 23 July 2016 at 14:15:03 UTC, Lodovico Giaretta wrote:
>>> On Saturday, 23 July 2016 at 13:18:03 UTC, Rufus Smith wrote:
>>>> Trying to compare a *ptr value with a value in nogc code results in
>>>> the error:
>>>>
>>>> Error: @nogc function '...' cannot call non-@nogc function
>>>> 'object.opEquals'
>>>>
>>>> Shouldn't object opEquals be marked?
>>>
>>> If object.opEquals is marked @nogc, than all D classes must implement
>>> it as @nogc, because (of course) you cannot override a @nogc method
>>> with a not-@nogc one (while the opposite is possible, of course).
>>> So marking it @nogc is not only a big breaking change, but also very
>>> limiting.
>>
>> Um, this isn't right. GC code can always call non-gc code.
>
> The issue is that for *classes*, the proper way to add an opEquals is to override the base version. The base version existed LONG before @nogc did, and so it's not appropriately marked.
>
> Not only that, but @nogc is too limiting (if I want to use GC in opEquals, I should be able to).
>
> The real problem here is that there is a base method at all. We have been striving to remove it at some point, but it is very difficult due to all the legacy code which is written.
>
> Almost all the Object base methods need to be removed IMO. You can add them at a higher level if you need them, and then specify your requirements for derived classes.
>
> Including opHash, opCmp, toString, etc.
>
>> If you mark opEquals nogc, it breaks nothing except implementations of
>> opEquals that use the GC. GC code can still call it nogc opequals, it
>> only enforces opEquals code to avoid the GC itself, which isn't a
>> terrible thing.
>
> It breaks all classes which use GC in opEquals. Note that you can't really compare two immutable or const objects either! (well, actually you can, but that's because the runtime just casts away const and lets you have undefined behavior).
>
>> What is terrible is that nogc code can never have any equality
>> comparisons! It is impossible unless one manually tests them, but how?
>> Every method would be brittle. Do a memory test? compare element by
>> element? One can't predict what to do.
>
> It is unfortunate. I hope we can fix it. I'd rather not add another exception like we have for comparing const objects.
>
>> So, you are trying off laziness to break nogc. As it stands, if nogc
>> code can't compare equality, it is broken and useless. Why put it in the
>> language then?
>
> @nogc is not useless, it just cannot handle Objects at the moment.
>
>> Broke! Even if opEquals of T does not use any GC we can't write test to
>> be nogc, which means we can't have S be nogc or anything that depends on
>> S that is nogc. This must be a dirty trick played by the implementors of
>> nogc to keep everyone on the gc nipple?
>
> I assure you, it's not a trick. It's legacy. It needs fixing, but the fix isn't painless or easy.
>
> -Steve

I've seen this type of problem many times before when using the @nogc attribute.  With alot of work, and breaking changes, you could fix it in the case of opEquals, and in the end you still end up with the restriction that you can't use the GC in opEquals, which may be a good thing, but some would disagree.  But this is a common problem and I think a more exhaustive solution would be to allow @nogc code to call that that is either marked as nogc, or inferred to be @nogc.  Instead of treating @nogc as a special compiler directive to check for GC code, the compiler could check for GC code in all cases, and infer the attribute for all functions.  Then @nogc would simply be a way for the developer to tell the compiler to make sure they aren't using @nogc where they don't intend to.  This allows code that is written without @nogc to be called from code that does use it.  It takes an effort away from the developer and moves it to the compiler.  It allows @nogc to work with existing code.

Maybe this would result in a big performance hit on the compiler because now it would always have to check for GC code, instead of just when it's specified with @nogc...not sure.

Anyway, this is just a general overview.  There's obviously alot of details and specifics that were glossed over but I think the general idea could be a good solution.
July 23, 2016
On Saturday, 23 July 2016 at 16:46:20 UTC, Jonathan Marler wrote:
> [...]

Actually Im going to disagree with myself. This technique actually wouldn't work with virtual methods:)
July 23, 2016
Am Sat, 23 Jul 2016 13:18:03 +0000
schrieb Rufus Smith <RufusSmith@indi.com>:

> Trying to compare a *ptr value with a value in nogc code results in the error:
> 
> Error: @nogc function '...' cannot call non-@nogc function
> 'object.opEquals'
> 
> Shouldn't object opEquals be marked?

The only situation that you can work around is if the 'object' is actually known to be one of your @nogc objects. Then you can simply downcast before calling opEquals.

It's certainly limiting, but just an artifact of OOP and I can assure you that making D usable without GC has been high on the priority list (for an community driven open-source project anyways). Phobos got reworked to get rid of unnecessary GC allocations and Andrei Alexandrescu contemplated making object's methods @nogc at one point.

If you need a restricted object hierarchy, you'll have to write a new "NoGcObject" base class. opEquals would still take "object", though you can avoid a dynamic cast by writing:

  (cast(NoGcObject)cast(void*)obj).someMethod();

The cast to void* prior to the downcast drops the class type information and a dynamic cast becomes a static cast.

-- 
Marco

July 23, 2016
On Saturday, 23 July 2016 at 17:04:42 UTC, Jonathan Marler wrote:
> On Saturday, 23 July 2016 at 16:46:20 UTC, Jonathan Marler wrote:
>> [...]
>
> Actually Im going to disagree with myself. This technique actually wouldn't work with virtual methods:)

I don't think we have the big problems with @nogc that people points out.

I mean, we cannot decide that specific methods or opXXX must always be @nogc. That's too restrictive.

So, what we need to do is:

- use templates: with them, we can have our algorithms be @safe when applied to @safe types, @nogc when applied to @nogc types, and so on; for example, instead of taking a specific delegate type, we shall always accept a generic type, and use traits to guarantee it is some delegate; in this way, we can accept @safe delegate and propagate @safety to our algorithm, or accept @system and have our algorithm usable in @system code; same with @nogc et al.

- when we use virtual methods, we are giving up all compiler-checked attributes; in this situation we have two options:
    - we trust what we are doing: e.g. we cannot mark a thing @nogc, but we know it is and the profiler confirms that no allocation happens, so we are happy; our aim is having code that doesn't freeze because of collections, and not marking code @nogc.
    - we must have @nogc (or @whatever): then we know we cannot use certain classes, because they are definitely @nogc; so we cast the objects we get to the classes/interfaces that we know are @nogc (and are marked as such), and then our code is @nogc; as you see, you don't need Object to have @nogc methods; you only need the specific classes you use have it; if you want to work on generic objects, and as such cannot do specific casts, then you should definitely be using templates.

The only real problems I found till now are:
- some things in Phobos that shall be @nogc are not; they shall be refactored to have that attribute (but this is not always easy and requires time)
- the interface IAllocator is mostly used with @nogc allocators, but cannot have the said attribute (as I explained above, it must not restrict the possibility of allocators); it shall have a sub-interface NoGCAllocator, so that code can accept a @nogc allocator parameter without using templates (of course templates are fine, and are what we use now, but if everyone uses templates, why have IAllocator in the first place? IMHO we must have or both or none).
July 23, 2016
On Saturday, 23 July 2016 at 17:23:37 UTC, Marco Leise wrote:
> Am Sat, 23 Jul 2016 13:18:03 +0000
> schrieb Rufus Smith <RufusSmith@indi.com>:
>
>> Trying to compare a *ptr value with a value in nogc code results in the error:
>> 
>> Error: @nogc function '...' cannot call non-@nogc function
>> 'object.opEquals'
>> 
>> Shouldn't object opEquals be marked?
>
> The only situation that you can work around is if the 'object' is actually known to be one of your @nogc objects. Then you can simply downcast before calling opEquals.
>
> It's certainly limiting, but just an artifact of OOP and I can assure you that making D usable without GC has been high on the priority list (for an community driven open-source project anyways). Phobos got reworked to get rid of unnecessary GC allocations and Andrei Alexandrescu contemplated making object's methods @nogc at one point.
>
> If you need a restricted object hierarchy, you'll have to write a new "NoGcObject" base class. opEquals would still take "object", though you can avoid a dynamic cast by writing:
>
>   (cast(NoGcObject)cast(void*)obj).someMethod();
>
> The cast to void* prior to the downcast drops the class type information and a dynamic cast becomes a static cast.

I am trying to write some general code that works on arbitrary types. I need to compare, obviously, as that is relatively basic thing on objects.

About the only half-ass solution I can think of is to use introspection and require the type to support a nogc version of Equals. Of course, this only passes the buck.

The problem is, D was designed with GC in mind, which was flawed from the get go and now trying to undo the tangled mess leads to "We can't do that because it's not backwards compatible".  So one crack leads to another to another. From what I gather, this isn't just a problem with nogc but many "after the fact enhancements" that don't work the way they should because of lack of foresight on the designers of D.







« First   ‹ Prev
1 2 3