class c{
}
c c;
c = new c;
Jarrett mentioned that it doesn't compile in bug 3125.
This can alleviate the pain of porting C# code.
I patched this in my own box when I tried to port some C# code.
Comment #1 by jarrett.billingsley — 2009-07-03T08:42:11Z
No. This is working as intended. What you're doing here is shadowing the global 'c' with a local 'c'. Symbol lookup in D is simple: it looks in enclosing scopes until it finds a symbol of the given name, no matter how you're using that name. If you're porting code from another language, you're going to have to expect some translation work. And besides, what's so difficult about "c c = new c;", or better yet, _not doing it in the first place_?
Comment #2 by davidl — 2009-07-03T16:33:30Z
(In reply to comment #1)
> No. This is working as intended. What you're doing here is shadowing the
> global 'c' with a local 'c'. Symbol lookup in D is simple: it looks in
> enclosing scopes until it finds a symbol of the given name, no matter how
> you're using that name. If you're porting code from another language, you're
> going to have to expect some translation work. And besides, what's so
> difficult about "c c = new c;", or better yet, _not doing it in the first
> place_?
c c = new c; <-- this is the only 1 case that trigger this issue.
Sometimes you can have
class ClassType1
{
}
class ClassType2
{
ClassType1 ClassType1;
void func(ClassType1 t){} // this ClassType1 is clearly referring to the type of "ClassType1"
}
And it's not hard to make this work. I see no reason to ignore this one.
My own dmd gets several other features patched. So it's somewhat troublesome to isolate the patch to this particular bug.
The idea is pretty simple, when you try to tell the user the error, firstly try to resolve the type upper scope
A roughly patch:
Type *TypeIdentifier::semantic(Loc loc, Scope *sc)
{
Type *t;
Expression *e;
Dsymbol *s;
+++ bool tried = false;
+++ Scope *sce = sc -> enclosing;
//printf("TypeIdentifier::semantic(%s)\n", toChars());
resolve(loc, sc, &e, &t, &s);
+++ L1:
if (t)
{
//printf("\tit's a type %d, %s, %s\n", t->ty, t->toChars(), t->deco);
if (t->ty == Ttypedef)
{ TypeTypedef *tt = (TypeTypedef *)t;
if (tt->sym->sem == 1)
error(loc, "circular reference of typedef %s", tt->toChars());
}
t = t->addMod(mod);
}
else
{
+++ if ( sce != NULL)
+++ {
+++ resolve(loc, sce, &e, &t, &s);
+++ sce = sce -> enclosing;
+++ goto L1;
+++ }
#ifdef DEBUG
if (!global.gag)
printf("1: ");
#endif
if (s)
{
s->error(loc, "is used as a type");
//halt();
}
else
error(loc, "%s is used as a type", toChars());
t = tvoid;
}
//t->print();
return t;
}
You see, it's simple. I think it should be enhanced.
Comment #3 by davidl — 2009-07-03T16:43:39Z
Umm, this time I review the patch, that bool tried is some legacy way to solve the issue. It didn't work correctly if it requires to resolve the symbol in upper-upper scope. I changed it to the current one, but the bool tried left forgotten.
I won't try to hack the compiler, if hacking it is relatively simple, the issue is hard to bypass. The original C# code uses duplication name of var and type everywhere.
Comment #4 by jarrett.billingsley — 2009-07-03T16:59:11Z
(In reply to comment #2)
> Sometimes you can have
>
> class ClassType1
> {
> }
>
> class ClassType2
> {
> ClassType1 ClassType1;
> void func(ClassType1 t){} // this ClassType1 is clearly referring to the type
> of "ClassType1"
> }
>
> And it's not hard to make this work. I see no reason to ignore this one.
I do. It's stupid and pointless. Just name your local variable _something else_.
> You see, it's simple. I think it should be enhanced.
Just because it's simple doesn't mean it should be done.
Comment #5 by ddparnell — 2009-07-03T20:33:36Z
Regardless of whether it is a good idea or not, I see a related problem in that there is a bit of apparent inconsistency.
Look at this program ...
// ------------
struct X
{
}
struct Y
{
X X; // Disallowed
}
void main()
{
X X; // Allowed.
}
// ------------
Either both instances should be disallowed or both allowed, IMO.
Comment #6 by jarrett.billingsley — 2009-07-03T21:43:33Z
(In reply to comment #5)
> Regardless of whether it is a good idea or not, I see a related problem in that
> there is a bit of apparent inconsistency.
>
> Look at this program ...
>
> // ------------
> struct X
> {
> }
> struct Y
> {
> X X; // Disallowed
> }
> void main()
> {
> X X; // Allowed.
> }
> // ------------
>
> Either both instances should be disallowed or both allowed, IMO.
I would say they should both be allowed. The declaration of the struct member should happen semantically after the type lookup, just like with the variable.
Comment #7 by andrej.mitrovich — 2012-12-21T10:46:48Z
(In reply to comment #6)
> I would say they should both be allowed. The declaration of the struct member
> should happen semantically after the type lookup, just like with the variable.
This kind of code is just asking for trouble.
Comment #8 by robert.schadek — 2024-12-13T17:50:28Z