Bug 3231 – Function declared to return a type with its same name doesn't compile

Status
RESOLVED
Resolution
INVALID
Severity
blocker
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2009-08-06T16:36:00Z
Last change time
2015-06-09T01:26:47Z
Keywords
rejects-valid, spec
Assigned to
nobody
Creator
tim.matthews7

Comments

Comment #0 by tim.matthews7 — 2009-08-06T16:36:19Z
class Bar { } class Foo { Bar Bar(); //declare a func without the implementation } void main() { }
Comment #1 by tim.matthews7 — 2009-08-06T16:37:53Z
The error reported: test.d(11): Error: function test.Foo.Bar is used as a type
Comment #2 by tim.matthews7 — 2009-08-06T16:52:53Z
Sorry I shouldn't have rushed this but it if I provide the implementation it makes no difference. Are we meant to be allowed a function with the same name as its return type or is this a bug? class Bar { } class Foo { Bar Bar() { return null; } } void main() { }
Comment #3 by jarrett.billingsley — 2009-08-06T17:22:42Z
Name lookup in D is simple: it starts with the scope in which the name is used, and moves outward until it finds a symbol of that name, regardless of how the symbol is used. The function 'Bar' is inserted into Foo's namespace before its return type is resolved. When the return type is resolved, it finds the function Bar() itself instead of the class Bar at module scope, and you get an error. Marking this as invalid unless it can be proven otherwise.
Comment #4 by shro8822 — 2009-08-06T17:53:44Z
I also think this is invalid. As a work around: class Bar { } class Foo { .Bar Bar() // note '.' to start from global scope { return null; } } void main() { }
Comment #5 by cristian — 2009-08-06T21:43:34Z
The bug was filed as a blocker for the .net implementation because there are cases like this System.Text.RegularExpressions that has a static Match Match(System.String, System.String) method. I think that marking the bug as invalid because of a limitation in the implementation is ridiculous. The fix (which I am applying to the my source tree at http://dnet.codeplex.com/) is very simple: continue the look up in enclosing scope.
Comment #6 by jarrett.billingsley — 2009-08-06T22:22:05Z
(In reply to comment #5) > The bug was filed as a blocker for the .net implementation because there are > cases like this System.Text.RegularExpressions that has a static Match > Match(System.String, System.String) method. > > I think that marking the bug as invalid because of a limitation in the > implementation is ridiculous. It's not a limitation in the implementation, it's how the language is defined. Java - and by proxy, C#, since MS doesn't seem to have strayed too far from their "inspiration" - allows for multiple symbols of the same name to be distinguished based on how they're used. D does not. If you have a disagreement, it's with the spec, not the implementation.
Comment #7 by shro8822 — 2009-08-07T09:58:05Z
(In reply to comment #5) > The bug was filed as a blocker for the .net implementation because there are > cases like this System.Text.RegularExpressions that has a static Match > Match(System.String, System.String) method. > > I think that marking the bug as invalid because of a limitation in the > implementation is ridiculous. > > The fix (which I am applying to the my source tree at > http://dnet.codeplex.com/) is very simple: continue the look up in enclosing > scope. Short of cases like this (illegal in both C# and D): class Foo { class Bar { } Bar Bar() { return new Bar(); } } There is no need to alter the compiler, just require fully qualified names. Heck, for imports of other CLR code via meta-data, that isn't even needed as IIRC assemblies alway use fully qualified names internally.
Comment #8 by cristian — 2009-08-07T21:28:56Z
@BCS That case is indeed illegal, AND handled correctly by my fix. You can try it out either by downloading the dnet compiler, or by applying the change to DMD, here's the full code: Type *TypeIdentifier::semantic(Loc loc, Scope *sc) { Type *t; Expression *e; Dsymbol *s; //printf("TypeIdentifier::semantic(%s)\n", toChars()); resolve(loc, sc, &e, &t, &s); 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 { #ifdef DEBUG if (!global.gag) printf("1: "); #endif if (s) { #if TARGET_NET //http://d.puremagic.com/issues/show_bug.cgi?id=3231 if (sc->enclosing) return semantic(loc, sc->enclosing); #endif 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; }
Comment #9 by smjg — 2009-08-08T12:37:01Z
(In reply to comment #5) > I think that marking the bug as invalid because of a limitation in the > implementation is ridiculous. AIUI this isn't a limitation in the implementation, but a characteristic of how D is designed. > The fix (which I am applying to the my source tree at > http://dnet.codeplex.com/) is very simple: continue the look up in enclosing > scope. Better beware of hijacking vulnerabilities.
Comment #10 by cristian — 2009-08-08T13:25:53Z
(In reply to comment #9) > (In reply to comment #5) > > I think that marking the bug as invalid because of a limitation in the > > implementation is ridiculous. > > AIUI this isn't a limitation in the implementation, but a characteristic of how > D is designed. > > > The fix (which I am applying to the my source tree at > > http://dnet.codeplex.com/) is very simple: continue the look up in enclosing > > scope. > > Better beware of hijacking vulnerabilities. This is possibly valid, do you have an example that drives your point?
Comment #11 by smjg — 2009-08-08T16:07:09Z
(In reply to comment #10) > (In reply to comment #9) > > Better beware of hijacking vulnerabilities. > > This is possibly valid, do you have an example that drives your point? I'm not sure if there are any in this particular instance, but it's something to be careful of whenever you try to resolve a symbol to a different scope depending on how it's being used. It may be the case that the worst that can happen here is that the class-level function conflicts with an opCall on the module-level class. While no call of the form Bar(...) is happening here, I can see it getting confusing if one does occur....
Comment #12 by tim.matthews7 — 2009-08-08T16:42:01Z
Adding the dot is so trivial and takes no time at all. I do believe that this is indeed a bug anyway explanation of such: This code will not compile: class A { void func(); func getFunc() { return null; } } void main() { } You cannot return a 'func' as it is not a type. 'func' is actually an instance of 'delegate' so it should have been declared as that. class A { void func(); void getFunc() { new func(); } } void main() { } Here 'getFunc' is declared as void but it doesn't compile either because this time I am trying to new a 'func' which causes a compile error again because 'func' is not a type but an instance of delegate. Whenever a peice of D code contains 2 identifiers next to each other like so: AB XY It can only mean either XY is an instance (whether that be value, ref, of function returning) and AB is a type. Likewise I can't 'new' everything either. The bug is simply that dmdfe wasn't attempting to find a type or instance with the particular name but it was satisfied with the first symbol it found. If this indeed hard to fixup now (and I would rather Walter focus on more important issues) then adding a couple of dots is something I wouldn't mind to do either (it would require a few bytes of code)
Comment #13 by smjg — 2009-08-09T09:38:26Z
I think comment 3 pretty much covers it, but is this stated in the spec? FTR here are two closely related examples that fail: ----- overload_scope_1.d ----- void func(int i) {} class C { static { void func() {} void func(string s) {} void test() { func(); func(42); // line 9 func("test"); } } } ---------- overload_scope_1.d(9): Error: function overload_scope_1.C.func () does not match parameter types (int) overload_scope_1.d(9): Error: cannot implicitly convert expression (42) of type int to immutable(char)[] overload_scope_1.d(9): Error: cannot cast int to immutable(char)[] ----- overload_scope_2.d ----- import overload_scope_2a; import overload_scope_2b; Qwert yuiop; ----- overload_scope_2a.d ----- alias string Qwert; ----- overload_scope_2b.d ----- void Qwert() {} ---------- overload_scope_2.d(4): Error: overload_scope_2a.Qwert at overload_scope_2a.d(1) conflicts with overload_scope_2b.Qwert at overload_scope_2b.d(1) ---------- (DMD 2.031 Win; messages essentially the same in DMD 1.046.) These cases show that it's down to two things: - overload sets apply only between imported modules, not between scoping levels - in any case, they apply only between function overloads, not between arbitrarily mixed entity types
Comment #14 by shro8822 — 2009-08-09T11:59:46Z
(In reply to comment #8) > @BCS > That case is indeed illegal, AND handled correctly by my fix. > Given that you are willing to redefine the language I have to ask; What is correctly? Assuming you are allowing it: Even if you can make D handle the case, unless you can point at another .NET language (one that MS supports) that supports having both a function and a type with the same name in the same scope, I think it would be a bad idea to let D.NET do this. What happens when you use such a type from c#? What does it give you when you use F12 on the reused name? Adding this requires changing the specification of the D language and maybe even the whole .NET system. I would be uncomfortable with any change at all and would I wouldn't actively oppose a change that allows for both a member and a type in a class to have the same name. Assuming you are disallowing it: You are adding nothing to the language. Using fully qualified names (or .identifier) gets the same end effect.
Comment #15 by shro8822 — 2009-08-09T12:02:15Z
(In reply to comment #9) > > Better beware of hijacking vulnerabilities. class Foo {} class Bar { int Foo(){} // this would become a conflict with only an arbitrary resolution. alias Foo Baz; }