Need help with gcassert?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

156 Stars 4 Forks MIT License 27 Commits 0 Opened issues


Assert your Go code is inlined and bounds-check eliminated

Services available


Need anything else?

Contributors list

# 12,805
23 commits
# 11,601
1 commit
# 12,814
1 commit


gcassert is a program for making assertions about compiler decisions in Golang programs, via inline comment directives like


Currently supported directives:

  • //gcassert:inline
    to assert function callsites are inlined
  • //gcassert:bce
    to assert bounds checks are eliminated
  • //gcassert:noescape
    to assert variables don't escape to the heap


Given a file

package foo

func addOne(i int) int { return i+1 }

//gcassert:inline func addTwo(i int) int { return i+1 }

func a(ints []int) int { var sum int for i := range ints { //gcassert:bce,inline sum += addOne(ints[i])

    sum += addTwo(ints[i]) //gcassert:bce

    sum += ints[i] //gcassert:bce
return sum


The inline

directive will cause
to fail if the line
sum += addOne(ints[i])
is either not inlined or contains bounds checks.


directive on a function will cause
to fail if any of the callers of that function do not get inlined.

comments expect a comma-separated list of directives after
. They can be included above the line in question or after, as an inline comment.


To get the gcassert binary:

go get

To get the gcassert library:

go get


As a binary

Run gcassert on packages containing gcassert directives, like this:

gcassert ./package/path

The program will output all lines that had a gcassert directive that wasn't respected by the compiler.

For example, running on the testdata directory in this library will produce the following output:

$ gcassert ./testdata
testdata/noescape.go:21:        foo := foo{a: 1, b: 2}: foo escapes to heap:
testdata/bce.go:8:      fmt.Println(ints[5]): Found IsInBounds
testdata/bce.go:17:     sum += notInlinable(ints[i]): call was not inlined
testdata/bce.go:19:     sum += notInlinable(ints[i]): call was not inlined
testdata/inline.go:45:  alwaysInlined(3): call was not inlined
testdata/inline.go:51:  sum += notInlinable(i): call was not inlined
testdata/inline.go:55:  sum += 1: call was not inlined
testdata/inline.go:58:  test(0).neverInlinedMethod(10): call was not inlined

Inspecting each of the listed lines will show a

directive that wasn't upheld when running the compiler on the package.

As a library

gcassert is runnable as a library as well, for integration into your linter suite. It has a single package function,


To use it, pass in an

to which errors will be written and a list of paths to check for
assertions, like this:
package main

import ""

func main() { var buf strings.Builder if err := gcassert.GCAssert(&buf, "./path/to/package", "./otherpath/to/package"); err != nil { // handle non-lint-failure related errors panic(err) } // Output the errors to stdout. fmt.Println(buf.String()) }



The inline directive on a CallExpr asserts that the following statement contains a function that is inlined by the compiler. If the function does not get inlined, gcassert will fail.

The inline directive on a FuncDecl asserts that every caller of that function is actually inlined by the compiler.


The bce directive asserts that the following statement contains a slice index that has no necessary bounds checks. If the compiler adds bounds checks, gcassert will fail.


The noescape directive asserts that the line it's attached to (meaning, whichever Go AST node is annotated by the comment) produces no "escaped to heap" messages by the Go compiler.

The Go compiler emits an "escaped to heap" message for a particular line of code if any variables on that line of code are forced to escape.

Typically, the compiler will emit such a message on the line of code that the variable is declared on. This includes method receivers, method arguments, and var declarations.

This means that the annotation must be attached to the line of code that actually contains the variable in question. For a multi-line function signature, for example, the annotation must come on the line that has the variable that would be expected not to escape to the heap:

type foo struct { a int }

// This annotation will pass, because f does not escape. //gcassert:noescape func (f foo) returnA( // This annotation will fail, because a will escape to the heap. //gcassert:noescape a int, ) *int { return &a }

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.