From: Brendan Hansen Date: Tue, 6 Jul 2021 03:26:44 +0000 (-0500) Subject: bugfix and added to immediate rendering capability X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=70f02878ae0579bcfb616aa625ed05df3e5a95ba;p=onyx.git bugfix and added to immediate rendering capability --- diff --git a/bin/onyx b/bin/onyx index efef128a..4248b85a 100755 Binary files a/bin/onyx and b/bin/onyx differ diff --git a/docs/plan b/docs/plan index 081487fd..116ef9e7 100644 --- a/docs/plan +++ b/docs/plan @@ -34,7 +34,7 @@ HOW: I have a working compiler with many features, but here are some additional features I would like to consider adding in the future. - [ ] Put type info in data section so it is runtime accessible + [X] Put type info in data section so it is runtime accessible - type name - size - alignment diff --git a/docs/todo b/docs/todo index cdbeaf3b..f7eab465 100644 --- a/docs/todo +++ b/docs/todo @@ -11,7 +11,7 @@ Command Line Interface: [X] Remove old code from CLI logic [X] Add statistic printing - [ ] Fix documentation generation (broken since compiler architecture change) + [X] Fix documentation generation (broken since compiler architecture change) [ ] Add automated running component - Store to temporary file (OS independent) - Detect / choose WASM backend diff --git a/modules/immediate_mode/canvas.onyx b/modules/immediate_mode/canvas.onyx new file mode 100644 index 00000000..6e9d88af --- /dev/null +++ b/modules/immediate_mode/canvas.onyx @@ -0,0 +1,39 @@ +package immediate_mode + +use package core + +// A render target that can be used instead of the default +Canvas :: struct { + color_texture : Texture; + depth_stencil_buffer : gl.GLRenderbuffer; + framebuffer : gl.GLFramebuffer; + + width, height: i32; +} + +create_canvas :: (width: i32, height: i32) -> Canvas { + + color_texture := create_texture(width, height, gl.RGBA8); + + depth_stencil_buffer := gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, depth_stencil_buffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH24_STENCIL8, width, height); + gl.bindRenderbuffer(gl.RENDERBUFFER, -1); + + framebuffer := gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, color_texture.texture, 0); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depth_stencil_buffer); + gl.bindFramebuffer(gl.FRAMEBUFFER, -1); + + assert(gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE, "Framebuffer not complete!"); + + return .{ + color_texture = color_texture, + depth_stencil_buffer = depth_stencil_buffer, + framebuffer = framebuffer, + width = width, + height = height, + }; +} + diff --git a/modules/immediate_mode/immediate_renderer.onyx b/modules/immediate_mode/immediate_renderer.onyx index 302f6482..1f750c50 100644 --- a/modules/immediate_mode/immediate_renderer.onyx +++ b/modules/immediate_mode/immediate_renderer.onyx @@ -1,7 +1,7 @@ package immediate_mode use package core -#private_file gl :: package gl +use package core.intrinsics.onyx { __initialize } Vector2 :: struct { x, y: f32; @@ -12,6 +12,11 @@ Color4 :: struct { a: f32 = 1; } +Immediate_Mode :: enum { + Triangles; + Lines; +} + Immediate_Vertex :: struct { position : Vector2; color : Color4; @@ -19,7 +24,6 @@ Immediate_Vertex :: struct { } Immediate_Renderer :: struct { - // Will point to either the simple_shader or the textured_shader. active_shader : ^Shader; simple_shader, textured_shader : Shader; @@ -28,7 +32,8 @@ Immediate_Renderer :: struct { // 'verticies' contains the vertex data and the maximum number of verticies // that can be rendered at a given time. 'vertex_count' is used to store how // many verticies will be rendered in the next draw call. 'vertex_count' is - // expected to be a multiple of 3, given that triangles are being rendered. + // expected to be a multiple of 3 or 2, given that triangles or lines are + // being rendered. verticies : [] Immediate_Vertex; vertex_count : u32; @@ -37,11 +42,18 @@ Immediate_Renderer :: struct { vertex_array : gl.GLVertexArrayObject; vertex_buffer : gl.GLBuffer; - // Needs to be a multiple of 3!! - Default_Max_Verticies :: 1023; + // Needs to be a multiple of 6!! + Default_Max_Verticies :: 1020; + + mode := Immediate_Mode.Triangles; + + canvas: ^Canvas = null; + + window_width, window_height: i32; make :: (max_verticies := Default_Max_Verticies) -> Immediate_Renderer { ir : Immediate_Renderer; + __initialize(^ir); init(^ir, max_verticies); return ir; @@ -120,9 +132,15 @@ Immediate_Renderer :: struct { gl.bufferSubData(gl.ARRAY_BUFFER, 0, .{ count = vertex_count * sizeof Immediate_Vertex, data = ~~verticies.data }); gl.bindBuffer(gl.ARRAY_BUFFER, -1); + gl_mode : gl.GLenum; + switch mode { + case .Triangles do gl_mode = gl.TRIANGLES; + case .Lines do gl_mode = gl.LINES; + } + gl.useProgram(active_shader.program); gl.bindVertexArray(vertex_array); - gl.drawArrays(gl.TRIANGLES, 0, vertex_count); + gl.drawArrays(gl_mode, 0, vertex_count); gl.bindVertexArray(-1); vertex_count = 0; @@ -267,6 +285,45 @@ Immediate_Renderer :: struct { gl.disable(gl.SCISSOR_TEST); } } + + set_mode :: (use ir: ^Immediate_Renderer, new_mode: Immediate_Mode) { + if mode != new_mode { + ir->flush(); + mode = new_mode; + } + } + + use_canvas :: (use ir: ^Immediate_Renderer, new_canvas: ^Canvas) { + if canvas == new_canvas do return; + ir->flush(); + + canvas = new_canvas; + if canvas != null { + gl.bindFramebuffer(gl.FRAMEBUFFER, canvas.framebuffer); + gl.viewport(0, 0, canvas.width, canvas.height); + + @Note // This flips the projection because generally with using framebuffers as textures, + // the bottom left, (0, 0) in WebGL world, you want to be the top left when you render it. + ir->use_ortho_projection(0, ~~canvas.width, ~~canvas.height, 0); + + } else { + gl.bindFramebuffer(gl.FRAMEBUFFER, -1); + gl.viewport(0, 0, window_width, window_height); + ir->use_ortho_projection(0, ~~window_width, 0, ~~window_height); + } + } + + set_window_size :: (use ir: ^Immediate_Renderer, width: i32, height: i32) { + window_width = width; + window_height = height; + + // If there is no active canvas, immediately update the values. + // Maybe this should flush the current buffer first? + if canvas == null { + gl.viewport(0, 0, window_width, window_height); + ir->use_ortho_projection(0, ~~window_width, 0, ~~window_height); + } + } } @@ -280,7 +337,7 @@ Immediate_Renderer :: struct { immediate_renderer : Immediate_Renderer; immediate_renderer_init :: () { - Immediate_Renderer.init(^immediate_renderer); + immediate_renderer = Immediate_Renderer.make(); } immediate_renderer_free :: () { @@ -306,7 +363,20 @@ circle :: (center: Vector2, radius: f32, color: Color4 = .{1,1,1}, segments := 1 flush :: () do immediate_renderer->flush(); -set_texture :: (texture_id: i32 = -1) do immediate_renderer->set_texture(texture_id); +set_texture :: #match { + (texture_id: i32 = -1) { immediate_renderer->set_texture(texture_id); }, + (texture: ^Texture, texture_index := 0) { + immediate_renderer->set_texture(texture_index); + + if texture_index >= 0 { + gl.activeTexture(gl.TEXTURE0 + texture_index); + gl.bindTexture(gl.TEXTURE_2D, texture.texture); + + } else { + gl.bindTexture(gl.TEXTURE_2D, -1); + } + } +} use_ortho_projection :: (left: f32, right: f32, top: f32, bottom: f32) { immediate_renderer->use_ortho_projection(left, right, top, bottom); @@ -322,4 +392,16 @@ scissor :: (x: f32, y: f32, w: f32, h: f32) { scissor_disable :: () { immediate_renderer->scissor_disable(); -} \ No newline at end of file +} + +set_mode :: (mode: Immediate_Mode) { + immediate_renderer->set_mode(mode); +} + +use_canvas :: (canvas: ^Canvas) { + immediate_renderer->use_canvas(canvas); +} + +set_window_size :: (width: i32, height: i32) { + immediate_renderer->set_window_size(width, height); +} diff --git a/modules/immediate_mode/module.onyx b/modules/immediate_mode/module.onyx index fc992e23..3c72307f 100644 --- a/modules/immediate_mode/module.onyx +++ b/modules/immediate_mode/module.onyx @@ -3,3 +3,7 @@ package immediate_mode #load "./immediate_renderer" #load "./gl_utils" +#load "./canvas" +#load "./texture" + +#private gl :: package gl diff --git a/modules/immediate_mode/texture.onyx b/modules/immediate_mode/texture.onyx new file mode 100644 index 00000000..9982e134 --- /dev/null +++ b/modules/immediate_mode/texture.onyx @@ -0,0 +1,49 @@ +package immediate_mode + +use package core + +Texture :: struct { + texture : gl.GLTexture; + + width, height: i32; +} + +create_texture :: (width: i32, height: i32, format: gl.GLenum) -> Texture { + texture := gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texStorage2D(gl.TEXTURE_2D, 1, format, width, height); + + // Setting some reasonable defaults for the texture parameters + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + + gl.bindTexture(gl.TEXTURE_2D, -1); + + return .{ + texture = texture, + width = width, + height = height, + }; +} + +load_texture :: (width: i32, height: i32, data: [] u8, format: gl.GLenum, internal_type := gl.UNSIGNED_BYTE) -> Texture { + texture := gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, format, internal_type, data); + + // Setting some reasonable defaults for the texture parameters + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + + gl.bindTexture(gl.TEXTURE_2D, -1); + + return .{ + texture = texture, + width = width, + height = height, + }; +} diff --git a/modules/ui/ui.onyx b/modules/ui/ui.onyx index 39f67518..aef9b0fe 100644 --- a/modules/ui/ui.onyx +++ b/modules/ui/ui.onyx @@ -5,7 +5,7 @@ use package core @Cleanup // Move these to the theme? // Or create a cache of fonts and put pointers/string in the themes? #private font : bmfont.BMFont; -#private font_texture : gl.GLTexture; +#private font_texture : gfx.Texture; @Temporary DEFAULT_TEXT_SIZE :: 1.0f @@ -161,8 +161,7 @@ is_hot_item :: (id: UI_Id) -> bool { @FontSizing // Currently, `size` is just a multipler for the baked font size. This should be changed to be height in pixels, or 'em's. draw_text_raw :: (text: str, x: f32, y: f32, size := DEFAULT_TEXT_SIZE, color := gfx.Color4.{1,1,1}) { - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, font_texture); + gfx.set_texture(^font_texture); gfx.use_alpha_shader(0); for glyph: bmfont.get_character_positions(^font, size, text, x, y) { @@ -173,7 +172,7 @@ draw_text_raw :: (text: str, x: f32, y: f32, size := DEFAULT_TEXT_SIZE, color := } gfx.flush(); - gl.bindTexture(gl.TEXTURE_2D, -1); + gfx.set_texture(); } draw_rect :: #match { @@ -286,14 +285,7 @@ get_text_width :: (text: str, size := DEFAULT_TEXT_SIZE) -> f32 { tex_width, tex_height := font.common.scale_width, font.common.scale_height; - font_texture = gl.createTexture(); - gl.bindTexture(gl.TEXTURE_2D, font_texture); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, tex_width, tex_height, 0, gl.ALPHA, gl.UNSIGNED_BYTE, texture_data); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.bindTexture(gl.TEXTURE_2D, -1); + font_texture = gfx.load_texture(tex_width, tex_height, texture_data, gl.ALPHA); } diff --git a/modules/webgl2/webgl2.js b/modules/webgl2/webgl2.js index bd533993..df8936d2 100644 --- a/modules/webgl2/webgl2.js +++ b/modules/webgl2/webgl2.js @@ -38,10 +38,34 @@ window.ONYX_MODULES.push({ gl.bindBuffer(target, buffers[buffer]); } }, - bindFramebuffer(target, framebuffer) { gl.bindFramebuffer(target, framebuffers[framebuffer]); }, - bindRenderbuffer(target, renderbuffer) { gl.bindRenderbuffer(target, renderbuffers[renderbuffer]); }, - bindTexture(target, texture) { gl.bindTexture(target, textures[texture]); }, - bindVertexArray(vertexArray) { gl.bindVertexArray(vertexArrays[vertexArray]); }, + bindFramebuffer(target, framebuffer) { + if (framebuffer == -1) { + gl.bindFramebuffer(target, null); + } else { + gl.bindFramebuffer(target, framebuffers[framebuffer]); + } + }, + bindRenderbuffer(target, renderbuffer) { + if (renderbuffer == -1) { + gl.bindRenderbuffer(target, null); + } else { + gl.bindRenderbuffer(target, renderbuffers[renderbuffer]); + } + }, + bindTexture(target, texture) { + if (texture == -1) { + gl.bindTexture(target, null); + } else { + gl.bindTexture(target, textures[texture]); + } + }, + bindVertexArray(vertexArray) { + if (vertexArray == -1) { + gl.bindVertexArray(null); + } else { + gl.bindVertexArray(vertexArrays[vertexArray]); + } + }, blendColor(red, green, blue, alpha) { gl.blendColor(red, green, blue, alpha); }, blendEquation(mode) { gl.blendEquation(mode); }, @@ -67,7 +91,7 @@ window.ONYX_MODULES.push({ canvas.width = width; canvas.height = height; }, - checkFrameBufferStatus(target) { return gl.checkFrameBufferStatus(target); }, + checkFramebufferStatus(target) { return gl.checkFramebufferStatus(target); }, clear(bit) { gl.clear(bit); }, clearColor(r, g, b, a) { gl.clearColor(r, g, b, a); }, clearDepth(depth) { gl.clearDepth(depth); }, @@ -83,8 +107,8 @@ window.ONYX_MODULES.push({ gl.compressedSubTexImage2D(target, level, internalformat, xoff, yoff, width, height, format, pixels); }, copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size) { gl.copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size); }, - copyTexImage2D(target, level, internalforamt, x, y, width, height, border) { - gl.copyTexImage2D(target, level, internalforamt, x, y, width, height, border); + copyTexImage2D(target, level, internalformat, x, y, width, height, border) { + gl.copyTexImage2D(target, level, internalformat, x, y, width, height, border); }, copyTexSubImage2D(target, level, xoff, yoff, x, y, width, height) { gl.copyTexSubImage2D(target, level, xoff, yoff, x, y, width, height); @@ -229,8 +253,9 @@ window.ONYX_MODULES.push({ gl.readPixels(x, y, width, height, format, type, pixeldata); }, readBuffer(src) { gl.readBuffer(src); }, - renderbufferStorageMultisample(target, samples, internalforamt, width, height) { - gl.renderbufferStorageMultisample(target, samples, internalforamt, width, height); + renderbufferStorage(target, internalformat, width, height) { gl.renderbufferStorage(target, internalformat, width, height); }, + renderbufferStorageMultisample(target, samples, internalformat, width, height) { + gl.renderbufferStorageMultisample(target, samples, internalformat, width, height); }, sampleCoverage(value, invert) { gl.sampleCoverage(value, invert); }, scissor(x, y, width, height) { gl.scissor(x, y, width, height); }, @@ -248,12 +273,13 @@ window.ONYX_MODULES.push({ stencilMaskSeparate(face, mask) { gl.stencilMaskSeparate(face, mask); }, stencilOp(fail, zfail, mask) { gl.stencilOp(fail, zfail, mask); }, stencilOpSeparate(face, fail, zfail, zpass) { gl.stencilOpSeparate(face, fail, zfail, zpass); }, - texImage2D(target, level, internalforamt, width, height, border, format, type, pixels, pixelslen) { + texImage2D(target, level, internalformat, width, height, border, format, type, pixels, pixelslen) { const data = new Uint8Array(window.ONYX_MEMORY.buffer, pixels, pixelslen); - gl.texImage2D(target, level, internalforamt, width, height, border, format, type, data); + gl.texImage2D(target, level, internalformat, width, height, border, format, type, data); }, texParameterf(target, pname, param) { gl.texParameterf(target, pname, param); }, texParameteri(target, pname, param) { gl.texParameteri(target, pname, param); }, + texStorage2D(target, levels, internalformat, width, height) { gl.texStorage2D(target, levels, internalformat, width, height); }, texSubImage2D(target, level, xoff, yoff, width, height, format, type, pixels, pixelslen) { const data = new Uint8Array(window.ONYX_MEMORY.buffer, pixels, pixelslen); gl.texSubImage2D(target, level, xoff, yoff, width, height, format, type, data); diff --git a/modules/webgl2/webgl2.onyx b/modules/webgl2/webgl2.onyx index 0b20cfb2..24141a39 100644 --- a/modules/webgl2/webgl2.onyx +++ b/modules/webgl2/webgl2.onyx @@ -747,7 +747,7 @@ bufferDataNoData :: (target: GLenum, size: GLsizei, usage: GLenum) bufferData :: #match { bufferDataWithData, bufferDataNoData } bufferSubData :: (target: GLenum, offset: GLsizei, data: [] void) -> void #foreign "gl" "bufferSubData" --- canvasSize :: (width: GLsizei, height: GLsizei) -> void #foreign "gl" "canvasSize" --- -checkFrameBufferStatus :: (target: GLenum) -> GLenum #foreign "gl" "checkFrameBufferStatus" --- +checkFramebufferStatus :: (target: GLenum) -> GLenum #foreign "gl" "checkFramebufferStatus" --- clear :: (mask: GLbitfield) -> void #foreign "gl" "clear" --- clearColor :: (red: GLclampf, green: GLclampf, blue: GLclampf, alpha: GLclampf) -> void #foreign "gl" "clearColor" --- clearDepth :: (depth: GLclampf) -> void #foreign "gl" "clearDepth" --- @@ -819,7 +819,8 @@ printProgramInfoLog :: (program: GLProgram) -> void #foreign "gl" "pr printShaderInfoLog :: (shader: GLShader) -> void #foreign "gl" "printShaderInfoLog" --- readBuffer :: (src: GLenum) -> void #foreign "gl" "readBuffer" --- readPixels :: (x: GLint, y: GLint, width: GLsizei, height: GLsizei, format: GLenum, type: GLenum, pixels: str) -> void #foreign "gl" "readPixels" --- -renderbufferStorageMultisample :: (target: GLenum, samples: GLsizei, internalforamt: GLenum, width: GLsizei, height: GLsizei) -> void #foreign "gl" "renderbufferStorageMultisample" --- +renderbufferStorage :: (target: GLenum, internalformat: GLenum, width: GLsizei, height: GLsizei) -> void #foreign "gl" "renderbufferStorage" --- +renderbufferStorageMultisample :: (target: GLenum, samples: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei) -> void #foreign "gl" "renderbufferStorageMultisample" --- sampleCoverage :: (value: GLclampf, invert: GLboolean) -> void #foreign "gl" "sampleCoverage" --- scissor :: (x: GLint, y: GLint, width: GLsizei, height: GLsizei) -> void #foreign "gl" "scissor" --- setSize :: (width: GLint, y: GLint) -> void #foreign "gl" "setSize" --- @@ -833,6 +834,7 @@ stencilOpSeparate :: (face: GLenum, fail: GLenum, zfail: GLenum, zp texImage2D :: (target: GLenum, level: GLint, internalFormat: GLenum, width: GLsizei, height: GLsizei, border: GLint, format: GLenum, type: GLenum, pixels: str) -> void #foreign "gl" "texImage2D" --- texParameterf :: (target: GLenum, pname: GLenum, param: GLfloat) -> void #foreign "gl" "texParameterf" --- texParameteri :: (target: GLenum, pname: GLenum, param: GLint) -> void #foreign "gl" "texParameteri" --- +texStorage2D :: (target: GLenum, levels: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei) -> void #foreign "gl" "texStorage2D" --- texSubImage2D :: (target: GLenum, level: GLint, xoff: GLint, yoff: GLint, width: GLsizei, height: GLsizei, format: GLenum, type: GLenum, pixels: str) -> void #foreign "gl" "texSubImage2D" --- uniform1f :: (loc: GLUniformLocation, x: GLfloat) -> void #foreign "gl" "uniform1f" --- uniform1i :: (loc: GLUniformLocation, x: GLint) -> void #foreign "gl" "uniform1i" --- diff --git a/src/onyxutils.c b/src/onyxutils.c index 5864662f..c4a8ef56 100644 --- a/src/onyxutils.c +++ b/src/onyxutils.c @@ -154,23 +154,27 @@ AstNode* symbol_resolve(Scope* start_scope, OnyxToken* tkn) { } AstNode* try_symbol_raw_resolve_from_node(AstNode* node, char* symbol) { - // CLEANUP: So many checks for null.... - if (!node) return NULL; - - if (node->kind == Ast_Kind_Type_Raw_Alias) - node = (AstNode *) ((AstTypeRawAlias *) node)->to->ast_type; + b32 used_pointer = 0; - if (!node) return NULL; + while (1) { + if (!node) return NULL; - if (node->kind == Ast_Kind_Type_Alias) - node = (AstNode *) ((AstTypeAlias *) node)->to; + switch (node->kind) { + case Ast_Kind_Type_Raw_Alias: node = (AstNode *) ((AstTypeRawAlias *) node)->to->ast_type; break; + case Ast_Kind_Type_Alias: node = (AstNode *) ((AstTypeAlias *) node)->to; break; + case Ast_Kind_Pointer_Type: { + if (used_pointer) goto all_types_peeled_off; + used_pointer = 1; - if (!node) return NULL; + node = (AstNode *) ((AstPointerType *) node)->elem; + break; + } - // A single pointer can be dereferenced to lookup symbols in struct. - if (node->kind == Ast_Kind_Pointer_Type) - node = (AstNode *) ((AstPointerType *) node)->elem; + default: goto all_types_peeled_off; + } + } +all_types_peeled_off: if (!node) return NULL; switch (node->kind) {