]> Cypherpunks repositories - gostls13.git/commitdiff
- handling of pointer forward decls
authorRobert Griesemer <gri@golang.org>
Tue, 29 Jul 2008 19:03:06 +0000 (12:03 -0700)
committerRobert Griesemer <gri@golang.org>
Tue, 29 Jul 2008 19:03:06 +0000 (12:03 -0700)
- some comments added to bug cases
- added notes

R=r
OCL=13543
CL=13543

test/bugs/bug041.go
test/bugs/bug042.go
usr/gri/gosrc/export.go
usr/gri/gosrc/globals.go
usr/gri/gosrc/parser.go

index 1a6e0dde801039ffca70033c5ddafb2ef8e76f46..709b774d05a839a884f8d219fe0514a83dd0d09d 100644 (file)
@@ -13,3 +13,11 @@ type S struct {
 func main() {
   var s S;
 }
+
+/*
+Another problem with implicit forward declarations (as in this program on line 6)
+is that it is not clear in which scope the type (here "T") should be declared.
+This is the main reason why we should not allow implicit forward declarations at all,
+and instead have an explicit type forward declaration. For more on this subject
+see bug042.go.
+*/
index 37a2bfd6936b5817fc9e90d18f2f24b6c1737c5f..9c873abfc8ce17a40743f3e4622ad4cdff753d82 100644 (file)
@@ -18,3 +18,11 @@ type T struct {
 func main() {
   var s S;
 }
+
+/*
+Per discussion w/ Ken, some time ago, we came to the conclusion that explicit
+forward declarations (as on line 5 in this program) are preferrable over
+implicit forward declarations because they make it explicit in which scope a
+type is to be declared fully, eventually. As an aside, the machinery for it is
+almost free in the compiler (one extra 'if' as far as I can tell).
+*/
index 57c49ba6dc5cef7ce6ebdd51561c1f069458e5a5..0ab3b0792c83b1110c6577597b19c73fe2cfceed 100755 (executable)
@@ -115,7 +115,7 @@ func (E *Exporter) WriteScope(scope *Globals.Scope) {
        // determine number of objects to export
        n := 0;
        for p := scope.entries.first; p != nil; p = p.next {
-               if p.obj.mark {
+               if p.obj.exported {
                        n++;
                }                       
        }
@@ -123,7 +123,7 @@ func (E *Exporter) WriteScope(scope *Globals.Scope) {
        // export the objects, if any
        if n > 0 {
                for p := scope.entries.first; p != nil; p = p.next {
-                       if p.obj.mark {
+                       if p.obj.exported {
                                E.WriteObject(p.obj);
                        }                       
                }
@@ -136,8 +136,8 @@ func (E *Exporter) WriteScope(scope *Globals.Scope) {
 
 
 func (E *Exporter) WriteObject(obj *Globals.Object) {
-       if obj == nil || !obj.mark {
-               panic "obj == nil || !obj.mark";
+       if obj == nil || !obj.exported {
+               panic "obj == nil || !obj.exported";
        }
 
        if obj.kind == Object.TYPE && obj.typ.obj == obj {
@@ -274,7 +274,7 @@ func (E *Exporter) Export(comp* Globals.Compilation, file_name string) {
        pkg := comp.pkgs[0];
        E.WritePackage(pkg);
        for p := pkg.scope.entries.first; p != nil; p = p.next {
-               if p.obj.mark {
+               if p.obj.exported {
                        E.WriteObject(p.obj);
                }
        }
index 31dc1a351353e40444395f13920a0d043fe35fb0..c98624124d633af3fc3a369aeaeac055e062abf7 100644 (file)
@@ -15,7 +15,7 @@ package Globals
 
 export Object
 type Object struct {
-       mark bool;  // mark => object marked for export
+       exported bool;
        pos int;  // source position
        kind int;
        ident string;
@@ -89,7 +89,7 @@ type Compilation struct {
 export NewObject
 func NewObject(pos, kind int, ident string) *Object {
        obj := new(Object);
-       obj.mark = false;
+       obj.exported = false;
        obj.pos = pos;
        obj.kind = kind;
        obj.ident = ident;
index 566e5176acaeac754109bd33c90ef3fd26d0bc7f..7d474275e48ec0fcdc63873517b2c20dd5b6b636 100644 (file)
@@ -29,6 +29,7 @@ type Parser struct {
 
        // Semantic analysis
        top_scope *Globals.Scope;
+       undef_types *Globals.List;
        exports *Globals.List;
 }
 
@@ -77,6 +78,7 @@ func (P *Parser) Open(comp *Globals.Compilation, S *Scanner.Scanner, verbose int
        P.S = S;
        P.Next();
        P.top_scope = Universe.scope;
+       P.undef_types = Globals.NewList();
        P.exports = Globals.NewList();
 }
 
@@ -244,7 +246,7 @@ func (P *Parser) ParseQualifiedIdent(pos int, ident string) *Globals.Object {
 // ----------------------------------------------------------------------------
 // Types
 
-func (P *Parser) ParseType() *Globals.Type{
+func (P *Parser) ParseType() *Globals.Type {
        P.Trace("Type");
        
        typ := P.TryType();
@@ -262,10 +264,11 @@ func (P *Parser) ParseTypeName() *Globals.Type {
        P.Trace("TypeName");
        
        if EnableSemanticTests {
+               pos := P.pos;
                obj := P.ParseQualifiedIdent(-1, "");
                typ := obj.typ;
                if obj.kind != Object.TYPE {
-                       P.Error(obj.pos, `"` + obj.ident + `" is not a type`);
+                       P.Error(pos, "qualified identifier does not denote a type");
                        typ = Universe.bad_t;
                }
                P.Ecart();
@@ -571,16 +574,42 @@ func (P *Parser) ParsePointerType() *Globals.Type {
        P.Trace("PointerType");
        
        P.Expect(Scanner.MUL);
-       typ := Universe.undef_t;
-       if (EnableSemanticTests && P.tok == Scanner.IDENT && P.Lookup(P.val) == nil) {
-               // forward declaration
-               panic "UNIMPLEMENTED *forward_declared_type";
+       typ := Globals.NewType(Type.POINTER);
+       
+       if EnableSemanticTests {
+               if P.tok == Scanner.IDENT {
+                       if P.Lookup(P.val) == nil {
+                               // implicit forward declaration
+                               // TODO very problematic: in which scope should the
+                               // type object be declared? It's different if this
+                               // is inside a struct or say in a var declaration.
+                               // This code is only here for "compatibility" with 6g.
+                               pos := P.pos;
+                               obj := Globals.NewObject(pos, Object.TYPE, P.ParseIdent());
+                               obj.typ = Globals.NewType(Type.UNDEF);
+                               obj.typ.obj = obj;  // primary type object
+                               typ.elt = obj.typ;
+                               // TODO obj should be declared, but scope is not clear
+                       } else {
+                               // type name
+                               // (ParseType() doesn't permit incomplete types,
+                               // so call ParseTypeName() here)
+                               typ.elt = P.ParseTypeName();
+                       }
+               } else {
+                       typ.elt = P.ParseType();
+               }
+       
+               // collect undefined pointer types
+               if typ.elt.form == Type.UNDEF {
+                       P.undef_types.AddTyp(typ);
+               }
+               
        } else {
-               typ = Globals.NewType(Type.POINTER);
                typ.elt = P.ParseType();
        }
 
-       P.Ecart();      
+       P.Ecart();
        return typ;
 }
 
@@ -589,6 +618,7 @@ func (P *Parser) ParsePointerType() *Globals.Type {
 func (P *Parser) TryType() *Globals.Type {
        P.Trace("Type (try)");
        
+       pos := P.pos;
        var typ *Globals.Type = nil;
        switch P.tok {
        case Scanner.IDENT: typ = P.ParseTypeName();
@@ -601,6 +631,10 @@ func (P *Parser) TryType() *Globals.Type {
        case Scanner.MUL: typ = P.ParsePointerType();
        }
 
+       if typ != nil && typ.form == Type.UNDEF {
+               P.Error(pos, "incomplete type");
+       }
+
        P.Ecart();
        return typ;
 }
@@ -1464,7 +1498,7 @@ func (P *Parser) ParseConstSpec(exported bool) {
        typ := P.TryType();
        if typ != nil {
                for p := list.first; p != nil; p = p.next {
-                       p.obj.mark = exported;
+                       p.obj.exported = exported;
                        p.obj.typ = typ;  // TODO should use/have set_type()!
                }
        }
@@ -1477,27 +1511,6 @@ func (P *Parser) ParseConstSpec(exported bool) {
 }
 
 
-func (P *Parser) ParseConstDecl(exported bool) {
-       P.Trace("ConstDecl");
-       
-       P.Expect(Scanner.CONST);
-       if P.tok == Scanner.LPAREN {
-               P.Next();
-               for P.tok == Scanner.IDENT {
-                       P.ParseConstSpec(exported);
-                       if P.tok != Scanner.RPAREN {
-                               P.Expect(Scanner.SEMICOLON);
-                       }
-               }
-               P.Next();
-       } else {
-               P.ParseConstSpec(exported);
-       }
-       
-       P.Ecart();
-}
-
-
 func (P *Parser) ParseTypeSpec(exported bool) {
        P.Trace("TypeSpec");
        
@@ -1505,22 +1518,23 @@ func (P *Parser) ParseTypeSpec(exported bool) {
        ident := P.ParseIdent();
        obj := P.top_scope.Lookup(ident);  // only lookup in top scope!
        if obj != nil {
-               // ok if forward declared type
+               // name already declared - ok if forward declared type
                if obj.kind != Object.TYPE || obj.typ.form != Type.UNDEF {
                        // TODO use obj.pos to refer to decl pos in error msg!
                        P.Error(pos, `"` + ident + `" is declared already`);
                }
        } else {
                obj = Globals.NewObject(pos, Object.TYPE, ident);
-               obj.mark = exported;
-               obj.typ = Universe.undef_t;  // TODO fix this
-               P.top_scope.Insert(obj);
+               obj.exported = exported;
+               obj.typ = Globals.NewType(Type.UNDEF);
+               obj.typ.obj = obj;  // primary type object
+               P.Declare(obj);
        }
        
-       typ := P.TryType();  // no type if we have a forward decl
+       typ := P.TryType();  // nil if we have an explicit forward declaration
+
        if typ != nil {
-               // TODO what about the name of incomplete types?
-               obj.typ = typ;  // TODO should use/have set_typ()!
+               obj.typ = typ;
                if typ.obj == nil {
                        typ.obj = obj;  // primary type object
                }
@@ -1530,27 +1544,6 @@ func (P *Parser) ParseTypeSpec(exported bool) {
 }
 
 
-func (P *Parser) ParseTypeDecl(exported bool) {
-       P.Trace("TypeDecl");
-       
-       P.Expect(Scanner.TYPE);
-       if P.tok == Scanner.LPAREN {
-               P.Next();
-               for P.tok == Scanner.IDENT {
-                       P.ParseTypeSpec(exported);
-                       if P.tok != Scanner.RPAREN {
-                               P.Expect(Scanner.SEMICOLON);
-                       }
-               }
-               P.Next();
-       } else {
-               P.ParseTypeSpec(exported);
-       }
-       
-       P.Ecart();
-}
-
-
 func (P *Parser) ParseVarSpec(exported bool) {
        P.Trace("VarSpec");
        
@@ -1573,21 +1566,32 @@ func (P *Parser) ParseVarSpec(exported bool) {
 }
 
 
-func (P *Parser) ParseVarDecl(exported bool) {
-       P.Trace("VarDecl");
+// TODO With method variables, we wouldn't need this dispatch function.
+func (P *Parser) ParseSpec(exported bool, keyword int) {
+       switch keyword {
+       case Scanner.CONST: P.ParseConstSpec(exported);
+       case Scanner.TYPE: P.ParseTypeSpec(exported);
+       case Scanner.VAR: P.ParseVarSpec(exported);
+       default: panic "UNREACHABLE";
+       }
+}
+
+
+func (P *Parser) ParseDecl(exported bool, keyword int) {
+       P.Trace("Decl");
        
-       P.Expect(Scanner.VAR);
+       P.Expect(keyword);
        if P.tok == Scanner.LPAREN {
                P.Next();
                for P.tok == Scanner.IDENT {
-                       P.ParseVarSpec(exported);
+                       P.ParseSpec(exported, keyword);
                        if P.tok != Scanner.RPAREN {
                                P.Expect(Scanner.SEMICOLON);
                        }
                }
                P.Next();
        } else {
-               P.ParseVarSpec(exported);
+               P.ParseSpec(exported, keyword);
        }
        
        P.Ecart();
@@ -1643,12 +1647,8 @@ func (P *Parser) ParseDeclaration() {
                exported = true;
        }
        switch P.tok {
-       case Scanner.CONST:
-               P.ParseConstDecl(exported);
-       case Scanner.TYPE:
-               P.ParseTypeDecl(exported);
-       case Scanner.VAR:
-               P.ParseVarDecl(exported);
+       case Scanner.CONST, Scanner.TYPE, Scanner.VAR:
+               P.ParseDecl(exported, P.tok);
        case Scanner.FUNC:
                P.ParseFuncDecl(exported);
        case Scanner.EXPORT:
@@ -1676,6 +1676,28 @@ func (P *Parser) ParseDeclaration() {
 // ----------------------------------------------------------------------------
 // Program
 
+func (P *Parser) ResolveUndefTypes() {
+       if !EnableSemanticTests {
+               return;
+       }
+       
+       for p := P.undef_types.first; p != nil; p = p.next {
+               typ := p.typ;
+               if typ.form != Type.POINTER {
+                       panic "unresolved types should be pointers only";
+               }
+               if typ.elt.form != Type.UNDEF {
+                       panic "unresolved pointer should point to undefined type";
+               }
+               obj := typ.elt.obj;
+               typ.elt = obj.typ;
+               if typ.elt.form == Type.UNDEF {
+                       P.Error(obj.pos, `"` + obj.ident + `" is not declared`);
+               }
+       }
+}
+
+
 func (P *Parser) MarkExports() {
        if !EnableSemanticTests {
                return;
@@ -1685,7 +1707,7 @@ func (P *Parser) MarkExports() {
        for p := P.exports.first; p != nil; p = p.next {
                obj := scope.Lookup(p.str);
                if obj != nil {
-                       obj.mark = true;
+                       obj.exported = true;
                        // For now we export deep
                        // TODO this should change eventually - we need selective export
                        if obj.kind == Object.TYPE {
@@ -1693,7 +1715,7 @@ func (P *Parser) MarkExports() {
                                if typ.form == Type.STRUCT || typ.form == Type.INTERFACE {
                                        scope := typ.scope;
                                        for p := scope.entries.first; p != nil; p = p.next {
-                                               p.obj.mark = true;
+                                               p.obj.exported = true;
                                        }
                                }
                        }
@@ -1726,6 +1748,7 @@ func (P *Parser) ParseProgram() {
                        P.Optional(Scanner.SEMICOLON);
                }
                
+               P.ResolveUndefTypes();
                P.MarkExports();
                P.CloseScope();
        }