gracelang / minigrace

Self-hosting compiler for the Grace programming language
39 stars 22 forks source link

Compiler doesn't know about methods declared in a dialect that's defined using another dialect #234

Open KimBruce opened 7 years ago

KimBruce commented 7 years ago

If dialect "dialect" is used to build a new method, e.g., "myDialect", then list operations are not available in programs using that dialect. E.g., in the repository under sample/dialects is a simple dialect "dialectExample" that is written using dialect "dialect". The following program crashes:

dialect "dialectExample"

def l: List = list [1,2,3,4]
print(l)

with the message:

Syntax error: there is no method list(_).
    in "listTest" (line 3, column 15-28)

but runs fine if you erase the first line. The dialect "dialectExample" includes the line

inherit prelude.methods

which is supposed to make the prelude methods available. Does prelude.methods not include the collection classes?

apblack commented 7 years ago

This is very strange. list is definitely in prelude.methods, when prelude is standardGrace:

import "mirrors" as m
def pm = prelude.methods
def methodList = list(m.reflect(pm).methodNames).sorted
print (methodList.asString.replace ", " with "\n")

This prints a long list of method names, including list and list(_).

If you move the same program into the dialect minitest, which also inherits from prelude.methods, where prelude is standardGrace, it still works (although now the output is longer, since it includes the method defined by minitest).

@KimBruce, you didn't post the URL for your "dialectExample" in the issue. Is it written in another dialect? If it is, then when you inherit prelude.methods, you are inheriting that dialect's methods object, which evidently does not contain list(_).

You could change the dialect in which you are writing dialectExample, or you could import "standardGrace" explicitly, and inherit from an attribute of that.

KimBruce commented 7 years ago

DialectExample is one of the sample dialects in the repository. It does use dialect “dialect”. It is exactly those dialects that are causing the problem.

apblack commented 7 years ago

There seems to be a problem with the way that the compiler checks for the existence of methods. Notice that the error message you were seeing

Syntax error: there is no method list(_).
    in "listTest" (line 3, column 15-28)

is from the compiler, which can't find a list(_) method, not from the runtime. If you write outer.list in your sample program, it will run fine, so the list(_) method really is there; the compiler just isn't seeing it. (The same is true for List.)

Everything works fine with a single level of dialect; this problem occurs only when you have a dialect written in another dialect. The gct file generated for the first dialect does not contain methods as a fresh method, and thus the second dialect, although it inherits from first dialect's methods, does not know what they are.

KimBruce commented 7 years ago

This is extremely puzzling. Why does it know about list and List through outer, but can't figure it out on its own. And yes, it only occurs within a dialect defined with dialect "dialect".

apblack commented 7 years ago

The compiler doesn't "know about" list or List. If you make an explicit request on outer.wombat, then it just compiles code that will make the request, and hopes for the best. However, if you write just wombat, then the compiler has to look around and try and find a definition of wombat, either on the super chain, or on the outer chain. In these cases, definitions that are missing will cause bogus compilation errors.

apblack commented 7 years ago

There seem to be two problems here.

  1. Methods inherited by the second-level dialect are not being recorded in its .gct file. So dialectExample, even though it inherits prelude.methods and actually does support all of these methods, does not put them in the list of public methods in `dialectExample.gct. One way to force these methods into the .gct is to redeclare them, for example:
    
    dialect "dialect"
    inherit prelude.methods alias myList(_) = list(_)

method list(x) { myList(x) } type List = collections.List

// This example implements most of the requireTypes dialect. // It must be compiled as a dynamic module to be used. ...

  Then the _.gct_ file will contain

... public: List checker(1) list(1) myList(1) types: List


  and the dialectical program will compile (but see below).

 2. The second problem seems to be a phasing problem.  Even though recompiling the dialectical program (here _#234.grace_) will force recompilation of the _dialectExample_, dialect, the _dialectExample.gct_ file that gets used to build the symbol table for _#234_ is the **old** one (if it exists).
apblack commented 7 years ago

Here is my work-around for this issue.

Ratter than writing dialect in standardGrace, I wrote it like this:

#pragma ExtendedLineups
dialect "none"
import "standardGrace" as sg
import "errormessages" as errormessages
import "ast" as ast

inherit sg.methods

method methods {
    // This should be unnecessary, because it is inherited.
    // But the compiler gets confised and does not find it statically.
    prelude.clone(self)
}

Thus, it gets the declarations from the standardGrace dialect by inheritance rather than by using it as a dialect.

Similarly, I defined staticTypes like this:

#pragma noTypeChecks
#pragma ExtendedLineups
dialect "none"
import "dialect" as dia
import "ast" as ast
import "util" as util

inherit dia.methods

def TypeError is public = dia.CheckerFailure.refine "TypeError"

I then removed all of the qualifications by prelude, since names (like List) are no longer both inherited and available in an outer scope; they are just inherited.

KimBruce commented 7 years ago

Hi Andrew,

I tried getting this to work for a couple of days, but kept running into complications. One issue is that that “methods” didn’t seem to pick up public defs, causing lots of issues. I may try it again later, but for now I’m going back to working on variants.

[Sorry, I didn’t save enough of a record of what I’ve done to provide you with any meaningful feedback. I’ll try to get back to it later, but for now just decided to scrap it to move forward.]

Have a good vacation and let’s not worry about this until after you return.

Kim

On Mar 15, 2017, at 10:22 PM, Andrew Black notifications@github.com wrote:

Here is my work-around for this issue.

Ratter than writing dialect in standardGrace, I wrote it like this:

pragma ExtendedLineups

dialect "none" import "standardGrace" as sg import "errormessages" as errormessages import "ast" as ast

inherit sg.methods

method methods { // This should be unnecessary, because it is inherited. // But the compiler gets confised and does not find it statically. prelude.clone(self) } Thus, it gets the standardGrace dialect by inheritance rather than by using it as a dialect.

Similarly, I defined staticTypes like this:

pragma noTypeChecks

pragma ExtendedLineups

dialect "none" import "dialect" as dia import "ast" as ast import "util" as util

inherit dia.methods

def TypeError is public = dia.CheckerFailure.refine "TypeError" I then removed all of the qualifications by prelude, since names (like List) are no longer both inherited and available in an outer scope; they are just inherited.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/gracelang/minigrace/issues/234#issuecomment-286960964, or mute the thread https://github.com/notifications/unsubscribe-auth/ABuh-nTuLlF8NKD6Yq8xSdKDHNPCXwQgks5rmMcDgaJpZM4MS6fK.

apblack commented 7 years ago

One issue is that that “methods” didn’t seem to pick up public defs, causing lots of issues.

Yes, I saw that too. That should be another issue, I think. That's why TypeError is defined in terms of dia.CheckerFailure rather than plain CheckerFailure.