Bug 320713 (MONO77963) - [GMCS] NRE on class X : Y<X.Z> - derived class of a generic type whose generic argument is its nested child class
Summary: [GMCS] NRE on class X : Y<X.Z> - derived class of a generic type whose generi...
Status: RESOLVED FIXED
Alias: MONO77963
Product: Mono: Compilers
Classification: Mono
Component: C# (show other bugs)
Version: 1.1
Hardware: Other Other
: P3 - Medium : Major
Target Milestone: ---
Assignee: Raja R Harinath
QA Contact: Mono Bugs
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2006-03-30 22:11 UTC by Atsushi Enomoto
Modified: 2007-09-15 21:24 UTC (History)
1 user (show)

See Also:
Found By: ---
Services Priority:
Business Priority:
Blocker: ---
Marketing QA Status: ---
IT Deployment: ---


Attachments
Workaround patch (I'm not attaching it for mcs hackers but those who really need to avoid this problem) (671 bytes, patch)
2006-03-31 03:16 UTC, Thomas Wiest
Details | Diff
Fix for the case of nested type inside generic type (516 bytes, patch)
2006-09-02 06:22 UTC, Thomas Wiest
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Thomas Wiest 2007-09-15 19:53:34 UTC


---- Reported by atsushi@ximian.com 2006-03-30 15:11:58 MST ----

The example code below causes NullReferenceException due to incomplete type
resolution. Bar is derived from a generic class whose generic argument is
the nested child class of Bar (i.e. Bar.Baz).

public class Foo<K>
{
}

public class Bar : Foo<Bar.Baz>
{
        public class Baz
        {
        }
}


Actual Results:

Unhandled Exception: System.NullReferenceException: Object reference not
set to an instance of an object
in <0x00169> Mono.CSharp.TypeContainer:DoDefineMembers ()
in <0x0004a> Mono.CSharp.TypeContainer:DefineMembers ()
in <0x0023f> Mono.CSharp.RootContext:PopulateTypes ()
in <0x00e23> Mono.CSharp.Driver:MainDriver (System.String[] args)
in <0x00056> Mono.CSharp.Driver:Main (System.String[] args)

Expected Results:

no such error.

How often does this happen? 

consistently.

Additional Information:

This blocks a production-ready game SDK library which works with mono.
http://yanesdkdotnet.sourceforge.jp/ , src/draw/BitmapLoader.cs



---- Additional Comments From atsushi@ximian.com 2006-03-30 20:16:59 MST ----

Created an attachment (id=169516)
Workaround patch (I'm not attaching it for mcs hackers but those who really need to avoid this problem)




---- Additional Comments From atsushi@ximian.com 2006-06-21 02:21:21 MST ----

Now one of Ruby.NET (Gardens Point Ruby.NET Compiler) hits the same
problem
http://www.plas.fit.qut.edu.au/Ruby.NET/



---- Additional Comments From martin@ximian.com 2006-06-21 09:56:27 MST ----

Possible duplicate of #77396.



---- Additional Comments From miguel@ximian.com 2006-07-02 10:51:44 MST ----

Am upping the priority



---- Additional Comments From martin@ximian.com 2006-07-03 08:12:51 MST ----

Increasing the priority doesn't help here - look at the dependency graph.



---- Additional Comments From rharinath@novell.com 2006-08-02 10:45:01 MST ----

Should be fixed in SVN r63251.



---- Additional Comments From jan.oravec@6com.sk 2006-09-01 20:20:17 MST ----

The more generic variation of this still doesn't work (current SVN):

public class Foo<K>
{
}

public class Bar<Q> : Foo<Bar<Q>.Baz>
{
        public class Baz
        {
        }
}

a.cs(5,27): error CS0426: The nested type `Baz' does not exist in the
type `Bar<Q>'
Compilation failed: 1 error(s), 0 warnings


The similar example not involving cyclic dependency (Bar -> Foo<Baz>
-> Baz -> Bar) works fine:

public class Foo<K>
{
}

public class Bar<Q>
{
        public class Baz
        {
        }
}

public class Goo<Q> : Foo<Bar<Q>.Baz>
{
}

Closing the cycle again with:

public class Foo<K>
{
}

public class Bar<Q> : Goo<Q>
{
        public class Baz
        {
        }
}

public class Goo<Q> : Foo<Bar<Q>.Baz>
{
}

Fails again with:

c.cs(12,27): error CS0426: The nested type `Baz' does not exist in the
type `Bar<Q>'
Compilation failed: 1 error(s), 0 warnings




---- Additional Comments From jan.oravec@6com.sk 2006-09-01 23:20:35 MST ----

I think I have found where is the problem. We are resolving
Foo<Bar<Q>.Baz> in the context of Bar`1[Q] instead of just Bar`1.

If I understand it right, TypeExpression denotes Bar`1, while
ConstructedType denotes Bar`1[Q]. The code was not passing TypeBuilder
to the MemberLookup and then to the MemberLookup_FindMembers, thus
weren't able to find Baz there.

This is first time I were looking at the gmcs source. I do not know
the terminology, etc., but I think that ResolveAsTypeTerminal was
supposed to get the TypeExpression from the ConstructedType, which is
called at ResolveNamespaceOrType. Thus I overloaded
ResolveAsTypeTerminal in ConstructedType.

Please see the attached patch. I am not sure if I am right, it may be
horribly wrong :). I have tested it and it appears to work and produce
correct code.




---- Additional Comments From jan.oravec@6com.sk 2006-09-01 23:22:14 MST ----

Created an attachment (id=169517)
Fix for the case of nested type inside generic type




---- Additional Comments From jan.oravec@6com.sk 2006-09-02 21:01:11 MST ----

My patch still doesn't work when constraints are added to the Bar<Q>,
e.g.:

public class Foo<K>
{
}

public class Bar<Q> : Foo<Bar<Q>.Baz>
  where Q : System.IEquatable<Q>
{
        public class Baz
        {
        }
}

Unhandled Exception: System.NullReferenceException: Object reference
not set to an instance of an object
  at Mono.CSharp.Constraints.ResolveTypes (IResolveContext ec) [0x00000]
  at Mono.CSharp.TypeParameter.ResolveType (IResolveContext ec) [0x00000]
  at Mono.CSharp.TypeParameter.DefineType (IResolveContext ec,
System.Reflection.Emit.MethodBuilder builder,
System.Reflection.MethodInfo implementing, Boolean is_override) [0x00000]
  at Mono.CSharp.TypeParameter.DefineType (IResolveContext ec) [0x00000]
  at Mono.CSharp.TypeContainer.ResolveType () [0x00000]
  at Mono.CSharp.RootContext.PopulateTypes () [0x00000]
  at Mono.CSharp.Driver.MainDriver (System.String[] args) [0x00000]
  at Mono.CSharp.Driver.Main (System.String[] args) [0x00000]


Seems like ResolveTypes() was called before Resolve(). A quick hack
"if(!resolved) Resolve();" in the begin of ResolveTypes() appears to
work. When this is fixed the correct way, I would propose adding
assertions to such places.




---- Additional Comments From jan.oravec@6com.sk 2006-09-03 15:41:25 MST ----

I had found some time to look deeper inside the compiler code. I
realized this:

We are constructing type Bar`1[Q] from Bar`1 when Bar`1 is not yet
fully built, so we need either:
- drop requirement for constructing type Bar`1[Q] until we have Bar`1
fully built, or
- build Bar`1[Q] together with Bar`1 (does Reflexion handle this
automagically for us when we change Bar`1? looks like it doesn't)




---- Additional Comments From rharinath@novell.com 2006-10-18 18:29:12 MST ----

There are several hacks in the compiler because of this.  Fixing this
issue properly will require a fair amount of refactoring, that is
anyway planned for other reasons including code hygiene.

Otherwise, we need some more DropGenericArguments in strategic locations.



---- Additional Comments From martin@ximian.com 2007-03-20 18:51:49 MST ----

Fixed in SVN; added gtest-317.cs and gtest-318.cs.



---- Additional Comments From martin@ximian.com 2007-03-20 18:52:25 MST ----

*** https://bugzilla.novell.com/show_bug.cgi?id=MONO80314 has been marked as a duplicate of this bug. ***



---- Additional Comments From martin@ximian.com 2007-03-20 18:53:44 MST ----

*** https://bugzilla.novell.com/show_bug.cgi?id=MONO81019 has been marked as a duplicate of this bug. ***



---- Additional Comments From jan.oravec@6com.sk 2007-03-21 06:15:56 MST ----

Thanks!

This bug depended on bug(s) 77396 77403.
Imported an attachment (id=169516)
Imported an attachment (id=169517)

Unknown operating system unknown. Setting to default OS "Other".