www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Prevention of UFCS hijacking

reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
I've run into a bit of an issue with UFCS today, take a look at the
reduced test-case:

-----
import std.conv;

class Label
{
    //  property string text() {  }
    //  property void text(string nText) {  }
}

void main()
{
    auto label = new Label;

    label.text = "My label";
    assert(label.text == "My label");  // fails at runtime
}
-----

Because of the UFCS feature std.conv.text is called even though I
expected compilation to fail since I've commented out the .text
properties in my class.

This is a problem since some of my classes might have a .text field,
while others might not. If the user imports std.conv he could easily
end up calling std.conv.text by mistake when a class object doesn't
implement such a property.

Anyway, there is a cure. I can work around this using  disabled fields
in the base class. For example:

-----
import std.conv;

class Widget
{
     disable string text;
}

class Label : Widget
{
     property string text() { return "My label"; }
     property void text(string nText) { }
}

class Image : Widget
{
}

void main()
{
    auto frame = new Label;
    frame.text = "My label";
    assert(frame.text == "My label");

    auto image = new Image;
    image.text = "foobar";  // ok, fails to compile
}

-----

Note that I've specifically made the base class declare a public
variable rather than a  property, otherwise the derived class would
have to override a virtual  property function.

Maybe this code might even become a case of accepts-invalid at some
point, since the properties do actually shadow the base class
variable. I'm not sure how long this workaround will work.

Has anyone else run into this sort of issue? I'm wondering whether we
should have some language support (through __traits,  disable, or
something else), to enable writing APIs which are safer to use, where
you can't by mistake call a UFCS function like that if there's an
equally named function introduced in another derived class.

---

Btw, why exactly is the following allowed to compile?:

-----
import std.conv;
import std.stdio;

struct S { }

void main()
{
    S s;
    writeln(s.text = "foo");
}
-----

This translates into std.conv.text(s, "foo"). I'm not a fan,
especially since 'text' is not a  property. But we've already reached
the consensus that all functions can be called like properties (for
some reason..), so I guess it's too late to change this.
Aug 27 2013
parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Tuesday, 27 August 2013 at 18:02:27 UTC, Andrej Mitrovic wrote:
 I've run into a bit of an issue with UFCS today, take a look at 
 the
 reduced test-case:

 -----
 import std.conv;

 class Label
 {
     //  property string text() {  }
     //  property void text(string nText) {  }
 }

 void main()
 {
     auto label = new Label;

     label.text = "My label";
     assert(label.text == "My label");  // fails at runtime
 }
 -----

 Because of the UFCS feature std.conv.text is called even though 
 I
 expected compilation to fail since I've commented out the .text
 properties in my class.

But commenting out "text" property is not order to compiler to ignore all UFCS involving "text". What commenting out has to do with UFCS?
 This is a problem since some of my classes might have a .text 
 field,
 while others might not. If the user imports std.conv he could 
 easily
 end up calling std.conv.text by mistake when a class object 
 doesn't
 implement such a property.

Then classes should implement member to disable UFCS hijacktion. If not, this is an allowance for such name hijacktion. This is by design.
 Has anyone else run into this sort of issue? I'm wondering 
 whether we
 should have some language support (through __traits,  disable, 
 or
 something else), to enable writing APIs which are safer to use, 
 where
 you can't by mistake call a UFCS function like that if there's 
 an
 equally named function introduced in another derived class.

disable is a way to block this feature
 ---

 Btw, why exactly is the following allowed to compile?:

 -----
 import std.conv;
 import std.stdio;

 struct S { }

 void main()
 {
     S s;
     writeln(s.text = "foo");
 }
 -----

 This translates into std.conv.text(s, "foo"). I'm not a fan,
 especially since 'text' is not a  property. But we've already 
 reached
 the consensus that all functions can be called like properties 
 (for
 some reason..), so I guess it's too late to change this.

Let's start from basics: writeln = 42; and this is also by design.
Aug 27 2013
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Aug 27, 2013 at 08:40:16PM +0200, Maxim Fomin wrote:
[...]
 Let's start from basics:
 
 writeln = 42;
 
 and this is also by design.

If that can be called a design at all, it's a horrible design. It should be illegal, if it isn't already. It makes assignment syntax meaningless, and is useful only for obfuscated code contests. T -- Why have vacation when you can work?? -- EC
Aug 27 2013
parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Tuesday, 27 August 2013 at 19:00:43 UTC, H. S. Teoh wrote:
 On Tue, Aug 27, 2013 at 08:40:16PM +0200, Maxim Fomin wrote:
 [...]
 Let's start from basics:
 
 writeln = 42;
 
 and this is also by design.

If that can be called a design at all, it's a horrible design. It should be illegal, if it isn't already. It makes assignment syntax meaningless, and is useful only for obfuscated code contests. T

It wasn't me who invented this :)
Aug 27 2013
prev sibling next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 8/27/13, Maxim Fomin <maxim maxim-fomin.ru> wrote:
 Let's start from basics

You've just repeated everything I've already said.
Aug 27 2013
parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Tuesday, 27 August 2013 at 19:39:34 UTC, Andrej Mitrovic wrote:
 On 8/27/13, Maxim Fomin <maxim maxim-fomin.ru> wrote:
 Let's start from basics

You've just repeated everything I've already said.

What should I tell except you are surprising of current state of things?
Aug 27 2013
parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 8/27/13, Maxim Fomin <maxim maxim-fomin.ru> wrote:
 What should I tell except you are surprising of current state of
 things?

I'm just raising awareness of the issue. And it's surprising when you're coding and not expecting it. Imagine adding an import to a 3rd party library and no longer getting errors at compile time when you assign to a field that doesn't exist. Anyone could be caught off-guard by this.
Aug 28 2013
prev sibling next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 8/27/13, H. S. Teoh <hsteoh quickfur.ath.cx> wrote:
 and this is also by design.

If that can be called a design at all, it's a horrible design.

I would call it an accidental implementation detail at best! :)
Aug 27 2013
parent "Jesse Phillips" <Jesse.K.Phillips+D gmail.com> writes:
On Tuesday, 27 August 2013 at 19:41:08 UTC, Andrej Mitrovic wrote:
 On 8/27/13, H. S. Teoh <hsteoh quickfur.ath.cx> wrote:
 and this is also by design.

If that can be called a design at all, it's a horrible design.

I would call it an accidental implementation detail at best! :)

It was by design, then property was added and now everyone is confused why it exists. Pretty sure the consensus was to require property for assignment, but parens were to be optional. At least it seemed to have the least disagreement.
Aug 27 2013
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Aug 27, 2013 at 09:40:54PM +0200, Andrej Mitrovic wrote:
 On 8/27/13, H. S. Teoh <hsteoh quickfur.ath.cx> wrote:
 and this is also by design.

If that can be called a design at all, it's a horrible design.

I would call it an accidental implementation detail at best! :)

Let's call it an accidental feature. -- Larry Wall :) That doesn't make it any less ugly, though. I still say we should make func=y syntax illegal for non- property functions. T -- Claiming that your operating system is the best in the world because more people use it is like saying McDonalds makes the best food in the world. -- Carl B. Constantine
Aug 27 2013
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-08-27 20:40, Maxim Fomin wrote:

 Let's start from basics:

 writeln = 42;

 and this is also by design.

I see no reasons for why this should be allowed. I say: * Getters marked with property: call without parentheses * Getters not marked with property: call with or without parentheses * Setters marked with property: require assignment syntax * Setters not marked with property: disallow assignment syntax -- /Jacob Carlborg
Aug 27 2013
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Aug 28, 2013 at 08:41:39AM +0200, Jacob Carlborg wrote:
 On 2013-08-27 20:40, Maxim Fomin wrote:
 
Let's start from basics:

writeln = 42;

and this is also by design.

I see no reasons for why this should be allowed. I say: * Getters marked with property: call without parentheses * Getters not marked with property: call with or without parentheses * Setters marked with property: require assignment syntax * Setters not marked with property: disallow assignment syntax

[...] +1, I agree with all of the above. T -- Старый друг лучше новых двух.
Aug 28 2013