GLSL Struct Semicolons vs Function Definitions

In the OpenGL Shading Language (GLSL), syntax rules inherited from C dictate that struct declarations must terminate with a trailing semicolon, whereas function definition blocks must not. This article breaks down why this distinction exists, examining how the GLSL compiler treats type definitions as declaration statements while treating function bodies as self-contained compound statements, and how this affects shader compilation.

The Syntactical Difference

In GLSL, a struct declaration requires a semicolon immediately after the closing brace:

struct Material {
    vec3 diffuse;
    float roughness;
}; // Semicolon is required here

Conversely, a function definition terminates at the closing brace without a semicolon:

void calculateLighting() {
    // Function body
} // No semicolon here

Why Struct Declarations Require a Semicolon

The requirement for a trailing semicolon after a struct definition stems from C grammar. In GLSL, defining a structure is technically a declaration statement. The syntax permits declaring variables of that struct type immediately between the closing brace and the terminating semicolon.

struct Light {
    vec3 position;
    vec3 color;
} sunLight, ambientLight; // Variables declared immediately

Even when no instance variables are instantiated at the time of definition, the compiler parses the block as a single declaration statement. The trailing semicolon acts as an explicit statement terminator, signaling to the parser that the type definition is complete and no variable instances are being declared alongside it.

If the semicolon is omitted, the compiler assumes the next identifier encountered in the code—such as the return type of an ensuing function or another variable—is an instance of the struct being declared, leading to cascade syntax errors:

struct Transform {
    mat4 model;
} // Missing semicolon

void main() { ... } // Error: Syntax error before 'void'

Why Function Definitions Do Not Use Semicolons

A function definition is composed of a function prototype header followed by a compound statement (the code block enclosed in { ... }).

In GLSL's formal grammar, a compound statement is a complete syntactic unit that carries its own boundaries. Unlike a variable or type declaration, it is not an expression or a declaration that requires a semicolon to delimit where it ends.

While function prototypes (forward declarations) require a semicolon because they are declarations without a body:

void calculateLighting(); // Valid prototype

Adding a semicolon after a function definition creates an extraneous empty statement at the global scope:

void main() {
    gl_Position = vec4(0.0);
}; // Error or warning: empty statement at global scope

Most GLSL compilers, such as glslang, will flag this trailing semicolon as a compilation error because empty statements are generally illegal outside of control flow blocks in standard GLSL specifications.