Adding (and consuming) an extra argument supplied to make(map...), compiler still throws "Too many arguments to make(map...)"

206 views
Skip to first unread message

Kyle Stanly

unread,
Jun 6, 2016, 4:48:50 PM6/6/16
to golang-dev
What I am trying to do is add a third argument to make(map...), where it will use that argument to make compile-time modifications when it substitutes it into makemap (and whatever else) later on. I.E...

make(map[string]int, 1024, TREE)


Where TREE is a constant defined used to specify we want to have a tree map instead of a normal hash map. Imagine if the above map declaration was used to print occurrences of words in alphabetical order, I.E

"A: 23"
"An: 4"
"Bee: 2"
"C++: 1"

A poor example, I know, but imagine if there were a LARGE number of words (as in it scans a HUGE document) and wanted to handle this. A normal hash map would be inefficient (because it's not ordered) while a tree would be very efficient (as we just traverse each node in the tree). No extra space or time to sort each value, etc. One could suggest that, of course, they create their own map, which they could, certainly, but adding a built-in one would be very beneficial as well. At the very least, it'll be a good experiment for me even if it is a rejected suggestion...

Now on to the main issue... In 'cmd/compile/internal/gc/typecheck.go', I have modified the specific OMAKE->TMAP portion to consume the third argument if it exists, otherwise just placing a default value of 0 for the Right child. Mine quite literally copies how it acquires the first argument (capacity/size), hence I do not think there should be an issue.

                    case TMAP:
if i < len(args) {
l = args[i]
i++
l = typecheck(l, Erv)
l = defaultlit(l, Types[TINT])
if l.Type == nil {
n.Type = nil
return n
}
if !checkmake(t, "size", l) {
n.Type = nil
return n
}
n.Left = l

// L.J: Code added for 3rd parameter, 'mode'
if i < len(args) {
right := args[i]
i++
right = typecheck(right, Erv)
right = defaultlit(right, Types[TINT])
if right.Type == nil {
n.Type = nil
return n
}
if !checkmake(t, "mode", right) {
n.Type = nil
return n
}
n.Right = right
}
} else {
n.Left = Nodintconst(0)
n.Right = Nodintconst(0)
}
n.Op = OMAKEMAP

As it can be see, it consumes the argument (advances the index, i), typechecks, etc. If it does not exist, it just becomes a int constant of 0 to symbolize it does not exist.

Next, while it walks the tree, in 'cmd/compile/internal/gc/walk.go', I explicitly check for the Right child, however I've had some issues. Even though it SHOULD always be present, it seems that the Right node becomes nil somewhere along the line, hence I need to explicitly check for if n.Right isn't nil to prevent nil-pointer dereference runtime errors. However, once again, I have no idea WHY this is the case, as it explicitly gets set in typecheck.go. 

case OMAKEMAP:
t := n.Type

a := nodnil() // hmap buffer
r := nodnil() // bucket buffer
if n.Esc == EscNone {
// Allocate hmap buffer on stack.
var_ := temp(hmap(t))

a = Nod(OAS, var_, nil) // zero temp
a = typecheck(a, Etop)
init.Append(a)
a = Nod(OADDR, var_, nil)

// Allocate one bucket on stack.
// Maximum key/value size is 128 bytes, larger objects
// are stored with an indirection. So max bucket size is 2048+eps.
var_ = temp(mapbucket(t))

r = Nod(OAS, var_, nil) // zero temp
r = typecheck(r, Etop)
init.Append(r)
r = Nod(OADDR, var_, nil)
}

// L.J: Only change in code
if n.Right != nil && n.Right.Int64() != 0 {
Yyerror("Mode given: %v", n.Right.E)
}

fn := syslook("makemap")
fn = substArgTypes(fn, hmap(t), mapbucket(t), t.Key(), t.Val())
n = mkcall1(fn, n.Type, init, typename(n.Type), conv(n.Left, Types[TINT64]), a, r)

Then, lastly, when i attempt to compile a program (and I have run all.bat and make.bat to create the go.exe and gofmt.exe executable, and I am using them to compile the test programs), it explicitly states that there are too many arguments to make(map...). I've searched for this error, and it only performs this check after the switch statement for case OMAKE, which only checks if there were more arguments supplied than were consumed (hence my confusion).

if i < len(args) {
Yyerror("too many arguments to make(%v)", t)
n.Op = OMAKE
n.Type = nil
return n
}

The above seems to be the culprit, but once again I explicitly consume the argument by advancing the index, i. I am very confused... Can someone help explain why this is? Is there another check for the amount of arguments to make(...)?

Matthew Dempsky

unread,
Jun 6, 2016, 4:57:47 PM6/6/16
to Kyle Stanly, golang-dev
I suggest you try structuring the code as:

    // Existing code to handle the second argument, if any.
    if i < len(args) {
        l = args[i]
        ...
        n.Left = l
    } else {
        n.Left = Nodintconst(0)
    }

    // Your new code to handle the third argument, if any.
    if i < len(args) {
        r := args[i]
        ...
        n.Right = r
    } else {
        n.Right = Nodintconst(0)
    }
    


--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Kyle Stanly

unread,
Jun 6, 2016, 5:51:02 PM6/6/16
to golang-dev, thei...@gmail.com
I did precisely that...

                     case TMAP:
if i < len(args) {
l = args[i]
i++
l = typecheck(l, Erv)
l = defaultlit(l, Types[TINT])
if l.Type == nil {
n.Type = nil
return n
}
if !checkmake(t, "size", l) {
n.Type = nil
return n
}
n.Left = l
} else {
n.Left = Nodintconst(0)
}

// L.J: Code added for 3rd parameter, 'mode'
if i < len(args) {
right := args[i]
i++
right = typecheck(right, Erv)
right = defaultlit(right, Types[TINT])
if right.Type == nil {
n.Type = nil
return n
}
if !checkmake(t, "mode", right) {
n.Type = nil
return n
}
n.Right = right
} else {
n.Right = Nodintconst(0)
}
n.Op = OMAKEMAP

I get the same precise error. I'm wondering if the problem is that it's using Go 1.6 rather than the custom Go I just built (even though I specify to use the one in the current directory). If I add some println's into the runtime, I can definitely see it running my custom version of Go. 

I don't mean to sound lazy either, it's been one of the things plaguing me for days now, and I've stripped all custom code out to just literally doing the same exact way the existing code does it (to make sure it isn't my code). It does the same thing whether it is called from cygwin terminal or cmd. Go is installed for Windows x86_64, although I often invoke commands from the Cygwin terminal. Those aren't the only things plaguing me, but the rest are for another day.

Matthew Dempsky

unread,
Jun 6, 2016, 5:59:58 PM6/6/16
to Kyle Stanly, golang-dev
On Mon, Jun 6, 2016 at 2:51 PM, Kyle Stanly <thei...@gmail.com> wrote:
I'm wondering if the problem is that it's using Go 1.6 rather than the custom Go I just built (even though I specify to use the one in the current directory). If I add some println's into the runtime, I can definitely see it running my custom version of Go.

That's what I suspect.  Try adding a print message to the compiler instead to make sure you see it.  Maybe you're not rebuilding the compiler or you're accidentally using some other compiler binary.

The change I suggested seems to work after running make.bash:

$ git diff
diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
index c8ee941..c737361 100644
--- a/src/cmd/compile/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -1790,6 +1790,23 @@ OpSwitch:
                        } else {
                                n.Left = Nodintconst(0)
                        }
+                       if i < len(args) {
+                               right := args[i]
+                               i++
+                               right = typecheck(right, Erv)
+                               right = defaultlit(right, Types[TINT])
+                               if right.Type == nil {
+                                       n.Type = nil
+                                       return n
+                               }
+                               if !checkmake(t, "size", right) {
+                                       n.Type = nil
+                                       return n
+                               }
+                               n.Right = right
+                       } else {
+                               n.Right = Nodintconst(0)
+                       }
                        n.Op = OMAKEMAP
 
                case TCHAN:

$ cat /tmp/kyle.go
package p
var _ = make(map[int]int, 0, 0)

$ go tool compile /tmp/kyle.go

Kyle Stanly

unread,
Jun 7, 2016, 8:26:03 AM6/7/16
to golang-dev, thei...@gmail.com
Well, I feel very silly. I forgot that, even though I explicitly use the custom built go.exe, it still uses GOROOT environment variable for other things. Spent so long on this issue for something so simple. Thanks for the help.
Reply all
Reply to author
Forward
0 new messages