From d29423c24e654bccaf823a1e14f4dc322c7ec52f Mon Sep 17 00:00:00 2001 From: skytrias Date: Tue, 20 Dec 2022 12:17:23 +0100 Subject: [PATCH 001/144] add fontstash and nanovg port from heimdall --- vendor/fontstash/fontstash.odin | 1227 +++++++++++ vendor/nanovg/gl/frag.glsl | 123 ++ vendor/nanovg/gl/gl.odin | 1452 +++++++++++++ vendor/nanovg/gl/vert.glsl | 19 + vendor/nanovg/nanovg.odin | 3503 +++++++++++++++++++++++++++++++ 5 files changed, 6324 insertions(+) create mode 100644 vendor/fontstash/fontstash.odin create mode 100644 vendor/nanovg/gl/frag.glsl create mode 100644 vendor/nanovg/gl/gl.odin create mode 100644 vendor/nanovg/gl/vert.glsl create mode 100644 vendor/nanovg/nanovg.odin diff --git a/vendor/fontstash/fontstash.odin b/vendor/fontstash/fontstash.odin new file mode 100644 index 000000000..ba19d068a --- /dev/null +++ b/vendor/fontstash/fontstash.odin @@ -0,0 +1,1227 @@ +package fontstash + +import "core:runtime" +import "core:fmt" +import "core:log" +import "core:os" +import "core:mem" +import "core:math" +import "core:unicode" +import "core:strings" +import stbtt "vendor:stb/truetype" + +// This is a port from Fontstash into odin - specialized for nanovg + +// Notable features of Fontstash: +// Contains a *single* channel texture atlas for multiple fonts +// Manages a lookup table for frequent glyphs +// Allows blurred font glyphs +// Atlas can resize + +// Changes from the original: +// stb truetype only +// no scratch allocation -> parts use odins dynamic arrays +// leaves GPU vertex creation & texture management up to the user +// texture atlas expands by default + +INVALID :: -1 +MAX_STATES :: 20 +HASH_LUT_SIZE :: 256 +INIT_GLYPHS :: 256 +INIT_ATLAS_NODES :: 256 +MAX_FALLBACKS :: 20 +Glyph_Index :: i32 // in case you want to change the handle for glyph indices + +AlignHorizontal :: enum { + LEFT, + CENTER, + RIGHT, +} + +AlignVertical :: enum { + TOP, + MIDDLE, + BOTTOM, + BASELINE, +} + +Font :: struct { + name: string, // allocated + + info: stbtt.fontinfo, + loadedData: []byte, + + ascender: f32, + descender: f32, + lineHeight: f32, + + glyphs: [dynamic]Glyph, + lut: [HASH_LUT_SIZE]int, + + fallbacks: [MAX_FALLBACKS]int, + nfallbacks: int, +} + +Glyph :: struct { + codepoint: rune, + index: Glyph_Index, + next: int, + isize: i16, + blurSize: i16, + x0, y0, x1, y1: i16, + xoff, yoff: i16, + xadvance: i16, +} + +AtlasNode :: struct { + x, y, width: i16, +} + +Vertex :: struct #packed { + x, y: f32, + u, v: f32, + color: [4]u8, +} + +QuadLocation :: enum { + TOPLEFT, + BOTTOMLEFT, +} + +FontContext :: struct { + fonts: [dynamic]Font, // allocated using context.allocator + + // always assuming user wants to resize + nodes: [dynamic]AtlasNode, + + // actual pixels + textureData: []byte, // allocated using context.allocator + width, height: int, + // 1 / texture_atlas_width, 1 / texture_atlas_height + itw, ith: f32, + + // state + states: []State, + state_count: int, // used states + + location: QuadLocation, + + // dirty rectangle of the texture region that was updated + dirtyRect: [4]f32, + + // callbacks with userData passed + userData: rawptr, // by default set to the context + + // called when a texture is expanded and needs handling + callbackResize: proc(data: rawptr, w, h: int), + // called in state_end to update the texture region that changed + callbackUpdate: proc(data: rawptr, dirtyRect: [4]f32, textureData: rawptr), +} + +Init :: proc(using ctx: ^FontContext, w, h: int, loc: QuadLocation) { + userData = ctx + location = loc + fonts = make([dynamic]Font, 0, 8) + + itw = f32(1) / f32(w) + ith = f32(1) / f32(h) + textureData = make([]byte, w * h) + + width = w + height = h + nodes = make([dynamic]AtlasNode, 0, INIT_ATLAS_NODES) + __dirtyRectReset(ctx) + + states = make([]State, MAX_STATES) + + // NOTE NECESSARY + append(&nodes, AtlasNode { + width = i16(w), + }) + + __AtlasAddWhiteRect(ctx, 2, 2) + + PushState(ctx) + ClearState(ctx) +} + +Destroy :: proc(using ctx: ^FontContext) { + for font in &fonts { + delete(font.loadedData) + delete(font.glyphs) + } + + delete(states) + delete(textureData) + delete(fonts) + delete(nodes) +} + +Reset :: proc(using ctx: ^FontContext) { + __atlasReset(ctx, width, height) + __dirtyRectReset(ctx) + mem.zero_slice(textureData) + + for font in &fonts { + __lutReset(&font) + } + + __AtlasAddWhiteRect(ctx, 2, 2) + PushState(ctx) + ClearState(ctx) +} + +__atlasInsertNode :: proc(using ctx: ^FontContext, idx, x, y, w: int) { + // resize is alright here + resize(&nodes, len(nodes) + 1) + + // shift nodes up once to leave space at idx + for i := len(nodes) - 1; i > idx; i -= 1 { + nodes[i] = nodes[i - 1] + } + + // set new inserted one to properties + nodes[idx].x = i16(x) + nodes[idx].y = i16(y) + nodes[idx].width = i16(w) +} + +__atlasRemoveNode :: proc(using ctx: ^FontContext, idx: int) { + if len(nodes) == 0 { + return + } + + // remove node at index, shift elements down + for i in idx.. width { + __atlasInsertNode(ctx, len(nodes), width, 0, w - width) + } + + width = w + height = h +} + +__atlasReset :: proc(using ctx: ^FontContext, w, h: int) { + width = w + height = h + clear(&nodes) + + // init root node + append(&nodes, AtlasNode { + width = i16(w), + }) +} + +__AtlasAddSkylineLevel :: proc(using ctx: ^FontContext, idx, x, y, w, h: int) { + // insert new node + __atlasInsertNode(ctx, idx, x, y + h, w) + + // Delete skyline segments that fall under the shadow of the new segment. + for i := idx + 1; i < len(nodes); i += 1 { + if nodes[i].x < nodes[i - 1].x + nodes[i - 1].width { + shrink := nodes[i-1].x + nodes[i-1].width - nodes[i].x + nodes[i].x += i16(shrink) + nodes[i].width -= i16(shrink) + + if nodes[i].width <= 0 { + __atlasRemoveNode(ctx, i) + i -= 1 + } else { + break + } + } else { + break + } + } + + // Merge same height skyline segments that are next to each other. + for i := 0; i < len(nodes) - 1; i += 1 { + if nodes[i].y == nodes[i + 1].y { + nodes[i].width += nodes[i + 1].width + __atlasRemoveNode(ctx, i + 1) + i -= 1 + } + } +} + +__AtlasRectFits :: proc(using ctx: ^FontContext, i, w, h: int) -> int { + // Checks if there is enough space at the location of skyline span 'i', + // and return the max height of all skyline spans under that at that location, + // (think tetris block being dropped at that position). Or -1 if no space found. + x := int(nodes[i].x) + y := int(nodes[i].y) + + if x + w > width { + return -1 + } + + i := i + space_left := w + for space_left > 0 { + if i == len(nodes) { + return -1 + } + + y = max(y, int(nodes[i].y)) + if y + h > height { + return -1 + } + + space_left -= int(nodes[i].width) + i += 1 + } + + return y +} + +__AtlasAddRect :: proc(using ctx: ^FontContext, rw, rh: int) -> (rx, ry: int, ok: bool) { + besth := height + bestw := width + besti, bestx, besty := -1, -1, -1 + + // Bottom left fit heuristic. + for i in 0.. int { + data, ok := os.read_entire_file(path) + + if !ok { + log.panicf("FONT: failed to read font at %s", path) + } + + return AddFontMem(ctx, name, data) +} + +// push a font to the font stack +// optionally init with ascii characters at a wanted size +AddFontMem :: proc( + ctx: ^FontContext, + name: string, + data: []u8, +) -> int { + append(&ctx.fonts, Font {}) + res := &ctx.fonts[len(ctx.fonts) - 1] + res.loadedData = data + res.name = strings.clone(name) + + stbtt.InitFont(&res.info, &res.loadedData[0], 0) + ascent, descent, line_gap: i32 + stbtt.GetFontVMetrics(&res.info, &ascent, &descent, &line_gap) + fh := f32(ascent - descent) + res.ascender = f32(ascent) / fh + res.descender = f32(descent) / fh + res.lineHeight = (fh + f32(line_gap)) / fh + res.glyphs = make([dynamic]Glyph, 0, INIT_GLYPHS) + + __lutReset(res) + return len(ctx.fonts) - 1 +} + +AddFont :: proc { AddFontPath, AddFontMem } + +AddFallbackFont :: proc(ctx: ^FontContext, base, fallback: int) -> bool { + base_font := __getFont(ctx, base) + + if base_font.nfallbacks < MAX_FALLBACKS { + base_font.fallbacks[base_font.nfallbacks] = fallback + base_font.nfallbacks += 1 + return true + } + + return false +} + +ResetFallbackFont :: proc(ctx: ^FontContext, base: int) { + base_font := __getFont(ctx, base) + base_font.nfallbacks = 0 + clear(&base_font.glyphs) + __lutReset(base_font) +} + +// find font by name +GetFontByName :: proc(ctx: ^FontContext, name: string) -> int { + for font, i in ctx.fonts { + if font.name == name { + return i + } + } + + return INVALID +} + +__lutReset :: proc(font: ^Font) { + // set lookup table + for i in 0.. u32 { + a := a + a += ~(a << 15) + a ~= (a >> 10) + a += (a << 3) + a ~= (a >> 6) + a += (a << 11) + a ~= (a >> 16) + return a +} + +__renderGlyphBitmap :: proc( + font: ^Font, + output: []u8, + outWidth: i32, + outHeight: i32, + outStride: i32, + scaleX: f32, + scaleY: f32, + glyphIndex: Glyph_Index, +) { + stbtt.MakeGlyphBitmap(&font.info, raw_data(output), outWidth, outHeight, outStride, scaleX, scaleY, glyphIndex) +} + +__buildGlyphBitmap :: proc( + font: ^Font, + glyphIndex: Glyph_Index, + pixelSize: f32, + scale: f32, +) -> (advance, lsb, x0, y0, x1, y1: i32) { + stbtt.GetGlyphHMetrics(&font.info, glyphIndex, &advance, &lsb) + stbtt.GetGlyphBitmapBox(&font.info, glyphIndex, scale, scale, &x0, &y0, &x1, &y1) + return +} + +// get glyph and push to atlas if not exists +__getGlyph :: proc( + ctx: ^FontContext, + font: ^Font, + codepoint: rune, + isize: i16, + blurSize: i16 = 0, +) -> (res: ^Glyph) #no_bounds_check { + if isize < 2 { + return + } + + // find code point and size + h := __hashint(u32(codepoint)) & (HASH_LUT_SIZE - 1) + i := font.lut[h] + for i != -1 { + glyph := &font.glyphs[i] + + if + glyph.codepoint == codepoint && + glyph.isize == isize && + glyph.blurSize == blurSize + { + res = glyph + return + } + + i = glyph.next + } + + // could not find glyph, create it. + render_font := font // font used to render + glyph_index := __getGlyph_index(font, codepoint) + if glyph_index == 0 { + // lookout for possible fallbacks + for i in 0.. 0 { + __blur(dst, int(gw), int(gh), ctx.width, blurSize) + } + + ctx.dirtyRect[0] = cast(f32) min(int(ctx.dirtyRect[0]), int(res.x0)) + ctx.dirtyRect[1] = cast(f32) min(int(ctx.dirtyRect[1]), int(res.y0)) + ctx.dirtyRect[2] = cast(f32) max(int(ctx.dirtyRect[2]), int(res.x1)) + ctx.dirtyRect[3] = cast(f32) max(int(ctx.dirtyRect[3]), int(res.y1)) + + return +} + +///////////////////////////////// +// blur +///////////////////////////////// + +// Based on Exponential blur, Jani Huhtanen, 2006 + +BLUR_APREC :: 16 +BLUR_ZPREC :: 7 + +__blurCols :: proc(dst: []u8, w, h, dstStride, alpha: int) { + dst := dst + + for y in 0..> BLUR_APREC + dst[x] = u8(z >> BLUR_ZPREC) + } + + dst[w - 1] = 0 // force zero border + z = 0 + + for x := w - 2; x >= 0; x -= 1 { + z += (alpha * ((int(dst[x]) << BLUR_ZPREC) - z)) >> BLUR_APREC + dst[x] = u8(z >> BLUR_ZPREC) + } + + dst[0] = 0 // force zero border + dst = dst[dstStride:] // advance slice + } +} + +__blurRows :: proc(dst: []u8, w, h, dstStride, alpha: int) { + dst := dst + + for x in 0..> BLUR_APREC + dst[y] = u8(z >> BLUR_ZPREC) + } + + dst[(h - 1) * dstStride] = 0 // force zero border + z = 0 + + for y := (h - 2) * dstStride; y >= 0; y -= dstStride { + z += (alpha * ((int(dst[y]) << BLUR_ZPREC) - z)) >> BLUR_APREC + dst[y] = u8(z >> BLUR_ZPREC) + } + + dst[0] = 0 // force zero border + dst = dst[1:] // advance + } +} + +__blur :: proc(dst: []u8, w, h, dstStride: int, blurSize: i16) { + assert(blurSize != 0) + + // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity) + sigma := f32(blurSize) * 0.57735 // 1 / sqrt(3) + alpha := int((1 << BLUR_APREC) * (1 - math.exp(-2.3 / (sigma + 1)))) + __blurRows(dst, w, h, dstStride, alpha) + __blurCols(dst, w, h, dstStride, alpha) + __blurRows(dst, w, h, dstStride, alpha) + __blurCols(dst, w, h, dstStride, alpha) +} + +///////////////////////////////// +// Texture expansion +///////////////////////////////// + +ExpandAtlas :: proc(ctx: ^FontContext, width, height: int, allocator := context.allocator) -> bool { + width := max(ctx.width, width) + height := max(ctx.height, height) + + if width == ctx.width && height == ctx.height { + return true + } + + if ctx.callbackResize != nil { + ctx.callbackResize(ctx.userData, width, height) + } + + data := make([]byte, width * height, allocator) + + for i in 0.. ctx.width { + mem.set(&data[i * width + ctx.width], 0, width - ctx.width) + } + } + + if height > ctx.height { + mem.set(&data[ctx.height * width], 0, (height - ctx.height) * width) + } + + delete(ctx.textureData) + ctx.textureData = data + + // increase atlas size + __atlasExpand(ctx, width, height) + + // add existing data as dirty + maxy := i16(0) + for node in ctx.nodes { + maxy = max(maxy, node.y) + } + ctx.dirtyRect[0] = 0 + ctx.dirtyRect[1] = 0 + ctx.dirtyRect[2] = f32(ctx.width) + ctx.dirtyRect[3] = f32(maxy) + + ctx.width = width + ctx.height = height + ctx.itw = 1.0 / f32(width) + ctx.ith = 1.0 / f32(height) + + return true +} + +ResetAtlas :: proc(ctx: ^FontContext, width, height: int, allocator := context.allocator) -> bool { + if width == ctx.width && height == ctx.height { + // just clear + mem.zero_slice(ctx.textureData) + } else { + // realloc + ctx.textureData = make([]byte, width * height, allocator) + } + + ctx.dirtyRect[0] = f32(width) + ctx.dirtyRect[1] = f32(height) + ctx.dirtyRect[2] = 0 + ctx.dirtyRect[3] = 0 + + // reset fonts + for font in &ctx.fonts { + clear(&font.glyphs) + __lutReset(&font) + } + + ctx.width = width + ctx.height = height + ctx.itw = 1.0 / f32(width) + ctx.ith = 1.0 / f32(height) + + __AtlasAddWhiteRect(ctx, 2, 2) + return true +} + +__getGlyph_index :: proc(font: ^Font, codepoint: rune) -> Glyph_Index { + return stbtt.FindGlyphIndex(&font.info, codepoint) +} + +__getPixelHeightScale :: proc(font: ^Font, pixel_height: f32) -> f32 { + return stbtt.ScaleForPixelHeight(&font.info, pixel_height) +} + +__getGlyphKernAdvance :: proc(font: ^Font, glyph1, glyph2: Glyph_Index) -> i32 { + return stbtt.GetGlyphKernAdvance(&font.info, glyph1, glyph2) +} + +// get a font with bounds checking +__getFont :: proc(ctx: ^FontContext, index: int, loc := #caller_location) -> ^Font #no_bounds_check { + runtime.bounds_check_error_loc(loc, index, len(ctx.fonts)) + return &ctx.fonts[index] +} + +// only useful for single glyphs where you quickly want the width +CodepointWidth :: proc( + font: ^Font, + codepoint: rune, + scale: f32, +) -> f32 { + glyph_index := __getGlyph_index(font, codepoint) + xadvance, lsb: i32 + stbtt.GetGlyphHMetrics(&font.info, glyph_index, &xadvance, &lsb) + return f32(xadvance) * scale +} + +// get top and bottom line boundary +LineBounds :: proc(ctx: ^FontContext, y: f32) -> (miny, maxy: f32) { + state := __getState(ctx) + font := __getFont(ctx, state.font) + isize := i16(state.size * 10.0) + y := y + y += __getVerticalAlign(ctx, font, state.av, isize) + + if ctx.location == .TOPLEFT { + miny = y - font.ascender * f32(isize) / 10 + maxy = miny + font.lineHeight * f32(isize / 10) + } else if ctx.location == .BOTTOMLEFT { + miny = y + font.ascender * f32(isize) / 10 + maxy = miny - font.lineHeight * f32(isize / 10) + } + + return +} + +// reset dirty rect +__dirtyRectReset :: proc(using ctx: ^FontContext) { + dirtyRect[0] = f32(width) + dirtyRect[1] = f32(height) + dirtyRect[2] = 0 + dirtyRect[3] = 0 +} + +// true when the dirty rectangle is valid and needs a texture update on the gpu +ValidateTexture :: proc(using ctx: ^FontContext, dirty: ^[4]f32) -> bool { + if dirtyRect[0] < dirtyRect[2] && dirtyRect[1] < dirtyRect[3] { + dirty[0] = dirtyRect[0] + dirty[1] = dirtyRect[1] + dirty[2] = dirtyRect[2] + dirty[3] = dirtyRect[3] + __dirtyRectReset(ctx) + return true + } + + return false +} + +// get alignment based on font +__getVerticalAlign :: proc( + ctx: ^FontContext, + font: ^Font, + av: AlignVertical, + pixelSize: i16, +) -> (res: f32) { + switch ctx.location { + case .TOPLEFT: { + switch av { + case .TOP: res = font.ascender * f32(pixelSize) / 10 + case .MIDDLE: res = (font.ascender + font.descender) / 2 * f32(pixelSize) / 10 + case .BASELINE: res = 0 + case .BOTTOM: res = font.descender * f32(pixelSize) / 10 + } + } + + case .BOTTOMLEFT: { + switch av { + case .TOP: res = -font.ascender * f32(pixelSize) / 10 + case .MIDDLE: res = -(font.ascender + font.descender) / 2 * f32(pixelSize) / 10 + case .BASELINE: res = 0 + case .BOTTOM: res = -font.descender * f32(pixelSize) / 10 + } + } + } + + return +} + +@(private) +UTF8_ACCEPT :: 0 + +@(private) +UTF8_REJECT :: 1 + +@(private) +utf8d := [400]u8 { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df + 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef + 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff + 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2 + 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4 + 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6 + 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8 +} + +// decode codepoints from a state +@(private) +__decutf8 :: #force_inline proc(state: ^rune, codep: ^rune, b: byte) -> bool { + b := rune(b) + type := utf8d[b] + codep^ = (state^ != UTF8_ACCEPT) ? ((b & 0x3f) | (codep^ << 6)) : ((0xff >> type) & (b)) + state^ = rune(utf8d[256 + state^ * 16 + rune(type)]) + return state^ == UTF8_ACCEPT +} + +// state used to share font options +State :: struct { + font: int, + size: f32, + color: [4]u8, + spacing: f32, + blur: f32, + + ah: AlignHorizontal, + av: AlignVertical, +} + +// quad that should be used to draw from the texture atlas +Quad :: struct { + x0, y0, s0, t0: f32, + x1, y1, s1, t1: f32, +} + +// text iteration with custom settings +TextIter :: struct { + x, y, nextx, nexty, scale, spacing: f32, + isize, iblur: i16, + + font: ^Font, + previousGlyphIndex: Glyph_Index, + + // unicode iteration + utf8state: rune, // utf8 + codepoint: rune, + text: string, + codepointCount: int, + + // byte indices + str: int, + next: int, + end: int, +} + +// push a state, copies the current one over to the next one +PushState :: proc(using ctx: ^FontContext, loc := #caller_location) #no_bounds_check { + runtime.bounds_check_error_loc(loc, state_count, MAX_STATES) + + if state_count > 0 { + states[state_count] = states[state_count - 1] + } + + state_count += 1 +} + +// pop a state +PopState :: proc(using ctx: ^FontContext) { + if state_count <= 1 { + log.error("FONTSTASH: state underflow! to many pops were called") + } else { + state_count -= 1 + } +} + +// clear current state +ClearState :: proc(ctx: ^FontContext) { + state := __getState(ctx) + state.size = 12 + state.color = 255 + state.blur = 0 + state.spacing = 0 + state.font = 0 + state.ah = .LEFT + state.av = .BASELINE +} + +__getState :: #force_inline proc(ctx: ^FontContext) -> ^State #no_bounds_check { + return &ctx.states[ctx.state_count - 1] +} + +SetSize :: proc(ctx: ^FontContext, size: f32) { + __getState(ctx).size = size +} + +SetColor :: proc(ctx: ^FontContext, color: [4]u8) { + __getState(ctx).color = color +} + +SetSpacing :: proc(ctx: ^FontContext, spacing: f32) { + __getState(ctx).spacing = spacing +} + +SetBlur :: proc(ctx: ^FontContext, blur: f32) { + __getState(ctx).blur = blur +} + +SetFont :: proc(ctx: ^FontContext, font: int) { + __getState(ctx).font = font +} + +SetAH :: SetAlignHorizontal +SetAV :: SetAlignVertical + +SetAlignHorizontal :: proc(ctx: ^FontContext, ah: AlignHorizontal) { + __getState(ctx).ah = ah +} + +SetAlignVertical :: proc(ctx: ^FontContext, av: AlignVertical) { + __getState(ctx).av = av +} + +__getQuad :: proc( + ctx: ^FontContext, + font: ^Font, + + previousGlyphIndex: i32, + glyph: ^Glyph, + + scale: f32, + spacing: f32, + + x, y: ^f32, + quad: ^Quad, +) { + if previousGlyphIndex != -1 { + adv := f32(__getGlyphKernAdvance(font, previousGlyphIndex, glyph.index)) * scale + x^ += f32(int(adv + spacing + 0.5)) + } + + // fill props right + rx, ry, x0, y0, x1, y1, xoff, yoff, glyph_width, glyph_height: f32 + xoff = f32(glyph.xoff + 1) + yoff = f32(glyph.yoff + 1) + x0 = f32(glyph.x0 + 1) + y0 = f32(glyph.y0 + 1) + x1 = f32(glyph.x1 - 1) + y1 = f32(glyph.y1 - 1) + + switch ctx.location { + case .TOPLEFT: { + rx = math.floor(x^ + xoff) + ry = math.floor(y^ + yoff) + + quad.x0 = rx + quad.y0 = ry + quad.x1 = rx + x1 - x0 + quad.y1 = ry + y1 - y0 + + quad.s0 = x0 * ctx.itw + quad.t0 = y0 * ctx.ith + quad.s1 = x1 * ctx.itw + quad.t1 = y1 * ctx.ith + } + + case .BOTTOMLEFT: { + rx = math.floor(x^ + xoff) + ry = math.floor(y^ - yoff) + + quad.x0 = rx + quad.y0 = ry + quad.x1 = rx + x1 - x0 + quad.y1 = ry - y1 + y0 + + quad.s0 = x0 * ctx.itw + quad.t0 = y0 * ctx.ith + quad.s1 = x1 * ctx.itw + quad.t1 = y1 * ctx.ith + } + } + + x^ += f32(int(f32(glyph.xadvance) / 10 + 0.5)) +} + +// init text iter struct with settings +TextIterInit :: proc( + ctx: ^FontContext, + x: f32, + y: f32, + text: string, +) -> (res: TextIter) { + state := __getState(ctx) + res.font = __getFont(ctx, state.font) + res.isize = i16(f32(state.size) * 10) + res.iblur = i16(state.blur) + res.scale = __getPixelHeightScale(res.font, f32(res.isize) / 10) + + // align horizontally + x := x + y := y + switch state.ah { + case .LEFT: {} + case .CENTER: { + width := TextBounds(ctx, text, x, y, nil) + x = math.round(x - width * 0.5) + } + case .RIGHT: { + width := TextBounds(ctx, text, x, y, nil) + x -= width + } + } + + // align vertically + y = math.round(y + __getVerticalAlign(ctx, res.font, state.av, res.isize)) + + // set positions + res.x = x + res.nextx = x + res.y = y + res.nexty = y + res.previousGlyphIndex = -1 + res.spacing = state.spacing + res.text = text + + res.str = 0 + res.next = 0 + res.end = len(text) + + return +} + +// step through each codepoint +TextIterNext :: proc( + ctx: ^FontContext, + iter: ^TextIter, + quad: ^Quad, +) -> (ok: bool) { + str := iter.next + iter.str = iter.next + + for str < iter.end { + defer str += 1 + + if __decutf8(&iter.utf8state, &iter.codepoint, iter.text[str]) { + iter.x = iter.nextx + iter.y = iter.nexty + iter.codepointCount += 1 + glyph := __getGlyph(ctx, iter.font, iter.codepoint, iter.isize, iter.iblur) + + if glyph != nil { + __getQuad(ctx, iter.font, iter.previousGlyphIndex, glyph, iter.scale, iter.spacing, &iter.nextx, &iter.nexty, quad) + } + + iter.previousGlyphIndex = glyph == nil ? -1 : glyph.index + ok = true + break + } + } + + iter.next = str + return +} + +// width of a text line, optionally the full rect +TextBounds :: proc( + ctx: ^FontContext, + text: string, + x: f32 = 0, + y: f32 = 0, + bounds: ^[4]f32 = nil, +) -> f32 { + state := __getState(ctx) + isize := i16(state.size * 10) + iblur := i16(state.blur) + font := __getFont(ctx, state.font) + + // bunch of state + x := x + y := y + minx := x + maxx := x + miny := y + maxy := y + start_x := x + + // iterate + scale := __getPixelHeightScale(font, f32(isize) / 10) + previousGlyphIndex: Glyph_Index = -1 + quad: Quad + utf8state: rune + codepoint: rune + for byte_offset in 0.. maxx { + maxx = quad.x1 + } + + if ctx.location == .TOPLEFT { + if quad.y0 < miny { + miny = quad.y0 + } + if quad.y1 > maxy { + maxy = quad.y1 + } + } else if ctx.location == .BOTTOMLEFT { + if quad.y1 < miny { + miny = quad.y1 + } + if quad.y0 > maxy { + maxy = quad.y0 + } + } + } + + previousGlyphIndex = glyph == nil ? -1 : glyph.index + } + } + + // horizontal alignment + advance := x - start_x + switch state.ah { + case .LEFT: {} + case .CENTER: { + minx -= advance * 0.5 + maxx -= advance * 0.5 + } + case .RIGHT: { + minx -= advance + maxx -= advance + } + } + + if bounds != nil { + bounds^ = { minx, miny, maxx, maxy } + } + + return advance +} + +VerticalMetrics :: proc( + ctx: ^FontContext, +) -> (ascender, descender, lineHeight: f32) { + state := __getState(ctx) + isize := i16(state.size * 10.0) + font := __getFont(ctx, state.font) + ascender = font.ascender * f32(isize / 10) + descender = font.descender * f32(isize / 10) + lineHeight = font.lineHeight * f32(isize / 10) + return +} + +// reset to single state +BeginState :: proc(using ctx: ^FontContext) { + state_count = 0 + PushState(ctx) + ClearState(ctx) +} + +// checks for texture updates after potential __getGlyph calls +EndState :: proc(using ctx: ^FontContext) { + // check for texture update + if dirtyRect[0] < dirtyRect[2] && dirtyRect[1] < dirtyRect[3] { + if callbackUpdate != nil { + callbackUpdate(userData, dirtyRect, raw_data(textureData)) + } + + __dirtyRectReset(ctx) + } +} \ No newline at end of file diff --git a/vendor/nanovg/gl/frag.glsl b/vendor/nanovg/gl/frag.glsl new file mode 100644 index 000000000..423214c9b --- /dev/null +++ b/vendor/nanovg/gl/frag.glsl @@ -0,0 +1,123 @@ +#ifdef GL_ES +#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3) + precision highp float; +#else + precision mediump float; +#endif +#endif +#ifdef NANOVG_GL3 +#ifdef USE_UNIFORMBUFFER + layout(std140) uniform frag { + mat3 scissorMat; + mat3 paintMat; + vec4 innerCol; + vec4 outerCol; + vec2 scissorExt; + vec2 scissorScale; + vec2 extent; + float radius; + float feather; + float strokeMult; + float strokeThr; + int texType; + int type; + }; +#else // NANOVG_GL3 && !USE_UNIFORMBUFFER + uniform vec4 frag[UNIFORMARRAY_SIZE]; +#endif + uniform sampler2D tex; + in vec2 ftcoord; + in vec2 fpos; + out vec4 outColor; +#else // !NANOVG_GL3 + uniform vec4 frag[UNIFORMARRAY_SIZE]; + uniform sampler2D tex; + varying vec2 ftcoord; + varying vec2 fpos; +#endif +#ifndef USE_UNIFORMBUFFER + #define scissorMat mat3(frag[0].xyz, frag[1].xyz, frag[2].xyz) + #define paintMat mat3(frag[3].xyz, frag[4].xyz, frag[5].xyz) + #define innerCol frag[6] + #define outerCol frag[7] + #define scissorExt frag[8].xy + #define scissorScale frag[8].zw + #define extent frag[9].xy + #define radius frag[9].z + #define feather frag[9].w + #define strokeMult frag[10].x + #define strokeThr frag[10].y + #define texType int(frag[10].z) + #define type int(frag[10].w) +#endif + +float sdroundrect(vec2 pt, vec2 ext, float rad) { + vec2 ext2 = ext - vec2(rad,rad); + vec2 d = abs(pt) - ext2; + return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rad; +} + +// Scissoring +float scissorMask(vec2 p) { + vec2 sc = (abs((scissorMat * vec3(p,1.0)).xy) - scissorExt); + sc = vec2(0.5,0.5) - sc * scissorScale; + return clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0); +} +#ifdef EDGE_AA +// Stroke - from [0..1] to clipped pyramid, where the slope is 1px. +float strokeMask() { + return min(1.0, (1.0-abs(ftcoord.x*2.0-1.0))*strokeMult) * min(1.0, ftcoord.y); +} +#endif + +void main(void) { + vec4 result; + float scissor = scissorMask(fpos); +#ifdef EDGE_AA + float strokeAlpha = strokeMask(); + if (strokeAlpha < strokeThr) discard; +#else + float strokeAlpha = 1.0; +#endif + if (type == 0) { // Gradient + // Calculate gradient color using box gradient + vec2 pt = (paintMat * vec3(fpos,1.0)).xy; + float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0); + vec4 color = mix(innerCol,outerCol,d); + // Combine alpha + color *= strokeAlpha * scissor; + result = color; + } else if (type == 1) { // Image + // Calculate color fron texture + vec2 pt = (paintMat * vec3(fpos,1.0)).xy / extent; +#ifdef NANOVG_GL3 + vec4 color = texture(tex, pt); +#else + vec4 color = texture2D(tex, pt); +#endif + if (texType == 1) color = vec4(color.xyz*color.w,color.w); + if (texType == 2) color = vec4(color.x); + // Apply color tint and alpha. + color *= innerCol; + // Combine alpha + color *= strokeAlpha * scissor; + result = color; + } else if (type == 2) { // Stencil fill + result = vec4(1,1,1,1); + } else if (type == 3) { // Textured tris +#ifdef NANOVG_GL3 + vec4 color = texture(tex, ftcoord); +#else + vec4 color = texture2D(tex, ftcoord); +#endif + if (texType == 1) color = vec4(color.xyz*color.w,color.w); + if (texType == 2) color = vec4(color.x); + color *= scissor; + result = color * innerCol; + } +#ifdef NANOVG_GL3 + outColor = result; +#else + gl_FragColor = result; +#endif +} \ No newline at end of file diff --git a/vendor/nanovg/gl/gl.odin b/vendor/nanovg/gl/gl.odin new file mode 100644 index 000000000..1f4d8ceb0 --- /dev/null +++ b/vendor/nanovg/gl/gl.odin @@ -0,0 +1,1452 @@ +package nanovg_gl + +import "core:log" +import "core:strings" +import "core:mem" +import "core:math" +import "core:fmt" +import gl "vendor:OpenGL" +import nvg "../../nanovg" + +Color :: nvg.Color +Vertex :: nvg.Vertex +ImageFlags :: nvg.ImageFlags +TextureType :: nvg.Texture +Paint :: nvg.Paint +ScissorT :: nvg.ScissorT + +CreateFlag :: enum { + // Flag indicating if geometry based anti-aliasing is used (may not be needed when using MSAA). + ANTI_ALIAS, + // Flag indicating if strokes should be drawn using stencil buffer. The rendering will be a little + // slower, but path overlaps (i.e. self-intersecting or sharp turns) will be drawn just once. + STENCIL_STROKES, + // additional debug checks + DEBUG, +} +CreateFlags :: bit_set[CreateFlag] + +FRAG_BINDING :: 0 +USE_STATE_FILTER :: true +GL_UNIFORMARRAY_SIZE :: 11 + +UniformLoc :: enum { + VIEW_SIZE, + TEX, + FRAG, +} + +ShaderType :: enum i32 { + FILL_GRAD, + FILL_IMG, + SIMPLE, + IMG, +} + +Shader :: struct { + prog: u32, + frag: u32, + vert: u32, + loc: [UniformLoc]i32, +} + +Texture :: struct { + id: int, + tex: u32, + width, height: int, + type: TextureType, + flags: ImageFlags, +} + +Blend :: struct { + src_RGB: u32, + dst_RGB: u32, + src_alpha: u32, + dst_alpha: u32, +} + +CallType :: enum { + NONE, + FILL, + CONVEX_FILL, + STROKE, + TRIANGLES, +} + +Call :: struct { + type: CallType, + image: int, + pathOffset: int, + pathCount: int, + triangleOffset: int, + triangleCount: int, + uniformOffset: int, + blendFunc: Blend, +} + +Path :: struct { + fillOffset: int, + fillCount: int, + strokeOffset: int, + strokeCount: int, +} + +when GL2_IMPLEMENTATION { + FragUniforms :: struct #raw_union { + using _: struct { + scissorMat: [12]f32, // matrices are actually 3 vec4s + paintMat: [12]f32, + innerColor: Color, + outerColor: Color, + scissorExt: [2]f32, + scissorScale: [2]f32, + extent: [2]f32, + radius: f32, + feather: f32, + strokeMult: f32, + strokeThr: f32, + texType: i32, + type: ShaderType, + }, + uniform_array: [GL_UNIFORMARRAY_SIZE][4]f32, + } +} else { + FragUniforms :: struct #packed { + scissorMat: [12]f32, // matrices are actually 3 vec4s + paintMat: [12]f32, + innerColor: Color, + outerColor: Color, + scissorExt: [2]f32, + scissorScale: [2]f32, + extent: [2]f32, + radius: f32, + feather: f32, + strokeMult: f32, + strokeThr: f32, + texType: i32, + type: ShaderType, + } +} + +GL2_IMPLEMENTATION :: false +GL3_IMPLEMENTATION :: true +GLES2_IMPLEMENTATION :: false +GLES3_IMPLEMENTATION :: false + +when GL2_IMPLEMENTATION { + GL2 :: true + GL3 :: false + GLES2 :: false + GLES3 :: false + GL_IMPLEMENTATION :: true + GL_USE_UNIFORMBUFFER :: false +} else when GL3_IMPLEMENTATION { + GL2 :: false + GL3 :: true + GLES2 :: false + GLES3 :: false + GL_IMPLEMENTATION :: true + GL_USE_UNIFORMBUFFER :: true +} else when GLES2_IMPLEMENTATION { + GL2 :: false + GL3 :: false + GLES2 :: true + GLES3 :: false + GL_IMPLEMENTATION :: true + GL_USE_UNIFORMBUFFER :: false +} else when GLES3_IMPLEMENTATION { + GL2 :: false + GL3 :: false + GLES2 :: false + GLES3 :: true + GL_IMPLEMENTATION :: true + GL_USE_UNIFORMBUFFER :: false +} + +Context :: struct { + shader: Shader, + textures: [dynamic]Texture, + view: [2]f32, + textureId: int, + + vertBuf: u32, + vertArr: u32, // GL3 + fragBuf: u32, // USE_UNIFORMBUFFER + fragSize: int, + flags: CreateFlags, + + // Per frame buffers + calls: [dynamic]Call, + paths: [dynamic]Path, + verts: [dynamic]Vertex, + uniforms: [dynamic]byte, + + // cached state used for state filter + boundTexture: u32, + stencilMask: u32, + stencilFunc: u32, + stencilFuncRef: i32, + stencilFuncMask: u32, + blendFunc: Blend, + + dummyTex: int, +} + +__nearestPow2 :: proc(num: uint) -> uint { + n := num > 0 ? num - 1 : 0 + n |= n >> 1 + n |= n >> 2 + n |= n >> 4 + n |= n >> 8 + n |= n >> 16 + n += 1 + return n +} + +__bindTexture :: proc(ctx: ^Context, tex: u32) { + when USE_STATE_FILTER { + if ctx.boundTexture != tex { + ctx.boundTexture = tex + gl.BindTexture(gl.TEXTURE_2D, tex) + } + } else { + gl.BindTexture(gl.TEXTURE_2D, tex) + } +} + +__stencilMask :: proc(ctx: ^Context, mask: u32) { + when USE_STATE_FILTER { + if ctx.stencilMask != mask { + ctx.stencilMask = mask + gl.StencilMask(mask) + } + } else { + gl.StencilMask(mask) + } +} + +__stencilFunc :: proc(ctx: ^Context, func: u32, ref: i32, mask: u32) { + when USE_STATE_FILTER { + if ctx.stencilFunc != func || + ctx.stencilFuncRef != ref || + ctx.stencilFuncMask != mask { + ctx.stencilFunc = func + ctx.stencilFuncRef = ref + ctx.stencilFuncMask = mask + gl.StencilFunc(func, ref, mask) + } + } else { + gl.StencilFunc(func, ref, mask) + } +} + +__blendFuncSeparate :: proc(ctx: ^Context, blend: ^Blend) { + when USE_STATE_FILTER { + if ctx.blendFunc != blend^ { + ctx.blendFunc = blend^ + gl.BlendFuncSeparate(blend.src_RGB, blend.dst_RGB, blend.src_alpha, blend.dst_alpha) + } + } else { + gl.BlendFuncSeparate(blend.src_RGB, blend.dst_RGB, blend.src_alpha, blend.dst_alpha) + } +} + +__allocTexture :: proc(ctx: ^Context) -> (tex: ^Texture) { + for texture in &ctx.textures { + if texture.id == 0 { + tex = &texture + break + } + } + + if tex == nil { + append(&ctx.textures, Texture {}) + tex = &ctx.textures[len(ctx.textures) - 1] + } + + tex^ = {} + ctx.textureId += 1 + tex.id = ctx.textureId + + return +} + +__findTexture :: proc(ctx: ^Context, id: int) -> ^Texture { + for texture in &ctx.textures { + if texture.id == id { + return &texture + } + } + + return nil +} + +__deleteTexture :: proc(ctx: ^Context, id: int) -> bool { + for texture, i in &ctx.textures { + if texture.id == id { + if texture.tex != 0 && (.NO_DELETE not_in texture.flags) { + gl.DeleteTextures(1, &texture.tex) + } + + ctx.textures[i] = {} + return true + } + } + + return false +} + +__deleteShader :: proc(shader: ^Shader) { + if shader.prog != 0 { + gl.DeleteProgram(shader.prog) + } + + if shader.vert != 0 { + gl.DeleteShader(shader.vert) + } + + if shader.frag != 0 { + gl.DeleteShader(shader.frag) + } +} + +__getUniforms :: proc(shader: ^Shader) { + shader.loc[.VIEW_SIZE] = gl.GetUniformLocation(shader.prog, "viewSize") + shader.loc[.TEX] = gl.GetUniformLocation(shader.prog, "tex") + + when GL_USE_UNIFORMBUFFER { + shader.loc[.FRAG] = i32(gl.GetUniformBlockIndex(shader.prog, "frag")) + } else { + shader.loc[.FRAG] = gl.GetUniformLocation(shader.prog, "frag") + } +} + +vert_shader := #load("vert.glsl") +frag_shader := #load("frag.glsl") + +__renderCreate :: proc(uptr: rawptr) -> bool { + ctx := cast(^Context) uptr + + // just build the string at runtime + builder := strings.builder_make(0, 512, context.temp_allocator) + + when GL2 { + strings.write_string(&builder, "#define NANOVG_GL2 1\n") + } else when GL3 { + strings.write_string(&builder, "#version 150 core\n#define NANOVG_GL3 1\n") + } else when GLES2 { + strings.write_string(&builder, "#version 100\n#define NANOVG_GL2 1\n") + } else when GLES3 { + strings.write_string(&builder, "#version 300 es\n#define NANOVG_GL3 1\n") + } + + when GL_USE_UNIFORMBUFFER { + strings.write_string(&builder, "#define USE_UNIFORMBUFFER 1\n") + } else { + strings.write_string(&builder, "#define UNIFORMARRAY_SIZE 11\n") + } + + __checkError(ctx, "init") + + shader_header := strings.to_string(builder) + anti: string = .ANTI_ALIAS in ctx.flags ? "#define EDGE_AA 1\n" : " " + if !__createShader( + &ctx.shader, + shader_header, + anti, + string(vert_shader), + string(frag_shader), + ) { + return false + } + + __checkError(ctx, "uniform locations") + __getUniforms(&ctx.shader) + + when GL3 { + gl.GenVertexArrays(1, &ctx.vertArr) + } + + gl.GenBuffers(1, &ctx.vertBuf) + align := i32(4) + + when GL_USE_UNIFORMBUFFER { + // Create UBOs + gl.UniformBlockBinding(ctx.shader.prog, u32(ctx.shader.loc[.FRAG]), FRAG_BINDING) + gl.GenBuffers(1, &ctx.fragBuf) + gl.GetIntegerv(gl.UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align) + } + + ctx.fragSize = int(size_of(FragUniforms) + align - size_of(FragUniforms) % align) + // ctx.fragSize = size_of(FragUniforms) + ctx.dummyTex = __renderCreateTexture(ctx, .Alpha, 1, 1, {}, nil) + + __checkError(ctx, "create done") + + gl.Finish() + + return true +} + +__renderCreateTexture :: proc( + uptr: rawptr, + type: TextureType, + w, h: int, + imageFlags: ImageFlags, + data: []byte, +) -> int { + ctx := cast(^Context) uptr + tex := __allocTexture(ctx) + imageFlags := imageFlags + + if tex == nil { + return 0 + } + + when GLES2 { + if __nearestPow2(uint(w)) != uint(w) || __nearestPow2(uint(h)) != uint(h) { + // No repeat + if (.REPEAT_X in imageFlags) || (.REPEAT_Y in imageFlags) { + log.errorf("Repeat X/Y is not supported for non power-of-two textures (%d x %d)\n", w, h) + excl(&imageFlags, ImageFlags { .REPEAT_X, .REPEAT_Y }) + } + + // No mips. + if .GENERATE_MIPMAPS in imageFlags { + log.errorf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h); + excl(&imageFlags, nvg.Image_Flag.GENERATE_MIPMAPS) + } + } + } + + gl.GenTextures(1, &tex.tex) + tex.width = w + tex.height = h + tex.type = type + tex.flags = imageFlags + __bindTexture(ctx, tex.tex) + + gl.PixelStorei(gl.UNPACK_ALIGNMENT,1) + + when GLES2 { + gl.PixelStorei(gl.UNPACK_ROW_LENGTH, i32(tex.width)) + gl.PixelStorei(gl.UNPACK_SKIP_PIXELS, 0) + gl.PixelStorei(gl.UNPACK_SKIP_ROWS, 0) + } + + when GL2 { + if .GENERATE_MIPMAPS in imageFlags { + gl.TexParameteri(gl.TEXTURE_2D, GENERATE_MIPMAP, 1) + } + } + + if type == .RGBA { + gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, i32(w), i32(h), 0, gl.RGBA, gl.UNSIGNED_BYTE, raw_data(data)) + } else { + when GLES2 || GL2 { + // TODO missing in odin + gl.TexImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, w, h, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, data) + } else when GLES3 { + gl.TexImage2D(gl.TEXTURE_2D, 0, gl.R8, i32(w), i32(h), 0, gl.RED, gl.UNSIGNED_BYTE, raw_data(data)) + } else { + gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RED, i32(w), i32(h), 0, gl.RED, gl.UNSIGNED_BYTE, raw_data(data)) + } + } + + if .GENERATE_MIPMAPS in imageFlags { + if .NEAREST in imageFlags { + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_NEAREST) + } else { + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR) + } + } else { + if .NEAREST in imageFlags { + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) + } else { + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) + } + } + + if .NEAREST in imageFlags { + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) + } else { + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) + } + + if .REPEAT_X in imageFlags { + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT) + } else { + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) + } + + if .REPEAT_Y in imageFlags { + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT) + } else { + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) + } + + gl.PixelStorei(gl.UNPACK_ALIGNMENT, 4) + + when GLES2 { + gl.PixelStorei(gl.UNPACK_ROW_LENGTH, 0) + gl.PixelStorei(gl.UNPACK_SKIP_PIXELS, 0) + gl.PixelStorei(gl.UNPACK_SKIP_ROWS, 0) + } + + // The new way to build mipmaps on GLES and GL3 + when !GL2 { + if .GENERATE_MIPMAPS in imageFlags { + gl.GenerateMipmap(gl.TEXTURE_2D) + } + } + + __checkError(ctx, "create tex") + __bindTexture(ctx, 0) + + return tex.id +} + +__checkError :: proc(ctx: ^Context, str: string) { + if .DEBUG in ctx.flags { + err := gl.GetError() + + if err != gl.NO_ERROR { + log.errorf("FOUND ERROR %08x:\n\t%s\n", err, str) + } + } +} + +__checkProgramError :: proc(prog: u32) { + status: i32 + gl.GetProgramiv(prog, gl.LINK_STATUS, &status) + length: i32 + gl.GetProgramiv(prog, gl.INFO_LOG_LENGTH, &length) + + if status == 0 { + temp := make([]byte, length) + defer delete(temp) + + gl.GetProgramInfoLog(prog, length, nil, raw_data(temp)) + log.errorf("Program Error:\n%s\n", string(temp[:length])) + } +} + +__checkShaderError :: proc(shader: u32, type: string) { + status: i32 + gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status) + length: i32 + gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &length) + + if status == 0 { + temp := make([]byte, length) + defer delete(temp) + + gl.GetShaderInfoLog(shader, length, nil, raw_data(temp)) + log.errorf("Shader error:\n%s\n", string(temp[:length])) + } +} + +// TODO good case for or_return +__createShader :: proc( + shader: ^Shader, + header: string, + opts: string, + vshader: string, + fshader: string, +) -> bool { + shader^ = {} + str: [3]cstring + lengths: [3]i32 + str[0] = cstring(raw_data(header)) + str[1] = cstring(raw_data(opts)) + + lengths[0] = i32(len(header)) + lengths[1] = i32(len(opts)) + + prog := gl.CreateProgram() + vert := gl.CreateShader(gl.VERTEX_SHADER) + frag := gl.CreateShader(gl.FRAGMENT_SHADER) + + // vert shader + str[2] = cstring(raw_data(vshader)) + lengths[2] = i32(len(vshader)) + gl.ShaderSource(vert, 3, &str[0], &lengths[0]) + gl.CompileShader(vert) + __checkShaderError(vert, "vert") + + // fragment shader + str[2] = cstring(raw_data(fshader)) + lengths[2] = i32(len(fshader)) + gl.ShaderSource(frag, 3, &str[0], &lengths[0]) + gl.CompileShader(frag) + __checkShaderError(frag, "frag") + + gl.AttachShader(prog, vert) + gl.AttachShader(prog, frag) + + gl.BindAttribLocation(prog, 0, "vertex") + gl.BindAttribLocation(prog, 1, "tcoord") + + gl.LinkProgram(prog) + __checkProgramError(prog) + + shader.prog = prog + shader.vert = vert + shader.frag = frag + return true +} + +__renderDeleteTexture :: proc(uptr: rawptr, image: int) -> bool { + ctx := cast(^Context) uptr + return __deleteTexture(ctx, image) +} + +__renderUpdateTexture :: proc( + uptr: rawptr, + image: int, + x, y: int, + w, h: int, + data: []byte, +) -> bool { + ctx := cast(^Context) uptr + tex := __findTexture(ctx, image) + + if tex == nil { + return false + } + + __bindTexture(ctx, tex.tex) + + gl.PixelStorei(gl.UNPACK_ALIGNMENT,1) + + x := x + w := w + data := data + + when GLES2 { + gl.PixelStorei(gl.UNPACK_ROW_LENGTH, i32(tex.width)) + gl.PixelStorei(gl.UNPACK_SKIP_PIXELS, i32(x)) + gl.PixelStorei(gl.UNPACK_SKIP_ROWS, i32(y)) + } else { + // No support for all of skip, need to update a whole row at a time. + if tex.type == .RGBA { + data = data[y * tex.width * 4:] + } else { + data = data[y * tex.width:] + } + + x = 0 + w = tex.width + } + + if tex.type == .RGBA { + gl.TexSubImage2D(gl.TEXTURE_2D, 0, i32(x), i32(y), i32(w), i32(h), gl.RGBA, gl.UNSIGNED_BYTE, raw_data(data)) + } else { + when GLES2 || GL2 { + // TODO is missing in odin + // gl.TexSubImage2D(gl.TEXTURE_2D, 0, i32(x), i32(y), i32(w), i32(h), gl.LUMINANCE, gl.UNSIGNED_BYTE, raw_data(data)) + } else { + gl.TexSubImage2D(gl.TEXTURE_2D, 0, i32(x), i32(y), i32(w), i32(h), gl.RED, gl.UNSIGNED_BYTE, raw_data(data)) + } + } + + gl.PixelStorei(gl.UNPACK_ALIGNMENT, 4) + + when GLES2 { + gl.PixelStorei(gl.UNPACK_ROW_LENGTH, 0) + gl.PixelStorei(gl.UNPACK_SKIP_PIXELS, 0) + gl.PixelStorei(gl.UNPACK_SKIP_ROWS, 0) + } + + __bindTexture(ctx, 0) + + return true +} + +__renderGetTextureSize :: proc(uptr: rawptr, image: int, w, h: ^int) -> bool { + ctx := cast(^Context) uptr + tex := __findTexture(ctx, image) + + if tex == nil { + return false + } + + w^ = tex.width + h^ = tex.height + return true +} + +__xformToMat3x4 :: proc(m3: ^[12]f32, t: [6]f32) { + m3[0] = t[0] + m3[1] = t[1] + m3[2] = 0 + m3[3] = 0 + m3[4] = t[2] + m3[5] = t[3] + m3[6] = 0 + m3[7] = 0 + m3[8] = t[4] + m3[9] = t[5] + m3[10] = 1 + m3[11] = 0 +} + +__premulColor :: proc(c: Color) -> (res: Color) { + res = c + res.r *= c.a + res.g *= c.a + res.b *= c.a + return +} + +__convertPaint :: proc( + ctx: ^Context, + frag: ^FragUniforms, + paint: ^Paint, + scissor: ^ScissorT, + width: f32, + fringe: f32, + strokeThr: f32, +) -> bool { + invxform: [6]f32 + frag^ = {} + frag.innerColor = __premulColor(paint.innerColor) + frag.outerColor = __premulColor(paint.outerColor) + + if scissor.extent[0] < -0.5 || scissor.extent[1] < -0.5 { + frag.scissorMat = {} + frag.scissorExt[0] = 1.0 + frag.scissorExt[1] = 1.0 + frag.scissorScale[0] = 1.0 + frag.scissorScale[1] = 1.0 + } else { + nvg.TransformInverse(&invxform, scissor.xform) + __xformToMat3x4(&frag.scissorMat, invxform) + frag.scissorExt[0] = scissor.extent[0] + frag.scissorExt[1] = scissor.extent[1] + frag.scissorScale[0] = math.sqrt(scissor.xform[0]*scissor.xform[0] + scissor.xform[2]*scissor.xform[2]) / fringe + frag.scissorScale[1] = math.sqrt(scissor.xform[1]*scissor.xform[1] + scissor.xform[3]*scissor.xform[3]) / fringe + } + + frag.extent = paint.extent + frag.strokeMult = (width * 0.5 + fringe * 0.5) / fringe + frag.strokeThr = strokeThr + + if paint.image != 0 { + tex := __findTexture(ctx, paint.image) + + if tex == nil { + return false + } + + // TODO maybe inversed? + if .FLIP_Y in tex.flags { + m1: [6]f32 + m2: [6]f32 + nvg.TransformTranslate(&m1, 0.0, frag.extent[1] * 0.5) + nvg.TransformMultiply(&m1, paint.xform) + nvg.TransformScale(&m2, 1.0, -1.0) + nvg.TransformMultiply(&m2, m1) + nvg.TransformTranslate(&m1, 0.0, -frag.extent[1] * 0.5) + nvg.TransformMultiply(&m1, m2) + nvg.TransformInverse(&invxform, m1) + } else { + nvg.TransformInverse(&invxform, paint.xform) + } + + frag.type = .FILL_IMG + + when GL_USE_UNIFORMBUFFER { + if tex.type == .RGBA { + frag.texType = (.PREMULTIPLIED in tex.flags) ? 0 : 1 + } else { + frag.texType = 2 + } + } else { + if tex.type == .RGBA { + frag.texType = (.PREMULTIPLIED in tex.flags) ? 0.0 : 1.0 + } else { + frag.texType = 2.0 + } + } + } else { + frag.type = .FILL_GRAD + frag.radius = paint.radius + frag.feather = paint.feather + nvg.TransformInverse(&invxform, paint.xform) + } + + __xformToMat3x4(&frag.paintMat, invxform) + + return true +} + +__setUniforms :: proc(ctx: ^Context, uniformOffset: int, image: int) { + when GL_USE_UNIFORMBUFFER { + gl.BindBufferRange(gl.UNIFORM_BUFFER, FRAG_BINDING, ctx.fragBuf, uniformOffset, size_of(FragUniforms)) + } else { + frag := __fragUniformPtr(ctx, uniformOffset) + gl.Uniform4fv(ctx.shader.loc[.FRAG], GL_UNIFORMARRAY_SIZE, cast(^f32) frag) + } + + __checkError(ctx, "uniform4") + + tex: ^Texture + if image != 0 { + tex = __findTexture(ctx, image) + } + + // If no image is set, use empty texture + if tex == nil { + tex = __findTexture(ctx, ctx.dummyTex) + } + + __bindTexture(ctx, tex != nil ? tex.tex : 0) + __checkError(ctx, "tex paint tex") +} + +__renderViewport :: proc(uptr: rawptr, width, height, devicePixelRatio: f32) { + ctx := cast(^Context) uptr + ctx.view[0] = width + ctx.view[1] = height +} + +__fill :: proc(ctx: ^Context, call: ^Call) { + paths := ctx.paths[call.pathOffset:] + + // Draw shapes + gl.Enable(gl.STENCIL_TEST) + __stencilMask(ctx, 0xff) + __stencilFunc(ctx, gl.ALWAYS, 0, 0xff) + gl.ColorMask(gl.FALSE, gl.FALSE, gl.FALSE, gl.FALSE) + + // set bindpoint for solid loc + __setUniforms(ctx, call.uniformOffset, 0) + __checkError(ctx, "fill simple") + + gl.StencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.INCR_WRAP) + gl.StencilOpSeparate(gl.BACK, gl.KEEP, gl.KEEP, gl.DECR_WRAP) + gl.Disable(gl.CULL_FACE) + for i in 0.. 0 { + gl.DrawArrays(gl.TRIANGLE_STRIP, i32(paths[i].strokeOffset), i32(paths[i].strokeCount)) + } + } +} + +__stroke :: proc(ctx: ^Context, call: ^Call) { + paths := ctx.paths[call.pathOffset:] + + if .STENCIL_STROKES in ctx.flags { + gl.Enable(gl.STENCIL_TEST) + __stencilMask(ctx, 0xff) + + // Fill the stroke base without overlap + __stencilFunc(ctx, gl.EQUAL, 0x0, 0xff) + gl.StencilOp(gl.KEEP, gl.KEEP, gl.INCR) + __setUniforms(ctx, call.uniformOffset + ctx.fragSize, call.image) + __checkError(ctx, "stroke fill 0") + + for i in 0.. Blend { + table := BLEND_FACTOR_TABLE + blend := Blend { + table[op.srcRGB], + table[op.dstRGB], + table[op.srcAlpha], + table[op.dstAlpha], + } + return blend +} + +__renderFlush :: proc(uptr: rawptr) { + ctx := cast(^Context) uptr + + if len(ctx.calls) > 0 { + // Setup require GL state. + gl.UseProgram(ctx.shader.prog) + + gl.Enable(gl.CULL_FACE) + gl.CullFace(gl.BACK) + gl.FrontFace(gl.CCW) + gl.Enable(gl.BLEND) + gl.Disable(gl.DEPTH_TEST) + gl.Disable(gl.SCISSOR_TEST) + gl.ColorMask(gl.TRUE, gl.TRUE, gl.TRUE, gl.TRUE) + gl.StencilMask(0xffffffff) + gl.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP) + gl.StencilFunc(gl.ALWAYS, 0, 0xffffffff) + gl.ActiveTexture(gl.TEXTURE0) + gl.BindTexture(gl.TEXTURE_2D, 0) + + when USE_STATE_FILTER { + ctx.boundTexture = 0 + ctx.stencilMask = 0xffffffff + ctx.stencilFunc = gl.ALWAYS + ctx.stencilFuncRef = 0 + ctx.stencilFuncMask = 0xffffffff + ctx.blendFunc.src_RGB = gl.INVALID_ENUM + ctx.blendFunc.src_alpha = gl.INVALID_ENUM + ctx.blendFunc.dst_RGB = gl.INVALID_ENUM + ctx.blendFunc.dst_alpha = gl.INVALID_ENUM + } + + when GL_USE_UNIFORMBUFFER { + // Upload ubo for frag shaders + gl.BindBuffer(gl.UNIFORM_BUFFER, ctx.fragBuf) + gl.BufferData(gl.UNIFORM_BUFFER, len(ctx.uniforms), raw_data(ctx.uniforms), gl.STREAM_DRAW) + } + + // Upload vertex data + when GL3 { + gl.BindVertexArray(ctx.vertArr) + } + + gl.BindBuffer(gl.ARRAY_BUFFER, ctx.vertBuf) + gl.BufferData(gl.ARRAY_BUFFER, len(ctx.verts) * size_of(Vertex), raw_data(ctx.verts), gl.STREAM_DRAW) + gl.EnableVertexAttribArray(0) + gl.EnableVertexAttribArray(1) + gl.VertexAttribPointer(0, 2, gl.FLOAT, gl.FALSE, size_of(Vertex), 0) + gl.VertexAttribPointer(1, 2, gl.FLOAT, gl.FALSE, size_of(Vertex), 2 * size_of(f32)) + + // Set view and texture just once per frame. + gl.Uniform1i(ctx.shader.loc[.TEX], 0) + gl.Uniform2fv(ctx.shader.loc[.VIEW_SIZE], 1, &ctx.view[0]) + + when GL_USE_UNIFORMBUFFER { + gl.BindBuffer(gl.UNIFORM_BUFFER, ctx.fragBuf) + } + + for i in 0.. (count: int) { + for i in 0.. ^Call { + append(&ctx.calls, Call {}) + return &ctx.calls[len(ctx.calls) - 1] +} + +// alloc paths and return the original start position +__allocPaths :: proc(ctx: ^Context, count: int) -> int { + old := len(ctx.paths) + resize(&ctx.paths, len(ctx.paths) + count) + return old +} + +// alloc verts and return the original start position +__allocVerts :: proc(ctx: ^Context, count: int) -> int { + old := len(ctx.verts) + resize(&ctx.verts, len(ctx.verts) + count) + return old +} + +// alloc uniforms and return the original start position +__allocFragUniforms :: proc(ctx: ^Context, count: int) -> int { + ret := len(ctx.uniforms) + resize(&ctx.uniforms, len(ctx.uniforms) + count * ctx.fragSize) + return ret +} + +// get frag uniforms from byte slice offset +__fragUniformPtr :: proc(ctx: ^Context, offset: int) -> ^FragUniforms { + return cast(^FragUniforms) &ctx.uniforms[offset] +} + +/////////////////////////////////////////////////////////// +// CALLBACKS +/////////////////////////////////////////////////////////// + +__renderFill :: proc( + uptr: rawptr, + paint: ^nvg.Paint, + compositeOperation: nvg.CompositeOperationState, + scissor: ^ScissorT, + fringe: f32, + bounds: [4]f32, + paths: []nvg.Path, +) { + ctx := cast(^Context) uptr + call := __allocCall(ctx) + + call.type = .FILL + call.triangleCount = 4 + call.pathOffset = __allocPaths(ctx, len(paths)) + call.pathCount = len(paths) + call.image = paint.image + call.blendFunc = __blendCompositeOperation(compositeOperation) + + if len(paths) == 1 && paths[0].convex { + call.type = .CONVEX_FILL + call.triangleCount = 0 + } + + // allocate vertices for all the paths + maxverts := __maxVertCount(paths) + call.triangleCount + offset := __allocVerts(ctx, maxverts) + + for i in 0.. 0 { + copy.fillOffset = offset + copy.fillCount = len(path.fill) + mem.copy(&ctx.verts[offset], &path.fill[0], size_of(Vertex) * len(path.fill)) + offset += len(path.fill) + } + + if len(path.stroke) > 0 { + copy.strokeOffset = offset + copy.strokeCount = len(path.stroke) + mem.copy(&ctx.verts[offset], &path.stroke[0], size_of(Vertex) * len(path.stroke)) + offset += len(path.stroke) + } + } + + // setup uniforms for draw calls + if call.type == .FILL { + // quad + call.triangleOffset = offset + quad := ctx.verts[call.triangleOffset:call.triangleOffset+4] + quad[0] = { bounds[2], bounds[3], 0.5, 1 } + quad[1] = { bounds[2], bounds[1], 0.5, 1 } + quad[2] = { bounds[0], bounds[3], 0.5, 1 } + quad[3] = { bounds[0], bounds[1], 0.5, 1 } + + // simple shader for stencil + call.uniformOffset = __allocFragUniforms(ctx, 2) + frag := __fragUniformPtr(ctx, call.uniformOffset) + frag^ = {} + frag.strokeThr = -1 + frag.type = .SIMPLE + + // fill shader + __convertPaint( + ctx, + __fragUniformPtr(ctx, call.uniformOffset + ctx.fragSize), + paint, + scissor, + fringe, + fringe, + -1, + ) + } else { + call.uniformOffset = __allocFragUniforms(ctx, 1) + // fill shader + __convertPaint( + ctx, + __fragUniformPtr(ctx, call.uniformOffset), + paint, + scissor, + fringe, + fringe, + -1, + ) + } +} + +__renderStroke :: proc( + uptr: rawptr, + paint: ^Paint, + compositeOperation: nvg.CompositeOperationState, + scissor: ^ScissorT, + fringe: f32, + strokeWidth: f32, + paths: []nvg.Path, +) { + ctx := cast(^Context) uptr + call := __allocCall(ctx) + + call.type = .STROKE + call.pathOffset = __allocPaths(ctx, len(paths)) + call.pathCount = len(paths) + call.image = paint.image + call.blendFunc = __blendCompositeOperation(compositeOperation) + + // allocate vertices for all the paths + maxverts := __maxVertCount(paths) + offset := __allocVerts(ctx, maxverts) + + for i in 0.. ^nvg.Context { + ctx := new(Context) + params: nvg.Params + params.renderCreate = __renderCreate + params.renderCreateTexture = __renderCreateTexture + params.renderDeleteTexture = __renderDeleteTexture + params.renderUpdateTexture = __renderUpdateTexture + params.renderGetTextureSize = __renderGetTextureSize + params.renderViewport = __renderViewport + params.renderCancel = __renderCancel + params.renderFlush = __renderFlush + params.renderFill = __renderFill + params.renderStroke = __renderStroke + params.renderTriangles = __renderTriangles + params.renderDelete = __renderDelete + params.userPtr = ctx + params.edgeAntiAlias = (.ANTI_ALIAS in flags) + ctx.flags = flags + return nvg.CreateInternal(params) +} + +Destroy :: proc(ctx: ^nvg.Context) { + nvg.DeleteInternal(ctx) +} + +CreateImageFromHandle :: proc(ctx: ^nvg.Context, textureId: u32, w, h: int, imageFlags: ImageFlags) -> int { + gctx := cast(^Context) ctx.params.userPtr + tex := __allocTexture(gctx) + tex.type = .RGBA + tex.tex = textureId + tex.flags = imageFlags + tex.width = w + tex.height = h + return tex.id +} + +ImageHandle :: proc(ctx: ^nvg.Context, textureId: int) -> u32 { + gctx := cast(^Context) ctx.params.userPtr + tex := __findTexture(gctx, textureId) + return tex.tex +} + +// framebuffer additional + +framebuffer :: struct { + ctx: ^nvg.Context, + fbo: u32, + rbo: u32, + texture: u32, + image: int, +} + +DEFAULT_FBO :: 100_000 +defaultFBO := i32(DEFAULT_FBO) + +// helper function to create GL frame buffer to render to +BindFramebuffer :: proc(fb: ^framebuffer) { + if defaultFBO == DEFAULT_FBO { + gl.GetIntegerv(gl.FRAMEBUFFER_BINDING, &defaultFBO) + } + gl.BindFramebuffer(gl.FRAMEBUFFER, fb != nil ? fb.fbo : u32(defaultFBO)) +} + +CreateFramebuffer :: proc(ctx: ^nvg.Context, w, h: int, imageFlags: ImageFlags) -> (fb: framebuffer) { + defaultFBO: i32 + defaultRBO: i32 + gl.GetIntegerv(gl.FRAMEBUFFER_BINDING, &defaultFBO) + gl.GetIntegerv(gl.RENDERBUFFER_BINDING, &defaultRBO) + + imageFlags := imageFlags + incl(&imageFlags, ImageFlags { .FLIP_Y, .PREMULTIPLIED }) + fb.image = nvg.CreateImageRGBA(ctx, w, h, imageFlags, nil) + fb.texture = ImageHandle(ctx, fb.image) + fb.ctx = ctx + + // frame buffer object + gl.GenFramebuffers(1, &fb.fbo) + gl.BindFramebuffer(gl.FRAMEBUFFER, fb.fbo) + + // render buffer object + gl.GenRenderbuffers(1, &fb.rbo) + gl.BindRenderbuffer(gl.RENDERBUFFER, fb.rbo) + gl.RenderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, i32(w), i32(h)) + + // combine all + gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fb.texture, 0) + gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, fb.rbo) + + if gl.CheckFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE { +// #ifdef gl.DEPTH24_STENCIL8 + // If gl.STENCIL_INDEX8 is not supported, try gl.DEPTH24_STENCIL8 as a fallback. + // Some graphics cards require a depth buffer along with a stencil. + gl.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH24_STENCIL8, i32(w), i32(h)) + gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fb.texture, 0) + gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, fb.rbo) + + if gl.CheckFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE { + fmt.eprintln("ERROR") + } +// #endif // gl.DEPTH24_STENCIL8 +// goto error + } + + gl.BindFramebuffer(gl.FRAMEBUFFER, u32(defaultFBO)) + gl.BindRenderbuffer(gl.RENDERBUFFER, u32(defaultRBO)) + return +} + +DeleteFramebuffer :: proc(fb: ^framebuffer) { + if fb == nil { + return + } + + if fb.fbo != 0 { + gl.DeleteFramebuffers(1, &fb.fbo) + } + + if fb.rbo != 0 { + gl.DeleteRenderbuffers(1, &fb.rbo) + } + + if fb.image >= 0 { + nvg.DeleteImage(fb.ctx, fb.image) + } + + fb.ctx = nil + fb.fbo = 0 + fb.rbo = 0 + fb.texture = 0 + fb.image = -1 +} \ No newline at end of file diff --git a/vendor/nanovg/gl/vert.glsl b/vendor/nanovg/gl/vert.glsl new file mode 100644 index 000000000..f937da09a --- /dev/null +++ b/vendor/nanovg/gl/vert.glsl @@ -0,0 +1,19 @@ +#ifdef NANOVG_GL3 + uniform vec2 viewSize; + in vec2 vertex; + in vec2 tcoord; + out vec2 ftcoord; + out vec2 fpos; +#else + uniform vec2 viewSize; + attribute vec2 vertex; + attribute vec2 tcoord; + varying vec2 ftcoord; + varying vec2 fpos; +#endif + +void main(void) { + ftcoord = tcoord; + fpos = vertex; + gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1); +} \ No newline at end of file diff --git a/vendor/nanovg/nanovg.odin b/vendor/nanovg/nanovg.odin new file mode 100644 index 000000000..db99ef5a4 --- /dev/null +++ b/vendor/nanovg/nanovg.odin @@ -0,0 +1,3503 @@ +package nanovg + +// TODO rename structs to old nanovg style! +// TODO rename enums to old nanovg style! + +import "core:mem" +import "core:runtime" +import "core:math" +import "core:fmt" +import "../fontstash" +import stbi "vendor:stb/image" + +AlignVertical :: fontstash.AlignVertical +AlignHorizontal :: fontstash.AlignHorizontal + +INIT_FONTIMAGE_SIZE :: 512 +MAX_FONTIMAGE_SIZE :: 2048 +MAX_FONTIMAGES :: 4 + +MAX_STATES :: 32 +INIT_COMMANDS_SIZE :: 256 +INIT_POINTS_SIZE :: 128 +INIT_PATH_SIZE :: 16 +INIT_VERTS_SIZE :: 26 +KAPPA :: 0.5522847493 + +Color :: [4]f32 +Matrix :: [6]f32 +Vertex :: [4]f32 // x,y,u,v + +ImageFlag :: enum { + GENERATE_MIPMAPS, + REPEAT_X, + REPEAT_Y, + FLIP_Y, + PREMULTIPLIED, + NEAREST, + NO_DELETE, +} +ImageFlags :: bit_set[ImageFlag] + +Paint :: struct { + xform: Matrix, + extent: [2]f32, + radius: f32, + feather: f32, + innerColor: Color, + outerColor: Color, + image: int, +} + +Winding :: enum { + CCW = 1, + CW, +} + +Solidity :: enum { + SOLID = 1, // CCW + HOLE, // CW +} + +LineCapType :: enum { + BUTT, + ROUND, + SQUARE, + BEVEL, + MITER, +} + +BlendFactor :: enum { + ZERO, + ONE, + SRC_COLOR, + ONE_MINUS_SRC_COLOR, + DST_COLOR, + ONE_MINUS_DST_COLOR, + SRC_ALPHA, + ONE_MINUS_SRC_ALPHA, + DST_ALPHA, + ONE_MINUS_DST_ALPHA, + SRC_ALPHA_SATURATE, +} + +CompositeOperation :: enum { + SOURCE_OVER, + SOURCE_IN, + SOURCE_OUT, + ATOP, + DESTINATION_OVER, + DESTINATION_IN, + DESTINATION_OUT, + DESTINATION_ATOP, + LIGHTER, + COPY, + XOR, +} + +CompositeOperationState :: struct { + srcRGB: BlendFactor, + dstRGB: BlendFactor, + srcAlpha: BlendFactor, + dstAlpha: BlendFactor, +} + +// render data structures + +Texture :: enum { + Alpha, + RGBA, +} + +ScissorT :: struct { + xform: Matrix, + extent: [2]f32, +} + +Commands :: enum { + MOVE_TO, + LINE_TO, + BEZIER_TO, + CLOSE, + WINDING, +} + +PointFlag :: enum { + CORNER, + LEFT, + BEVEL, + INNER_BEVEL, +} +PointFlags :: bit_set[PointFlag] + +Point :: struct { + x, y: f32, + dx, dy: f32, + len: f32, + dmx, dmy: f32, + flags: PointFlags, +} + +PathCache :: struct { + points: [dynamic]Point, + paths: [dynamic]Path, + verts: [dynamic]Vertex, + bounds: [4]f32, +} + +Path :: struct { + first: int, + count: int, + closed: bool, + nbevel: int, + fill: []Vertex, + stroke: []Vertex, + winding: Winding, + convex: bool, +} + +State :: struct { + compositeOperation: CompositeOperationState, + shapeAntiAlias: bool, + fill: Paint, + stroke: Paint, + strokeWidth: f32, + miterLimit: f32, + lineJoin: LineCapType, + lineCap: LineCapType, + alpha: f32, + xform: Matrix, + scissor: ScissorT, + + // font state + fontSize: f32, + letterSpacing: f32, + lineHeight: f32, + fontBlur: f32, + alignHorizontal: AlignHorizontal, + alignVertical: AlignVertical, + fontId: int, +} + +Context :: struct { + params: Params, + commands: [dynamic]f32, + commandx, commandy: f32, + states: [MAX_STATES]State, + nstates: int, + cache: PathCache, + tessTol: f32, + distTol: f32, + fringeWidth: f32, + devicePxRatio: f32, + + // font + fs: fontstash.FontContext, + fontImages: [MAX_FONTIMAGES]int, + fontImageIdx: int, + + // stats + drawCallCount: int, + fillTriCount: int, + strokeTriCount: int, + textTriCount: int, + + // flush texture + textureDirty: bool, +} + +Params :: struct { + userPtr: rawptr, + edgeAntiAlias: bool, + + // callbacks to fill out + renderCreate: proc(uptr: rawptr) -> bool, + renderDelete: proc(uptr: rawptr), + + // textures calls + renderCreateTexture: proc( + uptr: rawptr, + type: Texture, + w, h: int, + imageFlags: ImageFlags, + data: []byte, + ) -> int, + renderDeleteTexture: proc(uptr: rawptr, image: int) -> bool, + renderUpdateTexture: proc( + uptr: rawptr, + image: int, + x, y: int, + w, h: int, + data: []byte, + ) -> bool, + renderGetTextureSize: proc(uptr: rawptr, image: int, w, h: ^int) -> bool, + + // rendering calls + renderViewport: proc(uptr: rawptr, width, height, devicePixelRatio: f32), + renderCancel: proc(uptr: rawptr), + renderFlush: proc(uptr: rawptr), + renderFill: proc( + uptr: rawptr, + paint: ^Paint, + compositeOperation: CompositeOperationState, + scissor: ^ScissorT, + fringe: f32, + bounds: [4]f32, + paths: []Path, + ), + renderStroke: proc( + uptr: rawptr, + paint: ^Paint, + compositeOperation: CompositeOperationState, + scissor: ^ScissorT, + fringe: f32, + strokeWidth: f32, + paths: []Path, + ), + renderTriangles: proc( + uptr: rawptr, + paint: ^Paint, + compositeOperation: CompositeOperationState, + scissor: ^ScissorT, + verts: []Vertex, + fringe: f32, + ), +} + +__allocPathCache :: proc(c: ^PathCache) { + c.points = make([dynamic]Point, 0, INIT_POINTS_SIZE) + c.paths = make([dynamic]Path, 0, INIT_PATH_SIZE) + c.verts = make([dynamic]Vertex, 0, INIT_VERTS_SIZE) +} + +__deletePathCache :: proc(c: PathCache) { + delete(c.points) + delete(c.paths) + delete(c.verts) +} + +__setDevicePxRatio :: proc(ctx: ^Context, ratio: f32) { + ctx.tessTol = 0.25 / ratio + ctx.distTol = 0.01 / ratio + ctx.fringeWidth = 1.0 / ratio + ctx.devicePxRatio = ratio +} + +__getState :: #force_inline proc(ctx: ^Context) -> ^State #no_bounds_check { + return &ctx.states[ctx.nstates - 1] +} + +CreateInternal :: proc(params: Params) -> (ctx: ^Context) { + ctx = new(Context) + ctx.params = params + ctx.commands = make([dynamic]f32, 0, INIT_COMMANDS_SIZE) + __allocPathCache(&ctx.cache) + + Save(ctx) + Reset(ctx) + __setDevicePxRatio(ctx, 1) + + assert(ctx.params.renderCreate != nil) + if !ctx.params.renderCreate(ctx.params.userPtr) { + DeleteInternal(ctx) + panic("Nanovg - CreateInternal failed") + } + + w := INIT_FONTIMAGE_SIZE + h := INIT_FONTIMAGE_SIZE + fontstash.Init(&ctx.fs, w, h, .TOPLEFT) + assert(ctx.params.renderCreateTexture != nil) + ctx.fs.userData = ctx + + // handle to the image needs to be set to the new generated texture + ctx.fs.callbackResize = proc(data: rawptr, w, h: int) { + ctx := cast(^Context) data + ctx.fontImages[0] = ctx.params.renderCreateTexture(ctx.params.userPtr, .Alpha, w, h, {}, ctx.fs.textureData) + } + + // texture atlas + ctx.fontImages[0] = ctx.params.renderCreateTexture(ctx.params.userPtr, .Alpha, w, h, {}, nil) + ctx.fontImageIdx = 0 + + return +} + +DeleteInternal :: proc(ctx: ^Context) { + __deletePathCache(ctx.cache) + fontstash.Destroy(&ctx.fs) + + for image in &ctx.fontImages { + if image != 0 { + DeleteImage(ctx, image) + } + } + + if ctx.params.renderDelete != nil { + ctx.params.renderDelete(ctx.params.userPtr) + } + + free(ctx) +} + +/* + Begin drawing a new frame + Calls to nanovg drawing API should be wrapped in nvgBeginFrame() & nvgEndFrame() + nvgBeginFrame() defines the size of the window to render to in relation currently + set viewport (i.e. glViewport on GL backends). Device pixel ration allows to + control the rendering on Hi-DPI devices. + For example, GLFW returns two dimension for an opened window: window size and + frame buffer size. In that case you would set windowWidth/Height to the window size + devicePixelRatio to: frameBufferWidth / windowWidth. +*/ +BeginFrame :: proc( + ctx: ^Context, + windowWidth: f32, + windowHeight: f32, + devicePixelRatio: f32, +) { + ctx.nstates = 0 + Save(ctx) + Reset(ctx) + __setDevicePxRatio(ctx, devicePixelRatio) + + assert(ctx.params.renderViewport != nil) + ctx.params.renderViewport(ctx.params.userPtr, windowWidth, windowHeight, devicePixelRatio) + + ctx.drawCallCount = 0 + ctx.fillTriCount = 0 + ctx.strokeTriCount = 0 + ctx.textTriCount = 0 +} + +@(deferred_out=EndFrame) +FrameScoped :: proc( + ctx: ^Context, + windowWidth: f32, + windowHeight: f32, + devicePixelRatio: f32, +) -> ^Context { + BeginFrame(ctx, windowWidth, windowHeight, devicePixelRatio) + return ctx +} + +// Cancels drawing the current frame. +CancelFrame :: proc(ctx: ^Context) { + assert(ctx.params.renderCancel != nil) + ctx.params.renderCancel(ctx.params.userPtr) +} + +// Ends drawing flushing remaining render state. +EndFrame :: proc(ctx: ^Context) { + // flush texture only once + if ctx.textureDirty { + __flushTextTexture(ctx) + ctx.textureDirty = false + } + + assert(ctx.params.renderFlush != nil) + ctx.params.renderFlush(ctx.params.userPtr) + + // delete textures with invalid size + if ctx.fontImageIdx != 0 { + font_image := ctx.fontImages[ctx.fontImageIdx] + ctx.fontImages[ctx.fontImageIdx] = 0 + + if font_image == 0 { + return + } + + iw, ih := ImageSize(ctx, font_image) + j: int + for i in 0.. Color { + return RGBA(r, g, b, 255) +} + +// Returns a color value from red, green, blue and alpha values. +RGBA :: proc(r, g, b, a: u8) -> (res: Color) { + res.r = f32(r) / f32(255) + res.g = f32(g) / f32(255) + res.b = f32(b) / f32(255) + res.a = f32(a) / f32(255) + return +} + +// Linearly interpolates from color c0 to c1, and returns resulting color value. +LerpRGBA :: proc(c0, c1: Color, u: f32) -> (cint: Color) { + u := clamp(u, 0.0, 1.0) + oneminu := 1.0 - u + for i in 0..<4 { + cint[i] = c0[i] * oneminu + c1[i] * u + } + + return +} + +// Returns color value specified by hue, saturation and lightness. +// HSL values are all in range [0..1], alpha will be set to 255. +HSL :: proc(h, s, l: f32) -> Color { + return HSLA(h,s,l,255) +} + +// Returns color value specified by hue, saturation and lightness and alpha. +// HSL values are all in range [0..1], alpha in range [0..255] +HSLA :: proc(h, s, l: f32, a: u8) -> (col: Color) { + hue :: proc(h, m1, m2: f32) -> f32 { + h := h + + if h < 0 { + h += 1 + } + + if h > 1 { + h -= 1 + } + + if h < 1.0 / 6.0 { + return m1 + (m2 - m1) * h * 6.0 + } else if h < 3.0 / 6.0 { + return m2 + } else if h < 4.0 / 6.0 { + return m1 + (m2 - m1) * (2.0 / 3.0 - h) * 6.0 + } + + return m1 + } + + h := math.mod(h, 1.0) + if h < 0.0 { + h += 1.0 + } + s := clamp(s, 0.0, 1.0) + l := clamp(l, 0.0, 1.0) + m2 := l <= 0.5 ? (l * (1 + s)) : (l + s - l * s) + m1 := 2 * l - m2 + col.r = clamp(hue(h + 1.0/3.0, m1, m2), 0.0, 1.0) + col.g = clamp(hue(h, m1, m2), 0.0, 1.0) + col.b = clamp(hue(h - 1.0/3.0, m1, m2), 0.0, 1.0) + col.a = f32(a) / 255.0 + return +} + +// hex to 0xAARRGGBB color +ColorHex :: proc(color: u32) -> (res: Color) { + color := color + res.b = f32(0x000000FF & color) / 255 + color = color >> 8 + res.g = f32(0x000000FF & color) / 255 + color = color >> 8 + res.r = f32(0x000000FF & color) / 255 + color = color >> 8 + res.a = f32(0x000000FF & color) / 255 + return +} + +/////////////////////////////////////////////////////////// +// TRANSFORMS +// +// The following functions can be used to make calculations on 2x3 transformation matrices. +// A 2x3 matrix is represented as float[6]. +/////////////////////////////////////////////////////////// + +// Sets the transform to identity matrix. +TransformIdentity :: proc(t: ^Matrix) { + t[0] = 1 + t[1] = 0 + t[2] = 0 + t[3] = 1 + t[4] = 0 + t[5] = 0 +} + +// Sets the transform to translation matrix matrix. +TransformTranslate :: proc(t: ^Matrix, tx, ty: f32) { + t[0] = 1 + t[1] = 0 + t[2] = 0 + t[3] = 1 + t[4] = tx + t[5] = ty +} + +// Sets the transform to scale matrix. +TransformScale :: proc(t: ^Matrix, sx, sy: f32) { + t[0] = sx + t[1] = 0 + t[2] = 0 + t[3] = sy + t[4] = 0 + t[5] = 0 +} + +// Sets the transform to rotate matrix. Angle is specified in radians. +TransformRotate :: proc(t: ^Matrix, a: f32) { + cs := math.cos(a) + sn := math.sin(a) + t[0] = cs + t[1] = sn + t[2] = -sn + t[3] = cs + t[4] = 0 + t[5] = 0 +} + +// Sets the transform to skew-x matrix. Angle is specified in radians. +TransformSkewX :: proc(t: ^Matrix, a: f32) { + t[0] = 1 + t[1] = 0 + t[2] = math.tan(a) + t[3] = 1 + t[4] = 0 + t[5] = 0 +} + +// Sets the transform to skew-y matrix. Angle is specified in radians. +TransformSkewY :: proc(t: ^Matrix, a: f32) { + t[0] = 1 + t[1] = math.tan(a) + t[2] = 0 + t[3] = 1 + t[4] = 0 + t[5] = 0 +} + +// Sets the transform to the result of multiplication of two transforms, of A = A*B. +TransformMultiply :: proc(t: ^Matrix, s: Matrix) { + t0 := t[0] * s[0] + t[1] * s[2] + t2 := t[2] * s[0] + t[3] * s[2] + t4 := t[4] * s[0] + t[5] * s[2] + s[4] + t[1] = t[0] * s[1] + t[1] * s[3] + t[3] = t[2] * s[1] + t[3] * s[3] + t[5] = t[4] * s[1] + t[5] * s[3] + s[5] + t[0] = t0 + t[2] = t2 + t[4] = t4 +} + +// Sets the transform to the result of multiplication of two transforms, of A = B*A. +TransformPremultiply :: proc(t: ^Matrix, s: Matrix) { + temp := s + TransformMultiply(&temp, t^) + t^ = temp +} + +// Sets the destination to inverse of specified transform. +// Returns true if the inverse could be calculated, else false. +TransformInverse :: proc(inv: ^Matrix, t: Matrix) -> bool { + // TODO could be bad math? due to types + det := f64(t[0]) * f64(t[3]) - f64(t[2]) * f64(t[1]) + + if det > -1e-6 && det < 1e-6 { + TransformIdentity(inv) + return false + } + + invdet := 1.0 / det + inv[0] = f32(f64(t[3]) * invdet) + inv[2] = f32(f64(-t[2]) * invdet) + inv[4] = f32((f64(t[2]) * f64(t[5]) - f64(t[3]) * f64(t[4])) * invdet) + inv[1] = f32(f64(-t[1]) * invdet) + inv[3] = f32(f64(t[0]) * invdet) + inv[5] = f32((f64(t[1]) * f64(t[4]) - f64(t[0]) * f64(t[5])) * invdet) + return true +} + +// Transform a point by given transform. +TransformPoint :: proc( + dx: ^f32, + dy: ^f32, + t: Matrix, + sx: f32, + sy: f32, +) { + dx^ = sx * t[0] + sy * t[2] + t[4] + dy^ = sx * t[1] + sy * t[3] + t[5] +} + +DegToRad :: proc(deg: f32) -> f32 { + return deg / 180.0 * math.PI +} + +RadToDeg :: proc(rad: f32) -> f32 { + return rad / math.PI * 180.0 +} + +/////////////////////////////////////////////////////////// +// STATE MANAGEMENT +// +// NanoVG contains state which represents how paths will be rendered. +// The state contains transform, fill and stroke styles, text and font styles, +// and scissor clipping. +/////////////////////////////////////////////////////////// + +// Pushes and saves the current render state into a state stack. +// A matching nvgRestore() must be used to restore the state. +Save :: proc(ctx: ^Context) { + if ctx.nstates >= MAX_STATES { + return + } + + // copy prior + if ctx.nstates > 0 { + ctx.states[ctx.nstates] = ctx.states[ctx.nstates - 1] + } + + ctx.nstates += 1 +} + +// Pops and restores current render state. +Restore :: proc(ctx: ^Context) { + if ctx.nstates <= 1 { + return + } + + ctx.nstates -= 1 +} + +// NOTE useful helper +@(deferred_in=Restore) +SaveScoped :: #force_inline proc(ctx: ^Context) { + Save(ctx) +} + +__setPaintColor :: proc(p: ^Paint, color: Color) { + p^ = {} + TransformIdentity(&p.xform) + p.radius = 0 + p.feather = 1 + p.innerColor = color + p.outerColor = color +} + +// Resets current render state to default values. Does not affect the render state stack. +Reset :: proc(ctx: ^Context) { + state := __getState(ctx) + state^ = {} + + __setPaintColor(&state.fill, RGBA(255, 255, 255, 255)) + __setPaintColor(&state.stroke, RGBA(0, 0, 0, 255)) + + state.compositeOperation = __compositeOperationState(.SOURCE_OVER) + state.shapeAntiAlias = true + state.strokeWidth = 1 + state.miterLimit = 10 + state.lineCap = .BUTT + state.lineJoin = .MITER + state.alpha = 1 + TransformIdentity(&state.xform) + + state.scissor.extent[0] = -1 + state.scissor.extent[1] = -1 + + // font settings + state.fontSize = 16 + state.letterSpacing = 0 + state.lineHeight = 1 + state.fontBlur = 0 + state.alignHorizontal = .LEFT + state.alignVertical = .BASELINE + state.fontId = 0 +} + +/////////////////////////////////////////////////////////// +// STATE SETTING +/////////////////////////////////////////////////////////// + +// Sets whether to draw antialias for nvgStroke() and nvgFill(). It's enabled by default. +ShapeAntiAlias :: proc(ctx: ^Context, enabled: bool) { + state := __getState(ctx) + state.shapeAntiAlias = enabled +} + +// Sets the stroke width of the stroke style. +StrokeWidth :: proc(ctx: ^Context, width: f32) { + state := __getState(ctx) + state.strokeWidth = width +} + +// Sets the miter limit of the stroke style. +// Miter limit controls when a sharp corner is beveled. +MiterLimit :: proc(ctx: ^Context, limit: f32) { + state := __getState(ctx) + state.miterLimit = limit +} + +// Sets how the end of the line (cap) is drawn, +// Can be one of: NVG_BUTT (default), NVG_ROUND, NVG_SQUARE. +LineCap :: proc(ctx: ^Context, cap: LineCapType) { + state := __getState(ctx) + state.lineCap = cap +} + +// Sets how sharp path corners are drawn. +// Can be one of NVG_MITER (default), NVG_ROUND, NVG_BEVEL. +LineJoin :: proc(ctx: ^Context, join: LineCapType) { + state := __getState(ctx) + state.lineJoin = join +} + +// Sets the transparency applied to all rendered shapes. +// Already transparent paths will get proportionally more transparent as well. +GlobalAlpha :: proc(ctx: ^Context, alpha: f32) { + state := __getState(ctx) + state.alpha = alpha +} + +// Sets current stroke style to a solid color. +StrokeColor :: proc(ctx: ^Context, color: Color) { + state := __getState(ctx) + __setPaintColor(&state.stroke, color) +} + +// Sets current stroke style to a paint, which can be a one of the gradients or a pattern. +StrokePaint :: proc(ctx: ^Context, paint: Paint) { + state := __getState(ctx) + state.stroke = paint + TransformMultiply(&state.stroke.xform, state.xform) +} + +// Sets current fill style to a solid color. +FillColor :: proc(ctx: ^Context, color: Color) { + state := __getState(ctx) + __setPaintColor(&state.fill, color) +} + +// Sets current fill style to a paint, which can be a one of the gradients or a pattern. +FillPaint :: proc(ctx: ^Context, paint: Paint) { + state := __getState(ctx) + state.fill = paint + TransformMultiply(&state.fill.xform, state.xform) +} + +/////////////////////////////////////////////////////////// +// STATE TRANSFORMS +// +// The paths, gradients, patterns and scissor region are transformed by an transformation +// matrix at the time when they are passed to the API. +// The current transformation matrix is a affine matrix: +// [sx kx tx] +// [ky sy ty] +// [ 0 0 1] +// Where: sx,sy define scaling, kx,ky skewing, and tx,ty translation. +// The last row is assumed to be 0,0,1 and is not stored. +// +// Apart from nvgResetTransform(), each transformation function first creates +// specific transformation matrix and pre-multiplies the current transformation by it. +// +// Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore(). +/////////////////////////////////////////////////////////// + +Transform :: proc(ctx: ^Context, a, b, c, d, e, f: f32) { + state := __getState(ctx) + TransformPremultiply(&state.xform, { a, b, c, d, e, f }) +} + +// Resets current transform to a identity matrix. +ResetTransform :: proc(ctx: ^Context) { + state := __getState(ctx) + TransformIdentity(&state.xform) +} + +// Translates current coordinate system. +Translate :: proc(ctx: ^Context, x, y: f32) { + state := __getState(ctx) + temp: Matrix + TransformTranslate(&temp, x, y) + TransformPremultiply(&state.xform, temp) +} + +// Rotates current coordinate system. Angle is specified in radians. +Rotate :: proc(ctx: ^Context, angle: f32) { + state := __getState(ctx) + temp: Matrix + TransformRotate(&temp, angle) + TransformPremultiply(&state.xform, temp) +} + +// Skews the current coordinate system along X axis. Angle is specified in radians. +SkewX :: proc(ctx: ^Context, angle: f32) { + state := __getState(ctx) + temp: Matrix + TransformSkewX(&temp, angle) + TransformPremultiply(&state.xform, temp) +} + +// Skews the current coordinate system along Y axis. Angle is specified in radians. +SkewY :: proc(ctx: ^Context, angle: f32) { + state := __getState(ctx) + temp: Matrix + TransformSkewY(&temp, angle) + TransformPremultiply(&state.xform, temp) +} + +// Scales the current coordinate system. +Scale :: proc(ctx: ^Context, x, y: f32) { + state := __getState(ctx) + temp: Matrix + TransformScale(&temp, x, y) + TransformPremultiply(&state.xform, temp) +} + +/* + Stores the top part (a-f) of the current transformation matrix in to the specified buffer. + [a c e] + [b d f] + [0 0 1] + There should be space for 6 floats in the return buffer for the values a-f. +*/ +CurrentTransform :: proc(ctx: ^Context, xform: ^Matrix) { + state := __getState(ctx) + if xform == nil { + return + } + xform^ = state.xform +} + +/////////////////////////////////////////////////////////// +// IMAGE HANDLING +// +// NanoVG allows you to load jpg, png, psd, tga, pic and gif files to be used for rendering. +// In addition you can upload your own image. The image loading is provided by stb_image. +// The parameter imageFlags is a combination of flags defined in NVGimageFlags. +/////////////////////////////////////////////////////////// + +// Creates image by loading it from the disk from specified file name. +// Returns handle to the image. +CreateImagePath :: proc(ctx: ^Context, filename: cstring, imageFlags: ImageFlags) -> int { + stbi.set_unpremultiply_on_load(1) + stbi.convert_iphone_png_to_rgb(1) + w, h, n: i32 + img := stbi.load(filename, &w, &h, &n, 4) + + if img == nil { + return 0 + } + + data := mem.slice_ptr(img, int(w) * int(h) * int(n)) + image := CreateImageRGBA(ctx, int(w), int(h), imageFlags, data) + stbi.image_free(img) + return image +} + +// Creates image by loading it from the specified chunk of memory. +// Returns handle to the image. +CreateImageMem :: proc(ctx: ^Context, data: []byte, imageFlags: ImageFlags) -> int { + stbi.set_unpremultiply_on_load(1) + stbi.convert_iphone_png_to_rgb(1) + w, h, n: i32 + img := stbi.load_from_memory(raw_data(data), i32(len(data)), &w, &h, &n, 4) + + if img == nil { + return 0 + } + + data := mem.slice_ptr(img, int(w) * int(h) * int(n)) + image := CreateImageRGBA(ctx, int(w), int(h), imageFlags, data) + stbi.image_free(img) + return image +} + +CreateImage :: proc { CreateImagePath, CreateImageMem } + +// Creates image from specified image data. +// Returns handle to the image. +CreateImageRGBA :: proc(ctx: ^Context, w, h: int, imageFlags: ImageFlags, data: []byte) -> int { + assert(ctx.params.renderCreateTexture != nil) + return ctx.params.renderCreateTexture( + ctx.params.userPtr, + .RGBA, + w, h, + imageFlags, + data, + ) +} + +// Updates image data specified by image handle. +UpdateImage :: proc(ctx: ^Context, image: int, data: []byte) { + assert(ctx.params.renderGetTextureSize != nil) + assert(ctx.params.renderUpdateTexture != nil) + + w, h: int + found := ctx.params.renderGetTextureSize(ctx.params.userPtr, image, &w, &h) + if found { + ctx.params.renderUpdateTexture(ctx.params.userPtr, image, 0, 0, w, h, data) + } +} + +// Returns the dimensions of a created image. +ImageSize :: proc(ctx: ^Context, image: int) -> (w, h: int) { + assert(ctx.params.renderGetTextureSize != nil) + ctx.params.renderGetTextureSize(ctx.params.userPtr, image, &w, &h) + return +} + +// Deletes created image. +DeleteImage :: proc(ctx: ^Context, image: int) { + assert(ctx.params.renderDeleteTexture != nil) + ctx.params.renderDeleteTexture(ctx.params.userPtr, image) +} + +/////////////////////////////////////////////////////////// +// PAINT gradients / image +// +// NanoVG supports four types of paints: linear gradient, box gradient, radial gradient and image pattern. +// These can be used as paints for strokes and fills. +/////////////////////////////////////////////////////////// + +/* + Creates and returns a linear gradient. Parameters (sx,sy)-(ex,ey) specify the start and end coordinates + of the linear gradient, icol specifies the start color and ocol the end color. + The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). +*/ +LinearGradient :: proc( + sx, sy: f32, + ex, ey: f32, + icol: Color, + ocol: Color, +) -> (p: Paint) { + LARGE :: f32(1e5) + + // Calculate transform aligned to the line + dx := ex - sx + dy := ey - sy + d := math.sqrt(dx*dx + dy*dy) + if d > 0.0001 { + dx /= d + dy /= d + } else { + dx = 0 + dy = 1 + } + + p.xform[0] = dy + p.xform[1] = -dx + p.xform[2] = dx + p.xform[3] = dy + p.xform[4] = sx - dx*LARGE + p.xform[5] = sy - dy*LARGE + + p.extent[0] = LARGE + p.extent[1] = LARGE + d*0.5 + + p.feather = max(1.0, d) + + p.innerColor = icol + p.outerColor = ocol + + return +} + +/* + Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering + drop shadows or highlights for boxes. Parameters (x,y) define the top-left corner of the rectangle, + (w,h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry + the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient. + The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). +*/ +RadialGradient :: proc( + cx, cy: f32, + inr: f32, + outr: f32, + icol: Color, + ocol: Color, +) -> (p: Paint) { + r := (inr+outr)*0.5 + f := (outr-inr) + + TransformIdentity(&p.xform) + p.xform[4] = cx + p.xform[5] = cy + + p.extent[0] = r + p.extent[1] = r + + p.radius = r + p.feather = max(1.0, f) + + p.innerColor = icol + p.outerColor = ocol + + return +} + +/* + Creates and returns a radial gradient. Parameters (cx,cy) specify the center, inr and outr specify + the inner and outer radius of the gradient, icol specifies the start color and ocol the end color. + The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). +*/ +BoxGradient :: proc( + x, y: f32, + w, h: f32, + r: f32, + f: f32, + icol: Color, + ocol: Color, +) -> (p: Paint) { + TransformIdentity(&p.xform) + p.xform[4] = x+w*0.5 + p.xform[5] = y+h*0.5 + + p.extent[0] = w*0.5 + p.extent[1] = h*0.5 + + p.radius = r + p.feather = max(1.0, f) + + p.innerColor = icol + p.outerColor = ocol + + return +} + +/* + Creates and returns an image pattern. Parameters (ox,oy) specify the left-top location of the image pattern, + (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render. + The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). +*/ +ImagePattern :: proc( + cx, cy: f32, + w, h: f32, + angle: f32, + image: int, + alpha: f32, +) -> (p: Paint) { + TransformRotate(&p.xform, angle) + p.xform[4] = cx + p.xform[5] = cy + + p.extent[0] = w + p.extent[1] = h + + p.image = image + p.innerColor = { 1,1,1,alpha } + p.outerColor = p.innerColor + + return +} + +/////////////////////////////////////////////////////////// +// SCISSOR +// +// Scissoring allows you to clip the rendering into a rectangle. This is useful for various +// user interface cases like rendering a text edit or a timeline. +/////////////////////////////////////////////////////////// + +// Sets the current scissor rectangle. +// The scissor rectangle is transformed by the current transform. +Scissor :: proc( + ctx: ^Context, + x, y: f32, + w, h: f32, +) { + state := __getState(ctx) + w := max(w, 0) + h := max(h, 0) + + TransformIdentity(&state.scissor.xform) + state.scissor.xform[4] = x + w * 0.5 + state.scissor.xform[5] = y + h * 0.5 + TransformMultiply(&state.scissor.xform, state.xform) + + state.scissor.extent[0] = w * 0.5 + state.scissor.extent[1] = h * 0.5 +} + +/* + Intersects current scissor rectangle with the specified rectangle. + The scissor rectangle is transformed by the current transform. + Note: in case the rotation of previous scissor rect differs from + the current one, the intersection will be done between the specified + rectangle and the previous scissor rectangle transformed in the current + transform space. The resulting shape is always rectangle. +*/ +IntersectScissor :: proc( + ctx: ^Context, + x, y, w, h: f32, +) { + isect_rects :: proc( + dst: ^[4]f32, + ax, ay, aw, ah: f32, + bx, by, bw, bh: f32, + ) { + minx := max(ax, bx) + miny := max(ay, by) + maxx := min(ax + aw, bx + bw) + maxy := min(ay + ah, by + bh) + dst[0] = minx + dst[1] = miny + dst[2] = max(0.0, maxx - minx) + dst[3] = max(0.0, maxy - miny) + } + + state := __getState(ctx) + pxform: Matrix + invxorm: Matrix + + // If no previous scissor has been set, set the scissor as current scissor. + if state.scissor.extent[0] < 0 { + Scissor(ctx, x, y, w, h) + return + } + + pxform = state.scissor.xform + ex := state.scissor.extent[0] + ey := state.scissor.extent[1] + TransformInverse(&invxorm, state.xform) + TransformMultiply(&pxform, invxorm) + tex := ex * abs(pxform[0]) + ey * abs(pxform[2]) + tey := ex * abs(pxform[1]) + ey * abs(pxform[3]) + + rect: [4]f32 + isect_rects(&rect, pxform[4] - tex, pxform[5] - tey, tex * 2, tey * 2, x,y,w,h) + Scissor(ctx, rect.x, rect.y, rect.z, rect.w) +} + +// Reset and disables scissoring. +ResetScissor :: proc(ctx: ^Context) { + state := __getState(ctx) + state.scissor.xform = 0 + state.scissor.extent[0] = -1 + state.scissor.extent[1] = -1 +} + +/////////////////////////////////////////////////////////// +// Global composite operation +// +// The composite operations in NanoVG are modeled after HTML Canvas API, and +// the blend func is based on OpenGL (see corresponding manuals for more info). +// The colors in the blending state have premultiplied alpha. +/////////////////////////////////////////////////////////// + +// state table instead of if else chains +OP_STATE_TABLE :: [CompositeOperation][2]BlendFactor { + .SOURCE_OVER = { .ONE, .ONE_MINUS_SRC_ALPHA }, + .SOURCE_IN = { .DST_ALPHA, .ZERO }, + .SOURCE_OUT = { .ONE_MINUS_DST_ALPHA, .ZERO }, + .ATOP = { .DST_ALPHA, .ONE_MINUS_SRC_ALPHA }, + + .DESTINATION_OVER = { .ONE_MINUS_DST_ALPHA, .ONE }, + .DESTINATION_IN = { .ZERO, .SRC_ALPHA }, + .DESTINATION_OUT = { .ZERO, .ONE_MINUS_SRC_ALPHA }, + .DESTINATION_ATOP = { .ONE_MINUS_DST_ALPHA, .SRC_ALPHA }, + + .LIGHTER = { .ONE, .ONE }, + .COPY = { .ONE, .ZERO }, + .XOR = { .ONE_MINUS_DST_ALPHA, .ONE_MINUS_SRC_ALPHA }, +} + +__compositeOperationState :: proc(op: CompositeOperation) -> (res: CompositeOperationState) { + table := OP_STATE_TABLE + factors := table[op] + res.srcRGB = factors.x + res.dstRGB = factors.y + res.srcAlpha = factors.x + res.dstAlpha = factors.y + return +} + +// Sets the composite operation. The op parameter should be one of NVGcompositeOperation. +GlobalCompositeOperation :: proc(ctx: ^Context, op: CompositeOperation) { + state := __getState(ctx) + state.compositeOperation = __compositeOperationState(op) +} + +// Sets the composite operation with custom pixel arithmetic. The parameters should be one of NVGblendFactor. +GlobalCompositeBlendFunc :: proc(ctx: ^Context, sfactor, dfactor: BlendFactor) { + GlobalCompositeBlendFuncSeparate(ctx, sfactor, dfactor, sfactor, dfactor) +} + +// Sets the composite operation with custom pixel arithmetic for RGB and alpha components separately. The parameters should be one of NVGblendFactor. +GlobalCompositeBlendFuncSeparate :: proc( + ctx: ^Context, + srcRGB: BlendFactor, + dstRGB: BlendFactor, + srcAlpha: BlendFactor, + dstAlpha: BlendFactor, +) { + op := CompositeOperationState { + srcRGB, + dstRGB, + srcAlpha, + dstAlpha, + } + state := __getState(ctx) + state.compositeOperation = op +} + +/////////////////////////////////////////////////////////// +// Points / Path handling +/////////////////////////////////////////////////////////// + +__cross :: proc(dx0, dy0, dx1, dy1: f32) -> f32 { + return dx1*dy0 - dx0*dy1 +} + +__ptEquals :: proc(x1, y1, x2, y2, tol: f32) -> bool { + dx := x2 - x1 + dy := y2 - y1 + return dx * dx + dy * dy < tol * tol +} + +__distPtSeg :: proc(x, y, px, py, qx, qy: f32) -> f32 { + pqx := qx - px + pqy := qy - py + dx := x - px + dy := y - py + d := pqx * pqx + pqy * pqy + t := pqx * dx + pqy * dy + + if d > 0 { + t /= d + } + + if t < 0 { + t = 0 + } else if t > 1 { + t = 1 + } + + dx = px + t * pqx - x + dy = py + t * pqy - y + return dx * dx + dy * dy +} + +__appendCommands :: proc(ctx: ^Context, values: []f32) { + state := __getState(ctx) + + if Commands(values[0]) != .CLOSE && Commands(values[0]) != .WINDING { + ctx.commandx = values[len(values) - 2] + ctx.commandy = values[len(values) - 1] + } + + i := 0 + for i < len(values) { + cmd := Commands(values[i]) + + switch cmd { + case .MOVE_TO, .LINE_TO: { + TransformPoint(&values[i + 1], &values[i + 2], state.xform, values[i + 1], values[i + 2]) + i += 3 + } + + case .BEZIER_TO: { + TransformPoint(&values[i + 1], &values[i + 2], state.xform, values[i + 1], values[i + 2]) + TransformPoint(&values[i + 3], &values[i + 4], state.xform, values[i + 3], values[i + 4]) + TransformPoint(&values[i + 5], &values[i + 6], state.xform, values[i + 5], values[i + 6]) + i += 7 + } + + case .CLOSE: { + i += 1 + } + + case .WINDING: { + i += 2 + } + + // default + case: { + i += 1 + } + } + } + + // append values + append(&ctx.commands, ..values) +} + +__clearPathCache :: proc(ctx: ^Context) { + clear(&ctx.cache.points) + clear(&ctx.cache.paths) +} + +__lastPath :: proc(ctx: ^Context) -> ^Path { + if len(ctx.cache.paths) > 0 { + return &ctx.cache.paths[len(ctx.cache.paths) - 1] + } + + return nil +} + +__addPath :: proc(ctx: ^Context) { + append(&ctx.cache.paths, Path { + first = len(ctx.cache.points), + winding = .CCW, + }) +} + +__lastPoint :: proc(ctx: ^Context) -> ^Point { + if len(ctx.cache.paths) > 0 { + return &ctx.cache.points[len(ctx.cache.points) - 1] + } + + return nil +} + +__addPoint :: proc(ctx: ^Context, x, y: f32, flags: PointFlags) { + path := __lastPath(ctx) + + if path == nil { + return + } + + if path.count > 0 && len(ctx.cache.points) > 0 { + pt := __lastPoint(ctx) + + if __ptEquals(pt.x, pt.y, x, y, ctx.distTol) { + pt.flags |= flags + return + } + } + + append(&ctx.cache.points, Point { + x = x, + y = y, + flags = flags, + }) + path.count += 1 +} + +__closePath :: proc(ctx: ^Context) { + path := __lastPath(ctx) + if path == nil { + return + } + path.closed = true +} + +__pathWinding :: proc(ctx: ^Context, winding: Winding) { + path := __lastPath(ctx) + if path == nil { + return + } + path.winding = winding +} + +__getAverageScale :: proc(t: []f32) -> f32 { + assert(len(t) > 4) + sx := math.sqrt(f64(t[0]) * f64(t[0]) + f64(t[2]) * f64(t[2])) + sy := math.sqrt(f64(t[1]) * f64(t[1]) + f64(t[3]) * f64(t[3])) + return f32((sx + sy) * 0.5) + // sx := math.sqrt(t[0] * t[0] + t[2] * t[2]) + // sy := math.sqrt(t[1] * t[1] + t[3] * t[3]) + // return (sx + sy) * 0.5 +} + +__triarea2 :: proc(ax, ay, bx, by, cx, cy: f32) -> f32 { + abx := bx - ax + aby := by - ay + acx := cx - ax + acy := cy - ay + return acx * aby - abx * acy +} + +__polyArea :: proc(points: []Point) -> f32 { + area := f32(0) + + for i := 2; i < len(points); i += 1 { + a := &points[0] + b := &points[i - 1] + c := &points[i] + area += __triarea2(a.x, a.y, b.x, b.y, c.x, c.y) + } + + return area * 0.5 +} + +__polyReverse :: proc(points: []Point) { + tmp: Point + i := 0 + j := len(points) - 1 + + for i < j { + tmp = points[i] + points[i] = points[j] + points[j] = tmp + i += 1 + j -= 1 + } +} + +__normalize :: proc(x, y: ^f32) -> f32 { + d := math.sqrt(x^ * x^ + y^ * y^) + if d > 1e-6 { + id := 1.0 / d + x^ *= id + y^ *= id + } + return d +} + +__tesselateBezier :: proc( + ctx: ^Context, + x1, y1: f32, + x2, y2: f32, + x3, y3: f32, + x4, y4: f32, + level: int, + flags: PointFlags, +) { + if level > 10 { + return + } + + x12 := (x1 + x2) * 0.5 + y12 := (y1 + y2) * 0.5 + x23 := (x2 + x3) * 0.5 + y23 := (y2 + y3) * 0.5 + x34 := (x3 + x4) * 0.5 + y34 := (y3 + y4) * 0.5 + x123 := (x12 + x23) * 0.5 + y123 := (y12 + y23) * 0.5 + + dx := x4 - x1 + dy := y4 - y1 + d2 := abs(((x2 - x4) * dy - (y2 - y4) * dx)) + d3 := abs(((x3 - x4) * dy - (y3 - y4) * dx)) + + if (d2 + d3)*(d2 + d3) < ctx.tessTol * (dx*dx + dy*dy) { + __addPoint(ctx, x4, y4, flags) + return + } + + x234 := (x23 + x34) * 0.5 + y234 := (y23 + y34) * 0.5 + x1234 := (x123 + x234) * 0.5 + y1234 := (y123 + y234) * 0.5 + + __tesselateBezier(ctx, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, {}) + __tesselateBezier(ctx, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, flags) +} + +__flattenPaths :: proc(ctx: ^Context) { + cache := &ctx.cache + + if len(cache.paths) > 0 { + return + } + + // flatten + i := 0 + for i < len(ctx.commands) { + cmd := Commands(ctx.commands[i]) + + switch cmd { + case .MOVE_TO: { + __addPath(ctx) + p := ctx.commands[i + 1:] + __addPoint(ctx, p[0], p[1], { .CORNER }) + i += 3 + } + + case .LINE_TO: { + p := ctx.commands[i + 1:] + __addPoint(ctx, p[0], p[1], { .CORNER }) + i += 3 + } + + case .BEZIER_TO: { + last := __lastPoint(ctx) + + if last != nil { + cp1 := ctx.commands[i + 1:] + cp2 := ctx.commands[i + 3:] + p := ctx.commands[i + 5:] + __tesselateBezier(ctx, last.x,last.y, cp1[0],cp1[1], cp2[0],cp2[1], p[0],p[1], 0, { .CORNER }) + } + + i += 7 + } + + case .CLOSE: { + __closePath(ctx) + i += 1 + } + + case .WINDING: { + __pathWinding(ctx, Winding(ctx.commands[i + 1])) + i += 2 + } + + case: { + i += 1 + } + } + } + + cache.bounds[0] = 1e6 + cache.bounds[1] = 1e6 + cache.bounds[2] = -1e6 + cache.bounds[3] = -1e6 + + // Calculate the direction and length of line segments. + for j in 0.. 2 { + area := __polyArea(pts[:path.count]) + + if path.winding == .CCW && area < 0 { + __polyReverse(pts[:path.count]) + } + + if path.winding == .CW && area > 0 { + __polyReverse(pts[:path.count]) + } + } + + for k in 0.. f32 { + da := math.acos(r / (r + tol)) * 2 + return max(2, math.ceil(arc / da)) +} + +__chooseBevel :: proc( + bevel: bool, + p0: ^Point, + p1: ^Point, + w: f32, + x0, y0, x1, y1: ^f32, +) { + if bevel { + x0^ = p1.x + p0.dy * w + y0^ = p1.y - p0.dx * w + x1^ = p1.x + p1.dy * w + y1^ = p1.y - p1.dx * w + } else { + x0^ = p1.x + p1.dmx * w + y0^ = p1.y + p1.dmy * w + x1^ = p1.x + p1.dmx * w + y1^ = p1.y + p1.dmy * w + } +} + +/////////////////////////////////////////////////////////// +// Vertice Setting +/////////////////////////////////////////////////////////// + +// set vertex & increase slice position (decreases length) +__vset :: proc(dst: ^[]Vertex, x, y, u, v: f32, loc := #caller_location) { + dst[0] = { x, y, u, v } + dst^ = dst[1:] +} + +__roundJoin :: proc( + dst: ^[]Vertex, + p0: ^Point, + p1: ^Point, + lw: f32, + rw: f32, + lu: f32, + ru: f32, + ncap: int, +) { + dlx0 := p0.dy + dly0 := -p0.dx + dlx1 := p1.dy + dly1 := -p1.dx + + if .LEFT in p1.flags { + lx0,ly0,lx1,ly1: f32 + __chooseBevel(.INNER_BEVEL in p1.flags, p0, p1, lw, &lx0,&ly0, &lx1,&ly1) + a0 := math.atan2(-dly0, -dlx0) + a1 := math.atan2(-dly1, -dlx1) + + if a1 > a0 { + a1 -= math.PI * 2 + } + + __vset(dst, lx0, ly0, lu, 1) + __vset(dst, p1.x - dlx0 * rw, p1.y - dly0 * rw, ru, 1) + + temp := int(math.ceil((a0 - a1) / math.PI * f32(ncap))) + n := clamp(temp, 2, ncap) + + for i := 0; i < n; i += 1 { + u := f32(i) / f32(n - 1) + a := a0 + u * (a1 - a0) + rx := p1.x + math.cos(a) * rw + ry := p1.y + math.sin(a) * rw + __vset(dst, p1.x, p1.y, 0.5, 1) + __vset(dst, rx, ry, ru,1) + } + + __vset(dst, lx1, ly1, lu,1) + __vset(dst, p1.x - dlx1*rw, p1.y - dly1*rw, ru,1) + } else { + rx0,ry0,rx1,ry1: f32 + __chooseBevel(.INNER_BEVEL in p1.flags, p0, p1, -rw, &rx0, &ry0, &rx1, &ry1) + a0 := math.atan2(dly0, dlx0) + a1 := math.atan2(dly1, dlx1) + if a1 < a0 { + a1 += math.PI * 2 + } + + __vset(dst, p1.x + dlx0*rw, p1.y + dly0*rw, lu,1) + __vset(dst, rx0, ry0, ru,1) + + temp := int(math.ceil((a1 - a0) / math.PI * f32(ncap))) + n := clamp(temp, 2, ncap) + + for i := 0; i < n; i += 1 { + u := f32(i) / f32(n - 1) + a := a0 + u*(a1-a0) + lx := p1.x + math.cos(a) * lw + ly := p1.y + math.sin(a) * lw + __vset(dst, lx, ly, lu, 1) + __vset(dst, p1.x, p1.y, 0.5, 1) + } + + __vset(dst, p1.x + dlx1*rw, p1.y + dly1*rw, lu,1) + __vset(dst, rx1, ry1, ru,1) + } +} + +__bevelJoin :: proc( + dst: ^[]Vertex, + p0: ^Point, + p1: ^Point, + lw: f32, + rw: f32, + lu: f32, + ru: f32, +) { + dlx0 := p0.dy + dly0 := -p0.dx + dlx1 := p1.dy + dly1 := -p1.dx + + rx0, ry0, rx1, ry1: f32 + lx0, ly0, lx1, ly1: f32 + + if .LEFT in p1.flags { + __chooseBevel(.INNER_BEVEL in p1.flags, p0, p1, lw, &lx0,&ly0, &lx1,&ly1) + + __vset(dst, lx0, ly0, lu,1) + __vset(dst, p1.x - dlx0*rw, p1.y - dly0*rw, ru,1) + + if .BEVEL in p1.flags { + __vset(dst, lx0, ly0, lu,1) + __vset(dst, p1.x - dlx0*rw, p1.y - dly0*rw, ru,1) + + __vset(dst, lx1, ly1, lu,1) + __vset(dst, p1.x - dlx1*rw, p1.y - dly1*rw, ru,1) + } else { + rx0 = p1.x - p1.dmx * rw + ry0 = p1.y - p1.dmy * rw + + __vset(dst, p1.x, p1.y, 0.5,1) + __vset(dst, p1.x - dlx0*rw, p1.y - dly0*rw, ru,1) + + __vset(dst, rx0, ry0, ru,1) + __vset(dst, rx0, ry0, ru,1) + + __vset(dst, p1.x, p1.y, 0.5,1) + __vset(dst, p1.x - dlx1*rw, p1.y - dly1*rw, ru,1) + } + + __vset(dst, lx1, ly1, lu,1) + __vset(dst, p1.x - dlx1*rw, p1.y - dly1*rw, ru,1) + } else { + __chooseBevel(.INNER_BEVEL in p1.flags, p0, p1, -rw, &rx0,&ry0, &rx1,&ry1) + + __vset(dst, p1.x + dlx0*lw, p1.y + dly0*lw, lu,1) + __vset(dst, rx0, ry0, ru,1) + + if .BEVEL in p1.flags { + __vset(dst, p1.x + dlx0*lw, p1.y + dly0*lw, lu,1) + __vset(dst, rx0, ry0, ru,1) + + __vset(dst, p1.x + dlx1*lw, p1.y + dly1*lw, lu,1) + __vset(dst, rx1, ry1, ru,1) + } else { + lx0 = p1.x + p1.dmx * lw + ly0 = p1.y + p1.dmy * lw + + __vset(dst, p1.x + dlx0*lw, p1.y + dly0*lw, lu,1) + __vset(dst, p1.x, p1.y, 0.5,1) + + __vset(dst, lx0, ly0, lu,1) + __vset(dst, lx0, ly0, lu,1) + + __vset(dst, p1.x + dlx1*lw, p1.y + dly1*lw, lu,1) + __vset(dst, p1.x, p1.y, 0.5,1) + } + + __vset(dst, p1.x + dlx1*lw, p1.y + dly1*lw, lu,1) + __vset(dst, rx1, ry1, ru,1) + } +} + +__buttCapStart :: proc( + dst: ^[]Vertex, + p: ^Point, + dx, dy: f32, + w: f32, + d: f32, + aa: f32, + u0: f32, + u1: f32, +) { + px := p.x - dx * d + py := p.y - dy * d + dlx := dy + dly := -dx + __vset(dst, px + dlx*w - dx*aa, py + dly*w - dy*aa, u0,0) + __vset(dst, px - dlx*w - dx*aa, py - dly*w - dy*aa, u1,0) + __vset(dst, px + dlx*w, py + dly*w, u0,1) + __vset(dst, px - dlx*w, py - dly*w, u1,1) +} + +__buttCapEnd :: proc( + dst: ^[]Vertex, + p: ^Point, + dx, dy: f32, + w: f32, + d: f32, + aa: f32, + u0: f32, + u1: f32, +) { + px := p.x + dx * d + py := p.y + dy * d + dlx := dy + dly := -dx + __vset(dst, px + dlx*w, py + dly*w, u0,1) + __vset(dst, px - dlx*w, py - dly*w, u1,1) + __vset(dst, px + dlx*w + dx*aa, py + dly*w + dy*aa, u0,0) + __vset(dst, px - dlx*w + dx*aa, py - dly*w + dy*aa, u1,0) +} + +__roundCapStart :: proc( + dst: ^[]Vertex, + p: ^Point, + dx, dy: f32, + w: f32, + ncap: int, + u0: f32, + u1: f32, +) { + px := p.x + py := p.y + dlx := dy + dly := -dx + + for i in 0.. 0 { + iw = 1.0 / w + } + + // Calculate which joins needs extra vertices to append, and gather vertex count. + for path, i in &cache.paths { + pts := cache.points[path.first:] + p0 := &pts[path.count-1] + p1 := &pts[0] + nleft := 0 + path.nbevel = 0 + + for j in 0.. 0.000001) { + scale := 1.0 / dmr2 + if (scale > 600.0) { + scale = 600.0 + } + p1.dmx *= scale + p1.dmy *= scale + } + + // Clear flags, but keep the corner. + p1.flags = (.CORNER in p1.flags) ? { .CORNER } : {} + + // Keep track of left turns. + __cross = p1.dx * p0.dy - p0.dx * p1.dy + if __cross > 0.0 { + nleft += 1 + incl(&p1.flags, PointFlag.LEFT) + } + + // Calculate if we should use bevel or miter for inner join. + limit = max(1.01, min(p0.len, p1.len) * iw) + if (dmr2 * limit * limit) < 1.0 { + incl(&p1.flags, PointFlag.INNER_BEVEL) + } + + // Check to see if the corner needs to be beveled. + if .CORNER in p1.flags { + if (dmr2 * miterLimit*miterLimit) < 1.0 || lineJoin == .BEVEL || lineJoin == .ROUND { + incl(&p1.flags, PointFlag.BEVEL) + } + } + + if (.BEVEL in p1.flags) || (.INNER_BEVEL in p1.flags) { + path.nbevel += 1 + } + + p0 = p1 + p1 = mem.ptr_offset(p1, 1) + } + + path.convex = nleft == path.count + } +} + +// TODO could be done better? or not need dynamic +__allocTempVerts :: proc(ctx: ^Context, nverts: int) -> []Vertex { + resize(&ctx.cache.verts, nverts) + return ctx.cache.verts[:] +} + +__expandStroke :: proc( + ctx: ^Context, + w: f32, + fringe: f32, + lineCap: LineCapType, + lineJoin: LineCapType, + miterLimit: f32, +) -> bool { + cache := &ctx.cache + aa := fringe + u0 := f32(0.0) + u1 := f32(1.0) + ncap := __curveDivs(w, math.PI, ctx.tessTol) // Calculate divisions per half circle. + + w := w + w += aa * 0.5 + + // Disable the gradient used for antialiasing when antialiasing is not used. + if aa == 0.0 { + u0 = 0.5 + u1 = 0.5 + } + + __calculateJoins(ctx, w, lineJoin, miterLimit) + + // Calculate max vertex usage. + cverts := 0 + for path in &cache.paths { + loop := path.closed + + // TODO check if f32 calculation necessary? + if lineJoin == .ROUND { + cverts += (path.count + path.nbevel * int(ncap + 2) + 1) * 2 // plus one for loop + } else { + cverts += (path.count + path.nbevel*5 + 1) * 2 // plus one for loop + } + + if !loop { + // space for caps + if lineCap == .ROUND { + cverts += int(ncap*2 + 2)*2 + } else { + cverts += (3 + 3)*2 + } + } + } + + verts := __allocTempVerts(ctx, cverts) + dst_index: int + + for i in 0.. bool { + cache := &ctx.cache + aa := ctx.fringeWidth + fringe := w > 0.0 + __calculateJoins(ctx, w, lineJoin, miterLimit) + + // Calculate max vertex usage. + cverts := 0 + for path in &cache.paths { + cverts += path.count + path.nbevel + 1 + + if fringe { + cverts += (path.count + path.nbevel*5 + 1) * 2 // plus one for loop + } + } + + convex := len(cache.paths) == 1 && cache.paths[0].convex + verts := __allocTempVerts(ctx, cverts) + dst_index: int + + for path in &cache.paths { + pts := cache.points[path.first:] + p0, p1: ^Point + rw, lw, woff: f32 + ru, lu: f32 + + // Calculate shape vertices. + woff = 0.5*aa + dst := verts[dst_index:] + dst_start_length := len(dst) + + if fringe { + // Looping + p0 = &pts[path.count-1] + p1 = &pts[0] + + for j in 0.. f32 { + return f32(cmd) +} + +// Clears the current path and sub-paths. +BeginPath :: proc(ctx: ^Context) { + clear(&ctx.commands) + __clearPathCache(ctx) +} + +@(deferred_in=Fill) +FillScoped :: proc(ctx: ^Context) { + BeginPath(ctx) +} + +@(deferred_in=Stroke) +StrokeScoped :: proc(ctx: ^Context) { + BeginPath(ctx) +} + +@(deferred_in=Stroke) +FillStrokeScoped :: proc(ctx: ^Context) { + BeginPath(ctx) +} + +// Starts new sub-path with specified point as first point. +MoveTo :: proc(ctx: ^Context, x, y: f32) { + values := [3]f32 { __cmdf(.MOVE_TO), x, y } + __appendCommands(ctx, values[:]) +} + +// Adds line segment from the last point in the path to the specified point. +LineTo :: proc(ctx: ^Context, x, y: f32) { + values := [3]f32 { __cmdf(.LINE_TO), x, y } + __appendCommands(ctx, values[:]) +} + +// Adds cubic bezier segment from last point in the path via two control points to the specified point. +BezierTo :: proc( + ctx: ^Context, + c1x, c1y: f32, + c2x, c2y: f32, + x, y: f32, +) { + values := [?]f32 { __cmdf(.BEZIER_TO), c1x, c1y, c2x, c2y, x, y } + __appendCommands(ctx, values[:]) +} + +// Adds quadratic bezier segment from last point in the path via a control point to the specified point. +QuadTo :: proc(ctx: ^Context, cx, cy, x, y: f32) { + x0 := ctx.commandx + y0 := ctx.commandy + values := [?]f32 { + __cmdf(.BEZIER_TO), + x0 + 2 / 3 * (cx - x0), + y0 + 2 / 3 * (cy - y0), + x + 2 / 3 * (cx - x), + y + 2 / 3 * (cy - y), + x, + y, + } + __appendCommands(ctx, values[:]) +} + +// Adds an arc segment at the corner defined by the last path point, and two specified points. +ArcTo :: proc( + ctx: ^Context, + x1, y1: f32, + x2, y2: f32, + radius: f32, +) { + if len(ctx.commands) == 0 { + return + } + + x0 := ctx.commandx + y0 := ctx.commandy + // Handle degenerate cases. + if __ptEquals(x0,y0, x1,y1, ctx.distTol) || + __ptEquals(x1,y1, x2,y2, ctx.distTol) || + __distPtSeg(x1,y1, x0,y0, x2,y2) < ctx.distTol*ctx.distTol || + radius < ctx.distTol { + LineTo(ctx, x1, y1) + return + } + + // Calculate tangential circle to lines (x0,y0)-(x1,y1) and (x1,y1)-(x2,y2). + dx0 := x0-x1 + dy0 := y0-y1 + dx1 := x2-x1 + dy1 := y2-y1 + __normalize(&dx0,&dy0) + __normalize(&dx1,&dy1) + a := math.acos(dx0*dx1 + dy0*dy1) + d := radius / math.tan(a / 2.0) + + if d > 10000 { + LineTo(ctx, x1, y1) + return + } + + a0, a1, cx, cy: f32 + direction: Winding + + if __cross(dx0,dy0, dx1,dy1) > 0.0 { + cx = x1 + dx0*d + dy0*radius + cy = y1 + dy0*d + -dx0*radius + a0 = math.atan2(dx0, -dy0) + a1 = math.atan2(-dx1, dy1) + direction = .CW + } else { + cx = x1 + dx0*d + -dy0*radius + cy = y1 + dy0*d + dx0*radius + a0 = math.atan2(-dx0, dy0) + a1 = math.atan2(dx1, -dy1) + direction = .CCW + } + + Arc(ctx, cx, cy, radius, a0, a1, direction) +} + +// Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r, +// and the arc is drawn from angle a0 to a1, and swept in direction dir (NVG_CCW, or NVG_CW). +// Angles are specified in radians. +Arc :: proc(ctx: ^Context, cx, cy, r, a0, a1: f32, dir: Winding) { + move: Commands = .LINE_TO if len(ctx.commands) > 0 else .MOVE_TO + + // Clamp angles + da := a1 - a0 + if dir == .CW { + if abs(da) >= math.PI*2 { + da = math.PI*2 + } else { + for da < 0.0 { + da += math.PI*2 + } + } + } else { + if abs(da) >= math.PI*2 { + da = -math.PI*2 + } else { + for da > 0.0 { + da -= math.PI*2 + } + } + } + + // Split arc into max 90 degree segments. + ndivs := max(1, min((int)(abs(da) / (math.PI*0.5) + 0.5), 5)) + hda := (da / f32(ndivs)) / 2.0 + kappa := abs(4.0 / 3.0 * (1.0 - math.cos(hda)) / math.sin(hda)) + + if dir == .CCW { + kappa = -kappa + } + + values: [3 + 5 * 7 + 100]f32 + nvals := 0 + + px, py, ptanx, ptany: f32 + for i in 0..=ndivs { + a := a0 + da * f32(i) / f32(ndivs) + dx := math.cos(a) + dy := math.sin(a) + x := cx + dx*r + y := cy + dy*r + tanx := -dy*r*kappa + tany := dx*r*kappa + + if i == 0 { + values[nvals] = __cmdf(move); nvals += 1 + values[nvals] = x; nvals += 1 + values[nvals] = y; nvals += 1 + } else { + values[nvals] = __cmdf(.BEZIER_TO); nvals += 1 + values[nvals] = px + ptanx; nvals += 1 + values[nvals] = py + ptany; nvals += 1 + values[nvals] = x-tanx; nvals += 1 + values[nvals] = y-tany; nvals += 1 + values[nvals] = x; nvals += 1 + values[nvals] = y; nvals += 1 + } + px = x + py = y + ptanx = tanx + ptany = tany + } + + // stored internally + __appendCommands(ctx, values[:nvals]) +} + +// Closes current sub-path with a line segment. +ClosePath :: proc(ctx: ^Context) { + values := [1]f32 { __cmdf(.CLOSE) } + __appendCommands(ctx, values[:]) +} + +// Sets the current sub-path winding, see NVGwinding and NVGsolidity. +PathWinding :: proc(ctx: ^Context, direction: Winding) { + values := [2]f32 { __cmdf(.WINDING), f32(direction) } + __appendCommands(ctx, values[:]) +} + +// same as path_winding but with different enum +PathSolidity :: proc(ctx: ^Context, solidity: Solidity) { + values := [2]f32 { __cmdf(.WINDING), f32(solidity) } + __appendCommands(ctx, values[:]) +} + +// Creates new rectangle shaped sub-path. +Rect :: proc(ctx: ^Context, x, y, w, h: f32) { + values := [?]f32 { + __cmdf(.MOVE_TO), x, y, + __cmdf(.LINE_TO), x, y + h, + __cmdf(.LINE_TO), x + w, y + h, + __cmdf(.LINE_TO), x + w, y, + __cmdf(.CLOSE), + } + __appendCommands(ctx, values[:]) +} + +// Creates new rounded rectangle shaped sub-path. +RoundedRect :: proc(ctx: ^Context, x, y, w, h, radius: f32) { + RoundedRectVarying(ctx, x, y, w, h, radius, radius, radius, radius) +} + +// Creates new rounded rectangle shaped sub-path with varying radii for each corner. +RoundedRectVarying :: proc( + ctx: ^Context, + x, y: f32, + w, h: f32, + radius_top_left: f32, + radius_top_right: f32, + radius_bottom_right: f32, + radius_bottom_left: f32, +) { + if radius_top_left < 0.1 && radius_top_right < 0.1 && radius_bottom_right < 0.1 && radius_bottom_left < 0.1 { + Rect(ctx, x, y, w, h) + } else { + halfw := abs(w) * 0.5 + halfh := abs(h) * 0.5 + rxBL := min(radius_bottom_left, halfw) * math.sign(w) + ryBL := min(radius_bottom_left, halfh) * math.sign(h) + rxBR := min(radius_bottom_right, halfw) * math.sign(w) + ryBR := min(radius_bottom_right, halfh) * math.sign(h) + rxTR := min(radius_top_right, halfw) * math.sign(w) + ryTR := min(radius_top_right, halfh) * math.sign(h) + rxTL := min(radius_top_left, halfw) * math.sign(w) + ryTL := min(radius_top_left, halfh) * math.sign(h) + values := [?]f32 { + __cmdf(.MOVE_TO), x, y + ryTL, + __cmdf(.LINE_TO), x, y + h - ryBL, + __cmdf(.BEZIER_TO), x, y + h - ryBL*(1 - KAPPA), x + rxBL*(1 - KAPPA), y + h, x + rxBL, y + h, + __cmdf(.LINE_TO), x + w - rxBR, y + h, + __cmdf(.BEZIER_TO), x + w - rxBR*(1 - KAPPA), y + h, x + w, y + h - ryBR*(1 - KAPPA), x + w, y + h - ryBR, + __cmdf(.LINE_TO), x + w, y + ryTR, + __cmdf(.BEZIER_TO), x + w, y + ryTR*(1 - KAPPA), x + w - rxTR*(1 - KAPPA), y, x + w - rxTR, y, + __cmdf(.LINE_TO), x + rxTL, y, + __cmdf(.BEZIER_TO), x + rxTL*(1 - KAPPA), y, x, y + ryTL*(1 - KAPPA), x, y + ryTL, + __cmdf(.CLOSE), + } + __appendCommands(ctx, values[:]) + } +} + +// Creates new ellipse shaped sub-path. +Ellipse :: proc(ctx: ^Context, cx, cy, rx, ry: f32) { + values := [?]f32 { + __cmdf(.MOVE_TO), cx-rx, cy, + __cmdf(.BEZIER_TO), cx-rx, cy+ry*KAPPA, cx-rx*KAPPA, cy+ry, cx, cy+ry, + __cmdf(.BEZIER_TO), cx+rx*KAPPA, cy+ry, cx+rx, cy+ry*KAPPA, cx+rx, cy, + __cmdf(.BEZIER_TO), cx+rx, cy-ry*KAPPA, cx+rx*KAPPA, cy-ry, cx, cy-ry, + __cmdf(.BEZIER_TO), cx-rx*KAPPA, cy-ry, cx-rx, cy-ry*KAPPA, cx-rx, cy, + __cmdf(.CLOSE) + } + __appendCommands(ctx, values[:]) +} + +// Creates new circle shaped sub-path. +Circle :: #force_inline proc(ctx: ^Context, cx, cy: f32, radius: f32) { + Ellipse(ctx, cx, cy, radius, radius) +} + +// Fills the current path with current fill style. +Fill :: proc(ctx: ^Context) { + state := __getState(ctx) + fill_paint := state.fill + + __flattenPaths(ctx) + + if ctx.params.edgeAntiAlias && state.shapeAntiAlias { + __expandFill(ctx, ctx.fringeWidth, .MITER, 2.4) + } else { + __expandFill(ctx, 0, .MITER, 2.4) + } + + // apply global alpha + fill_paint.innerColor.a *= state.alpha + fill_paint.outerColor.a *= state.alpha + + assert(ctx.params.renderFill != nil) + ctx.params.renderFill( + ctx.params.userPtr, + &fill_paint, + state.compositeOperation, + &state.scissor, + ctx.fringeWidth, + ctx.cache.bounds, + ctx.cache.paths[:], + ) + + for path in &ctx.cache.paths { + ctx.fillTriCount += len(path.fill) - 2 + ctx.fillTriCount += len(path.stroke) - 2 + ctx.drawCallCount += 2 + } +} + +// Fills the current path with current stroke style. +Stroke :: proc(ctx: ^Context) { + state := __getState(ctx) + scale := __getAverageScale(state.xform[:]) + strokeWidth := clamp(state.strokeWidth * scale, 0, 200) + stroke_paint := state.stroke + + if strokeWidth < ctx.fringeWidth { + // If the stroke width is less than pixel size, use alpha to emulate coverage. + // Since coverage is area, scale by alpha*alpha. + alpha := clamp(strokeWidth / ctx.fringeWidth, 0, 1) + stroke_paint.innerColor.a *= alpha * alpha + stroke_paint.outerColor.a *= alpha * alpha + strokeWidth = ctx.fringeWidth + } + + // apply global alpha + stroke_paint.innerColor.a *= state.alpha + stroke_paint.outerColor.a *= state.alpha + + __flattenPaths(ctx) + + if ctx.params.edgeAntiAlias && state.shapeAntiAlias { + __expandStroke(ctx, strokeWidth * 0.5, ctx.fringeWidth, state.lineCap, state.lineJoin, state.miterLimit) + } else { + __expandStroke(ctx, strokeWidth * 0.5, 0, state.lineCap, state.lineJoin, state.miterLimit) + } + + assert(ctx.params.renderStroke != nil) + ctx.params.renderStroke( + ctx.params.userPtr, + &stroke_paint, + state.compositeOperation, + &state.scissor, + ctx.fringeWidth, + strokeWidth, + ctx.cache.paths[:], + ) + + for path in &ctx.cache.paths { + ctx.strokeTriCount += len(path.stroke) - 2 + ctx.drawCallCount += 1 + } +} + +DebugDumpPathCache :: proc(ctx: ^Context) { + fmt.printf("~~~~~~~~~~~~~Dumping %d cached paths\n", len(ctx.cache.paths)) + + for path, i in &ctx.cache.paths { + fmt.printf(" - Path %d\n", i) + + if len(path.fill) != 0 { + fmt.printf(" - fill: %d\n", len(path.fill)) + + for j in 0.. int { + return fontstash.AddFontPath(&ctx.fs, name, filename) +} + +// Creates font by loading it from the specified memory chunk. +// Returns handle to the font. +CreateFontMem :: proc(ctx: ^Context, name: string, slice: []byte) -> int { + return fontstash.AddFontMem(&ctx.fs, name, slice) +} + +// Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. +FindFont :: proc(ctx: ^Context, name: string) -> int { + if name == "" { + return -1 + } + + return fontstash.GetFontByName(&ctx.fs, name) +} + +// Adds a fallback font by handle. +AddFallbackFontId :: proc(ctx: ^Context, base_font, fallback_font: int) -> bool { + if base_font == -1 || fallback_font == -1 { + return false + } + + return fontstash.AddFallbackFont(&ctx.fs, base_font, fallback_font) +} + +// Adds a fallback font by name. +AddFallbackFont :: proc(ctx: ^Context, base_font: string, fallback_font: string) -> bool { + return AddFallbackFontId( + ctx, + FindFont(ctx, base_font), + FindFont(ctx, fallback_font), + ) +} + +// Resets fallback fonts by handle. +ResetFallbackFontsId :: proc(ctx: ^Context, base_font: int) { + fontstash.ResetFallbackFont(&ctx.fs, base_font) +} + +// Resets fallback fonts by name. +ResetFallbackFonts :: proc(ctx: ^Context, base_font: string) { + fontstash.ResetFallbackFont(&ctx.fs, FindFont(ctx, base_font)) +} + +// Sets the font size of current text style. +FontSize :: proc(ctx: ^Context, size: f32) { + state := __getState(ctx) + state.fontSize = size +} + +// Sets the blur of current text style. +FontBlur :: proc(ctx: ^Context, blur: f32) { + state := __getState(ctx) + state.fontBlur = blur +} + +// Sets the letter spacing of current text style. +TextLetterSpacing :: proc(ctx: ^Context, spacing: f32) { + state := __getState(ctx) + state.letterSpacing = spacing +} + +// Sets the proportional line height of current text style. The line height is specified as multiple of font size. +TextLineHeight :: proc(ctx: ^Context, lineHeight: f32) { + state := __getState(ctx) + state.lineHeight = lineHeight +} + +// Sets the horizontal text align of current text style +TextAlignHorizontal :: proc(ctx: ^Context, align: AlignHorizontal) { + state := __getState(ctx) + state.alignHorizontal = align +} + +// Sets the vertical text align of current text style +TextAlignVertical :: proc(ctx: ^Context, align: AlignVertical) { + state := __getState(ctx) + state.alignVertical = align +} + +// Sets the text align of current text style, see NVGalign for options. +TextAlign :: proc(ctx: ^Context, ah: AlignHorizontal, av: AlignVertical) { + state := __getState(ctx) + state.alignHorizontal = ah + state.alignVertical = av +} + +// Sets the font face based on specified name of current text style. +FontFaceId :: proc(ctx: ^Context, font: int) { + state := __getState(ctx) + state.fontId = font +} + +// Sets the font face based on specified name of current text style. +FontFace :: proc(ctx: ^Context, font: string) { + state := __getState(ctx) + state.fontId = fontstash.GetFontByName(&ctx.fs, font) +} + +__quantize :: proc(a, d: f32) -> f32 { + return f32(int(a / d + 0.5)) * d +} + +__getFontScale :: proc(state: ^State) -> f32 { + return min(__quantize(__getAverageScale(state.xform[:]), 0.01), 4.0) +} + +__flushTextTexture :: proc(ctx: ^Context) { + dirty: [4]f32 + assert(ctx.params.renderUpdateTexture != nil) + + + if fontstash.ValidateTexture(&ctx.fs, &dirty) { + font_image := ctx.fontImages[ctx.fontImageIdx] + + // Update texture + if font_image != 0 { + data := ctx.fs.textureData + x := dirty[0] + y := dirty[1] + w := dirty[2] - dirty[0] + h := dirty[3] - dirty[1] + ctx.params.renderUpdateTexture(ctx.params.userPtr, font_image, int(x), int(y), int(w), int(h), data) + } + } +} + +__allocTextAtlas :: proc(ctx: ^Context) -> bool { + __flushTextTexture(ctx) + + if ctx.fontImageIdx >= MAX_FONTIMAGES - 1 { + return false + } + + // if next fontImage already have a texture + iw, ih: int + if ctx.fontImages[ctx.fontImageIdx+1] != 0 { + iw, ih = ImageSize(ctx, ctx.fontImages[ctx.fontImageIdx+1]) + } else { // calculate the new font image size and create it. + iw, ih = ImageSize(ctx, ctx.fontImages[ctx.fontImageIdx]) + + if iw > ih { + ih *= 2 + } else { + iw *= 2 + } + + if iw > MAX_FONTIMAGE_SIZE || ih > MAX_FONTIMAGE_SIZE { + iw = MAX_FONTIMAGE_SIZE + ih = MAX_FONTIMAGE_SIZE + } + + ctx.fontImages[ctx.fontImageIdx + 1] = ctx.params.renderCreateTexture(ctx.params.userPtr, .Alpha, iw, ih, {}, nil) + } + + ctx.fontImageIdx += 1 + fontstash.ResetAtlas(&ctx.fs, iw, ih) + + return true +} + +__renderText :: proc(ctx: ^Context, verts: []Vertex) { + // disallow 0 + if len(verts) == 0 { + return + } + + state := __getState(ctx) + paint := state.fill + + // Render triangles. + paint.image = ctx.fontImages[ctx.fontImageIdx] + + // Apply global alpha + paint.innerColor.a *= state.alpha + paint.outerColor.a *= state.alpha + + ctx.params.renderTriangles(ctx.params.userPtr, &paint, state.compositeOperation, &state.scissor, verts, ctx.fringeWidth) + + ctx.drawCallCount += 1 + ctx.textTriCount += len(verts) / 3 +} + +__isTransformFlipped :: proc(xform: []f32) -> bool { + det := xform[0] * xform[3] - xform[2] * xform[1] + return det < 0 +} + +// draw a single codepoint, useful for icons +TextIcon :: proc(ctx: ^Context, x, y: f32, codepoint: rune) -> f32 { + state := __getState(ctx) + scale := __getFontScale(state) * ctx.devicePxRatio + invscale := f32(1.0) / scale + is_flipped := __isTransformFlipped(state.xform[:]) + + if state.fontId == -1 { + return x + } + + fs := &ctx.fs + fontstash.SetSize(fs, state.fontSize * scale) + fontstash.SetSpacing(fs, state.letterSpacing * scale) + fontstash.SetBlur(fs, state.fontBlur * scale) + fontstash.SetAlignHorizontal(fs, state.alignHorizontal) + fontstash.SetAlignVertical(fs, state.alignVertical) + fontstash.SetFont(fs, state.fontId) + + // fontstash internals + fstate := fontstash.__getState(fs) + font := fontstash.__getFont(fs, state.fontId) + isize := i16(fstate.size * 10) + iblur := i16(fstate.blur) + glyph := fontstash.__getGlyph(fs, font, codepoint, isize, iblur) + fscale := fontstash.__getPixelHeightScale(font, f32(isize) / 10) + + // transform x / y + x := x * scale + y := y * scale + switch fstate.ah { + case .LEFT: {} + case .CENTER: { + width := fontstash.CodepointWidth(font, codepoint, fscale) + x = math.round(x - width * 0.5) + } + case .RIGHT: { + width := fontstash.CodepointWidth(font, codepoint, fscale) + x -= width + } + } + + // align vertically + y = math.round(y + fontstash.__getVerticalAlign(fs, font, fstate.av, isize)) + nextx := f32(x) + nexty := f32(y) + + if glyph != nil { + q: fontstash.Quad + fontstash.__getQuad(fs, font, -1, glyph, fscale, fstate.spacing, &nextx, &nexty, &q) + + if is_flipped { + q.y0, q.y1 = q.y1, q.y0 + q.t0, q.t1 = q.t1, q.t0 + } + + // single glyph only + verts := __allocTempVerts(ctx, 6) + c: [4 * 2]f32 + + // Transform corners. + TransformPoint(&c[0], &c[1], state.xform, q.x0 * invscale, q.y0 * invscale) + TransformPoint(&c[2], &c[3], state.xform, q.x1 * invscale, q.y0 * invscale) + TransformPoint(&c[4], &c[5], state.xform, q.x1 * invscale, q.y1 * invscale) + TransformPoint(&c[6], &c[7], state.xform, q.x0 * invscale, q.y1 * invscale) + + // Create triangles + verts[0] = { c[0], c[1], q.s0, q.t0 } + verts[1] = { c[4], c[5], q.s1, q.t1 } + verts[2] = { c[2], c[3], q.s1, q.t0 } + verts[3] = { c[0], c[1], q.s0, q.t0 } + verts[4] = { c[6], c[7], q.s0, q.t1 } + verts[5] = { c[4], c[5], q.s1, q.t1 } + + ctx.textureDirty = true + __renderText(ctx, verts[:]) + } + + return nextx / scale +} + +// Draws text string at specified location. If end is specified only the sub-string up to the end is drawn. +Text :: proc(ctx: ^Context, x, y: f32, text: string) -> f32 { + state := __getState(ctx) + scale := __getFontScale(state) * ctx.devicePxRatio + invscale := f32(1.0) / scale + is_flipped := __isTransformFlipped(state.xform[:]) + + if state.fontId == -1 { + return x + } + + fs := &ctx.fs + fontstash.SetSize(fs, state.fontSize * scale) + fontstash.SetSpacing(fs, state.letterSpacing * scale) + fontstash.SetBlur(fs, state.fontBlur * scale) + fontstash.SetAlignHorizontal(fs, state.alignHorizontal) + fontstash.SetAlignVertical(fs, state.alignVertical) + fontstash.SetFont(fs, state.fontId) + + cverts := max(2, len(text)) * 6 // conservative estimate. + verts := __allocTempVerts(ctx, cverts) + nverts: int + + iter := fontstash.TextIterInit(fs, x * scale, y * scale, text) + prev_iter := iter + q: fontstash.Quad + for fontstash.TextIterNext(&ctx.fs, &iter, &q) { + c: [4 * 2]f32 + + if iter.previousGlyphIndex == -1 { // can not retrieve glyph? + if nverts != 0 { + __renderText(ctx, verts[:]) + nverts = 0 + } + + if !__allocTextAtlas(ctx) { + break // no memory :( + } + + iter = prev_iter + fontstash.TextIterNext(fs, &iter, &q) // try again + + if iter.previousGlyphIndex == -1 { + // still can not find glyph? + break + } + } + + prev_iter = iter + if is_flipped { + q.y0, q.y1 = q.y1, q.y0 + q.t0, q.t1 = q.t1, q.t0 + } + + // Transform corners. + TransformPoint(&c[0], &c[1], state.xform, q.x0 * invscale, q.y0 * invscale) + TransformPoint(&c[2], &c[3], state.xform, q.x1 * invscale, q.y0 * invscale) + TransformPoint(&c[4], &c[5], state.xform, q.x1 * invscale, q.y1 * invscale) + TransformPoint(&c[6], &c[7], state.xform, q.x0 * invscale, q.y1 * invscale) + + // Create triangles + if nverts + 6 <= cverts { + verts[nverts] = { c[0], c[1], q.s0, q.t0 } + verts[nverts + 1] = { c[4], c[5], q.s1, q.t1 } + verts[nverts + 2] = { c[2], c[3], q.s1, q.t0 } + verts[nverts + 3] = { c[0], c[1], q.s0, q.t0 } + verts[nverts + 4] = { c[6], c[7], q.s0, q.t1 } + verts[nverts + 5] = { c[4], c[5], q.s1, q.t1 } + nverts += 6 + } + } + + ctx.textureDirty = true + __renderText(ctx, verts[:nverts]) + + return iter.nextx / scale +} + +// Returns the vertical metrics based on the current text style. +// Measured values are returned in local coordinate space. +TextMetrics :: proc(ctx: ^Context) -> (ascender, descender, lineHeight: f32) { + state := __getState(ctx) + scale := __getFontScale(state) * ctx.devicePxRatio + invscale := f32(1.0) / scale + + if state.fontId == -1 { + return + } + + fs := &ctx.fs + fontstash.SetSize(fs, state.fontSize*scale) + fontstash.SetSpacing(fs, state.letterSpacing*scale) + fontstash.SetBlur(fs, state.fontBlur*scale) + fontstash.SetAlignHorizontal(fs, state.alignHorizontal) + fontstash.SetAlignVertical(fs, state.alignVertical) + fontstash.SetFont(fs, state.fontId) + + ascender, descender, lineHeight = fontstash.VerticalMetrics(fs) + ascender *= invscale + descender *= invscale + lineHeight *= invscale + return +} + +// Measures the specified text string. Parameter bounds should be a pointer to float[4], +// if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] +// Returns the horizontal advance of the measured text (i.e. where the next character should drawn). +// Measured values are returned in local coordinate space. +TextBounds :: proc( + ctx: ^Context, + x, y: f32, + input: string, + bounds: ^[4]f32 = nil, +) -> (advance: f32) { + state := __getState(ctx) + scale := __getFontScale(state) * ctx.devicePxRatio + invscale := f32(1.0) / scale + + if state.fontId == -1 { + return {} + } + + fs := &ctx.fs + fontstash.SetSize(fs, state.fontSize*scale) + fontstash.SetSpacing(fs, state.letterSpacing*scale) + fontstash.SetBlur(fs, state.fontBlur*scale) + fontstash.SetAlignHorizontal(fs, state.alignHorizontal) + fontstash.SetAlignVertical(fs, state.alignVertical) + fontstash.SetFont(fs, state.fontId) + + width := fontstash.TextBounds(fs, input, x * scale, y * scale, bounds) + + // Use line bounds for height. + one, two := fontstash.LineBounds(fs, y * scale) + + if bounds != nil { + bounds[1] = one + bounds[3] = two + bounds[0] *= invscale + bounds[1] *= invscale + bounds[2] *= invscale + bounds[3] *= invscale + } + + return width * invscale +} + +// text row with relative byte offsets into a string +Text_Row :: struct { + start: int, + end: int, + next: int, + width: f32, + minx, maxx: f32, +} + +Codepoint_Type :: enum { + Space, + Newline, + Char, + CJK, +} + +// Draws multi-line text string at specified location wrapped at the specified width. If end is specified only the sub-string up to the end is drawn. +// White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. +// Words longer than the max width are slit at nearest character (i.e. no hyphenation). +TextBox :: proc( + ctx: ^Context, + x, y: f32, + break_row_width: f32, + input: string, +) { + state := __getState(ctx) + rows: [2]Text_Row + + if state.fontId == -1 { + return + } + + _, _, lineHeight := TextMetrics(ctx) + old_align := state.alignHorizontal + defer state.alignHorizontal = old_align + state.alignHorizontal = .LEFT + rows_mod := rows[:] + + y := y + input := input + for nrows, input_last in TextBreakLines(ctx, &input, break_row_width, &rows_mod) { + for i in 0.. (nrows: int, last: string, ok: bool) { + state := __getState(ctx) + scale := __getFontScale(state) * ctx.devicePxRatio + invscale := 1.0 / scale + + row_start_x, row_width, row_min_x, row_max_x: f32 + max_rows := len(rows) + + row_start: int = -1 + row_end: int = -1 + word_start: int = -1 + break_end: int = -1 + word_start_x, word_min_x: f32 + + break_width, break_max_x: f32 + type := Codepoint_Type.Space + ptype := Codepoint_Type.Space + pcodepoint: rune + + if max_rows == 0 || state.fontId == -1 || len(text) == 0 { + return + } + + fs := &ctx.fs + fontstash.SetSize(fs, state.fontSize * scale) + fontstash.SetSpacing(fs, state.letterSpacing * scale) + fontstash.SetBlur(fs, state.fontBlur * scale) + fontstash.SetAlignHorizontal(fs, state.alignHorizontal) + fontstash.SetAlignVertical(fs, state.alignVertical) + fontstash.SetFont(fs, state.fontId) + + break_row_width := break_row_width * scale + iter := fontstash.TextIterInit(fs, 0, 0, text^) + prev_iter := iter + q: fontstash.Quad + stopped_early: bool + + for fontstash.TextIterNext(fs, &iter, &q) { + if iter.previousGlyphIndex < 0 && __allocTextAtlas(ctx) { // can not retrieve glyph? + iter = prev_iter + fontstash.TextIterNext(fs, &iter, &q) // try again + } + prev_iter = iter + + switch iter.codepoint { + case '\t', '\v', '\f', ' ', 0x00a0: { + // NBSP + type = .Space + } + + case '\n': { + type = pcodepoint == 13 ? .Space : .Newline + } + + case '\r': { + type = pcodepoint == 10 ? .Space : .Newline + } + + case 0x0085: { + // NEL + type = .Newline + } + + case: { + if (iter.codepoint >= 0x4E00 && iter.codepoint <= 0x9FFF) || + (iter.codepoint >= 0x3000 && iter.codepoint <= 0x30FF) || + (iter.codepoint >= 0xFF00 && iter.codepoint <= 0xFFEF) || + (iter.codepoint >= 0x1100 && iter.codepoint <= 0x11FF) || + (iter.codepoint >= 0x3130 && iter.codepoint <= 0x318F) || + (iter.codepoint >= 0xAC00 && iter.codepoint <= 0xD7AF) { + type = .CJK + } else { + type = .Char + } + } + } + + if type == .Newline { + // Always handle new lines. + rows[nrows].start = row_start != -1 ? row_start : iter.str + rows[nrows].end = row_end != -1 ? row_end : iter.str + rows[nrows].width = row_width * invscale + rows[nrows].minx = row_min_x * invscale + rows[nrows].maxx = row_max_x * invscale + rows[nrows].next = iter.next + nrows += 1 + + if nrows >= max_rows { + stopped_early = true + break + } + + // Set nil break point + break_end = row_start + break_width = 0.0 + break_max_x = 0.0 + // Indicate to skip the white space at the beginning of the row. + row_start = -1 + row_end = -1 + row_width = 0 + row_min_x = 0 + row_max_x = 0 + } else { + if row_start == -1 { + // Skip white space until the beginning of the line + if type == .Char || type == .CJK { + // The current char is the row so far + row_start_x = iter.x + row_start = iter.str + row_end = iter.next + row_width = iter.nextx - row_start_x + row_min_x = q.x0 - row_start_x + row_max_x = q.x1 - row_start_x + word_start = iter.str + word_start_x = iter.x + word_min_x = q.x0 - row_start_x + // Set nil break point + break_end = row_start + break_width = 0.0 + break_max_x = 0.0 + } + } else { + next_width := iter.nextx - row_start_x + + // track last non-white space character + if type == .Char || type == .CJK { + row_end = iter.next + row_width = iter.nextx - row_start_x + row_max_x = q.x1 - row_start_x + } + // track last end of a word + if ((ptype == .Char || ptype == .CJK) && type == .Space) || type == .CJK { + break_end = iter.str + break_width = row_width + break_max_x = row_max_x + } + // track last beginning of a word + if ((ptype == .Space && (type == .Char || type == .CJK)) || type == .CJK) { + word_start = iter.str + word_start_x = iter.x + word_min_x = q.x0 + } + + // Break to new line when a character is beyond break width. + if (type == .Char || type == .CJK) && next_width > break_row_width { + // The run length is too long, need to break to new line. + if (break_end == row_start) { + // The current word is longer than the row length, just break it from here. + rows[nrows].start = row_start + rows[nrows].end = iter.str + rows[nrows].width = row_width * invscale + rows[nrows].minx = row_min_x * invscale + rows[nrows].maxx = row_max_x * invscale + rows[nrows].next = iter.str + nrows += 1 + + if nrows >= max_rows { + stopped_early = true + break + } + + row_start_x = iter.x + row_start = iter.str + row_end = iter.next + row_width = iter.nextx - row_start_x + row_min_x = q.x0 - row_start_x + row_max_x = q.x1 - row_start_x + word_start = iter.str + word_start_x = iter.x + word_min_x = q.x0 - row_start_x + } else { + // Break the line from the end of the last word, and start new line from the beginning of the new. + rows[nrows].start = row_start + rows[nrows].end = break_end + rows[nrows].width = break_width * invscale + rows[nrows].minx = row_min_x * invscale + rows[nrows].maxx = break_max_x * invscale + rows[nrows].next = word_start + nrows += 1 + if nrows >= max_rows { + stopped_early = true + break + } + // Update row + row_start_x = word_start_x + row_start = word_start + row_end = iter.next + row_width = iter.nextx - row_start_x + row_min_x = word_min_x - row_start_x + row_max_x = q.x1 - row_start_x + } + // Set nil break point + break_end = row_start + break_width = 0.0 + break_max_x = 0.0 + } + } + } + + pcodepoint = iter.codepoint + ptype = type + } + + // Break the line from the end of the last word, and start new line from the beginning of the new. + if !stopped_early && row_start != -1 { + rows[nrows].start = row_start + rows[nrows].end = row_end + rows[nrows].width = row_width * invscale + rows[nrows].minx = row_min_x * invscale + rows[nrows].maxx = row_max_x * invscale + rows[nrows].next = iter.end + nrows += 1 + } + + // NOTE a bit hacky, row.start / row.end need to work with last string range + last = text^ + // advance early + next := rows[nrows - 1].next + text^ = text[next:] + // terminate the for loop on non ok + ok = nrows != 0 + + return +} + +// Measures the specified multi-text string. Parameter bounds should be a pointer to float[4], +// if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] +// Measured values are returned in local coordinate space. +TextBoxBounds :: proc( + ctx: ^Context, + x, y: f32, + breakRowWidth: f32, + input: string, + bounds: ^[4]f32, +) { + state := __getState(ctx) + rows: [2]Text_Row + scale := __getFontScale(state) * ctx.devicePxRatio + invscale := f32(1.0) / scale + + if state.fontId == -1 { + if bounds != nil { + bounds^ = {} + } + + return + } + + // alignment + halign := state.alignHorizontal + valign := state.alignVertical + old_align := state.alignHorizontal + defer state.alignHorizontal = old_align + state.alignHorizontal = .LEFT + + _, _, lineh := TextMetrics(ctx) + minx, maxx := x, x + miny, maxy := y, y + + fs := &ctx.fs + fontstash.SetSize(fs, state.fontSize * scale) + fontstash.SetSpacing(fs, state.letterSpacing * scale) + fontstash.SetBlur(fs, state.fontBlur * scale) + fontstash.SetAlignHorizontal(fs, state.alignHorizontal) + fontstash.SetAlignVertical(fs, state.alignVertical) + fontstash.SetFont(fs, state.fontId) + rminy, rmaxy := fontstash.LineBounds(fs, 0) + rminy *= invscale + rmaxy *= invscale + + input := input + rows_mod := rows[:] + y := y + + for nrows, input_last in TextBreakLines(ctx, &input, breakRowWidth, &rows_mod) { + for i in 0.. int { + state := __getState(ctx) + scale := __getFontScale(state) * ctx.devicePxRatio + + if state.fontId == -1 || len(text) == 0 { + return 0 + } + + fs := &ctx.fs + fontstash.SetSize(fs, state.fontSize*scale) + fontstash.SetSpacing(fs, state.letterSpacing*scale) + fontstash.SetBlur(fs, state.fontBlur*scale) + fontstash.SetAlignHorizontal(fs, state.alignHorizontal) + fontstash.SetAlignVertical(fs, state.alignVertical) + fontstash.SetFont(fs, state.fontId) + + iter := fontstash.TextIterInit(fs, 0, 0, text) + prev_iter := iter + q: fontstash.Quad + npos: int + for fontstash.TextIterNext(fs, &iter, &q) { + if iter.previousGlyphIndex < 0 && __allocTextAtlas(ctx) { // can not retrieve glyph? + iter = prev_iter + fontstash.TextIterNext(fs, &iter, &q) // try again + } + + prev_iter = iter + positions[npos].str = iter.str + positions[npos].x = iter.x + x + positions[npos].minx = min(iter.x, q.x0) + x + positions[npos].maxx = max(iter.nextx, q.x1) + x + npos += 1 + + if npos >= len(positions) { + break + } + } + + return npos +} \ No newline at end of file From 84a7f222ffb77317b7eca4a02e85ffeb8487833e Mon Sep 17 00:00:00 2001 From: Michael Kutowski Date: Tue, 20 Dec 2022 12:27:23 +0100 Subject: [PATCH 002/144] mention fontstash and nanovg in reamde --- vendor/README.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/vendor/README.md b/vendor/README.md index e7d8d4047..bf64933c3 100644 --- a/vendor/README.md +++ b/vendor/README.md @@ -6,9 +6,9 @@ Its use is similar to that of `core:` packages, which would be available in any Presently, the `vendor:` collection comprises the following packages: -## microui +## microui (Port) -A tiny, portable, immediate-mode UI library written in Odin. (Ported from [rxi/microui](https://github.com/rxi/microui).) +A tiny, portable, immediate-mode UI library written in Odin. [rxi/microui](https://github.com/rxi/microui) This package is available under the MIT license. See `LICENSE` for more details. @@ -158,4 +158,12 @@ Includes full bindings. Used in: [bgfx](https://github.com/bkaradzic/bgfx), [Filament](https://github.com/google/filament), [gltfpack](https://github.com/zeux/meshoptimizer/tree/master/gltf), [raylib](https://github.com/raysan5/raylib), [Unigine](https://developer.unigine.com/en/docs/2.14.1/third_party?rlang=cpp#cgltf), and more! -Se also LICENCE in `cgltf` directory itself. \ No newline at end of file +Se also LICENCE in `cgltf` directory itself. + +## nanovg (Port) + +[NanoVG](https://github.com/memononen/nanovg) is a small antialiased vector graphics rendering library for OpenGL. It has lean API modeled after HTML5 canvas API. It is aimed to be a practical and fun toolset for building scalable user interfaces and visualizations. + +## fontstash (Port) + +[Font stash](https://github.com/memononen/fontstash) is a light-weight online font texture atlas builder. It uses stb_truetype to render fonts on demand to a texture atlas. From 9474c997953fc11df1de03997809e9b4ae44760d Mon Sep 17 00:00:00 2001 From: skytrias Date: Wed, 21 Dec 2022 01:36:00 +0100 Subject: [PATCH 003/144] add freeLoadedData flag in case you dont want to remove font memory (e.g. #load) --- vendor/fontstash/fontstash.odin | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/vendor/fontstash/fontstash.odin b/vendor/fontstash/fontstash.odin index ba19d068a..f527d1e9b 100644 --- a/vendor/fontstash/fontstash.odin +++ b/vendor/fontstash/fontstash.odin @@ -50,6 +50,7 @@ Font :: struct { info: stbtt.fontinfo, loadedData: []byte, + freeLoadedData: bool, // in case you dont want loadedData to be removed ascender: f32, descender: f32, @@ -147,7 +148,11 @@ Init :: proc(using ctx: ^FontContext, w, h: int, loc: QuadLocation) { Destroy :: proc(using ctx: ^FontContext) { for font in &fonts { - delete(font.loadedData) + if font.freeLoadedData { + delete(font.loadedData) + } + + delete(font.name) delete(font.glyphs) } @@ -349,7 +354,7 @@ AddFontPath :: proc( log.panicf("FONT: failed to read font at %s", path) } - return AddFontMem(ctx, name, data) + return AddFontMem(ctx, name, data, true) } // push a font to the font stack @@ -358,10 +363,12 @@ AddFontMem :: proc( ctx: ^FontContext, name: string, data: []u8, + freeLoadedData: bool, ) -> int { append(&ctx.fonts, Font {}) res := &ctx.fonts[len(ctx.fonts) - 1] res.loadedData = data + res.freeLoadedData = freeLoadedData res.name = strings.clone(name) stbtt.InitFont(&res.info, &res.loadedData[0], 0) From 2a94b66f4da048adda1710f2e29450c8767be600 Mon Sep 17 00:00:00 2001 From: Skytrias Date: Sat, 24 Dec 2022 11:19:12 +0100 Subject: [PATCH 004/144] test --- vendor/fontstash/fontstash.odin | 110 ++++++++++---------- vendor/nanovg/gl/gl.odin | 10 +- vendor/nanovg/nanovg.odin | 171 ++++++++++++++------------------ 3 files changed, 133 insertions(+), 158 deletions(-) diff --git a/vendor/fontstash/fontstash.odin b/vendor/fontstash/fontstash.odin index f527d1e9b..b4bf86d46 100644 --- a/vendor/fontstash/fontstash.odin +++ b/vendor/fontstash/fontstash.odin @@ -816,22 +816,20 @@ __getVerticalAlign :: proc( pixelSize: i16, ) -> (res: f32) { switch ctx.location { - case .TOPLEFT: { - switch av { - case .TOP: res = font.ascender * f32(pixelSize) / 10 - case .MIDDLE: res = (font.ascender + font.descender) / 2 * f32(pixelSize) / 10 - case .BASELINE: res = 0 - case .BOTTOM: res = font.descender * f32(pixelSize) / 10 - } + case .TOPLEFT: + switch av { + case .TOP: res = font.ascender * f32(pixelSize) / 10 + case .MIDDLE: res = (font.ascender + font.descender) / 2 * f32(pixelSize) / 10 + case .BASELINE: res = 0 + case .BOTTOM: res = font.descender * f32(pixelSize) / 10 } - case .BOTTOMLEFT: { - switch av { - case .TOP: res = -font.ascender * f32(pixelSize) / 10 - case .MIDDLE: res = -(font.ascender + font.descender) / 2 * f32(pixelSize) / 10 - case .BASELINE: res = 0 - case .BOTTOM: res = -font.descender * f32(pixelSize) / 10 - } + case .BOTTOMLEFT: + switch av { + case .TOP: res = -font.ascender * f32(pixelSize) / 10 + case .MIDDLE: res = -(font.ascender + font.descender) / 2 * f32(pixelSize) / 10 + case .BASELINE: res = 0 + case .BOTTOM: res = -font.descender * f32(pixelSize) / 10 } } @@ -1005,35 +1003,33 @@ __getQuad :: proc( y1 = f32(glyph.y1 - 1) switch ctx.location { - case .TOPLEFT: { - rx = math.floor(x^ + xoff) - ry = math.floor(y^ + yoff) - - quad.x0 = rx - quad.y0 = ry - quad.x1 = rx + x1 - x0 - quad.y1 = ry + y1 - y0 + case .TOPLEFT: + rx = math.floor(x^ + xoff) + ry = math.floor(y^ + yoff) + + quad.x0 = rx + quad.y0 = ry + quad.x1 = rx + x1 - x0 + quad.y1 = ry + y1 - y0 - quad.s0 = x0 * ctx.itw - quad.t0 = y0 * ctx.ith - quad.s1 = x1 * ctx.itw - quad.t1 = y1 * ctx.ith - } + quad.s0 = x0 * ctx.itw + quad.t0 = y0 * ctx.ith + quad.s1 = x1 * ctx.itw + quad.t1 = y1 * ctx.ith - case .BOTTOMLEFT: { - rx = math.floor(x^ + xoff) - ry = math.floor(y^ - yoff) + case .BOTTOMLEFT: + rx = math.floor(x^ + xoff) + ry = math.floor(y^ - yoff) - quad.x0 = rx - quad.y0 = ry - quad.x1 = rx + x1 - x0 - quad.y1 = ry - y1 + y0 + quad.x0 = rx + quad.y0 = ry + quad.x1 = rx + x1 - x0 + quad.y1 = ry - y1 + y0 - quad.s0 = x0 * ctx.itw - quad.t0 = y0 * ctx.ith - quad.s1 = x1 * ctx.itw - quad.t1 = y1 * ctx.ith - } + quad.s0 = x0 * ctx.itw + quad.t0 = y0 * ctx.ith + quad.s1 = x1 * ctx.itw + quad.t1 = y1 * ctx.ith } x^ += f32(int(f32(glyph.xadvance) / 10 + 0.5)) @@ -1056,15 +1052,15 @@ TextIterInit :: proc( x := x y := y switch state.ah { - case .LEFT: {} - case .CENTER: { - width := TextBounds(ctx, text, x, y, nil) - x = math.round(x - width * 0.5) - } - case .RIGHT: { - width := TextBounds(ctx, text, x, y, nil) - x -= width - } + case .LEFT: {} + + case .CENTER: + width := TextBounds(ctx, text, x, y, nil) + x = math.round(x - width * 0.5) + + case .RIGHT: + width := TextBounds(ctx, text, x, y, nil) + x -= width } // align vertically @@ -1184,15 +1180,15 @@ TextBounds :: proc( // horizontal alignment advance := x - start_x switch state.ah { - case .LEFT: {} - case .CENTER: { - minx -= advance * 0.5 - maxx -= advance * 0.5 - } - case .RIGHT: { - minx -= advance - maxx -= advance - } + case .LEFT: {} + + case .CENTER: + minx -= advance * 0.5 + maxx -= advance * 0.5 + + case .RIGHT: + minx -= advance + maxx -= advance } if bounds != nil { diff --git a/vendor/nanovg/gl/gl.odin b/vendor/nanovg/gl/gl.odin index 1f4d8ceb0..72b469ddb 100644 --- a/vendor/nanovg/gl/gl.odin +++ b/vendor/nanovg/gl/gl.odin @@ -1020,11 +1020,11 @@ __renderFlush :: proc(uptr: rawptr) { __blendFuncSeparate(ctx, &call.blendFunc) switch call.type { - case .NONE: {} - case .FILL: __fill(ctx, call) - case .CONVEX_FILL: __convexFill(ctx, call) - case .STROKE: __stroke(ctx, call) - case .TRIANGLES: __triangles(ctx, call) + case .NONE: {} + case .FILL: __fill(ctx, call) + case .CONVEX_FILL: __convexFill(ctx, call) + case .STROKE: __stroke(ctx, call) + case .TRIANGLES: __triangles(ctx, call) } } diff --git a/vendor/nanovg/nanovg.odin b/vendor/nanovg/nanovg.odin index db99ef5a4..8beb08cce 100644 --- a/vendor/nanovg/nanovg.odin +++ b/vendor/nanovg/nanovg.odin @@ -1302,30 +1302,21 @@ __appendCommands :: proc(ctx: ^Context, values: []f32) { cmd := Commands(values[i]) switch cmd { - case .MOVE_TO, .LINE_TO: { - TransformPoint(&values[i + 1], &values[i + 2], state.xform, values[i + 1], values[i + 2]) - i += 3 - } + case .MOVE_TO, .LINE_TO: + TransformPoint(&values[i + 1], &values[i + 2], state.xform, values[i + 1], values[i + 2]) + i += 3 - case .BEZIER_TO: { - TransformPoint(&values[i + 1], &values[i + 2], state.xform, values[i + 1], values[i + 2]) - TransformPoint(&values[i + 3], &values[i + 4], state.xform, values[i + 3], values[i + 4]) - TransformPoint(&values[i + 5], &values[i + 6], state.xform, values[i + 5], values[i + 6]) - i += 7 - } + case .BEZIER_TO: + TransformPoint(&values[i + 1], &values[i + 2], state.xform, values[i + 1], values[i + 2]) + TransformPoint(&values[i + 3], &values[i + 4], state.xform, values[i + 3], values[i + 4]) + TransformPoint(&values[i + 5], &values[i + 6], state.xform, values[i + 5], values[i + 6]) + i += 7 - case .CLOSE: { - i += 1 - } + case .CLOSE: i += 1 + case .WINDING: i += 2 - case .WINDING: { - i += 2 - } - - // default - case: { - i += 1 - } + // default + case: i += 1 } } @@ -1510,45 +1501,38 @@ __flattenPaths :: proc(ctx: ^Context) { cmd := Commands(ctx.commands[i]) switch cmd { - case .MOVE_TO: { - __addPath(ctx) - p := ctx.commands[i + 1:] - __addPoint(ctx, p[0], p[1], { .CORNER }) - i += 3 + case .MOVE_TO: + __addPath(ctx) + p := ctx.commands[i + 1:] + __addPoint(ctx, p[0], p[1], { .CORNER }) + i += 3 + + case .LINE_TO: + p := ctx.commands[i + 1:] + __addPoint(ctx, p[0], p[1], { .CORNER }) + i += 3 + + case .BEZIER_TO: + last := __lastPoint(ctx) + + if last != nil { + cp1 := ctx.commands[i + 1:] + cp2 := ctx.commands[i + 3:] + p := ctx.commands[i + 5:] + __tesselateBezier(ctx, last.x,last.y, cp1[0],cp1[1], cp2[0],cp2[1], p[0],p[1], 0, { .CORNER }) } - case .LINE_TO: { - p := ctx.commands[i + 1:] - __addPoint(ctx, p[0], p[1], { .CORNER }) - i += 3 - } + i += 7 - case .BEZIER_TO: { - last := __lastPoint(ctx) - - if last != nil { - cp1 := ctx.commands[i + 1:] - cp2 := ctx.commands[i + 3:] - p := ctx.commands[i + 5:] - __tesselateBezier(ctx, last.x,last.y, cp1[0],cp1[1], cp2[0],cp2[1], p[0],p[1], 0, { .CORNER }) - } + case .CLOSE: + __closePath(ctx) + i += 1 - i += 7 - } + case .WINDING: + __pathWinding(ctx, Winding(ctx.commands[i + 1])) + i += 2 - case .CLOSE: { - __closePath(ctx) - i += 1 - } - - case .WINDING: { - __pathWinding(ctx, Winding(ctx.commands[i + 1])) - i += 2 - } - - case: { - i += 1 - } + case: i += 1 } } @@ -2520,7 +2504,7 @@ Ellipse :: proc(ctx: ^Context, cx, cy, rx, ry: f32) { __cmdf(.BEZIER_TO), cx+rx*KAPPA, cy+ry, cx+rx, cy+ry*KAPPA, cx+rx, cy, __cmdf(.BEZIER_TO), cx+rx, cy-ry*KAPPA, cx+rx*KAPPA, cy-ry, cx, cy-ry, __cmdf(.BEZIER_TO), cx-rx*KAPPA, cy-ry, cx-rx, cy-ry*KAPPA, cx-rx, cy, - __cmdf(.CLOSE) + __cmdf(.CLOSE), } __appendCommands(ctx, values[:]) } @@ -2674,8 +2658,8 @@ CreateFont :: proc(ctx: ^Context, name, filename: string) -> int { // Creates font by loading it from the specified memory chunk. // Returns handle to the font. -CreateFontMem :: proc(ctx: ^Context, name: string, slice: []byte) -> int { - return fontstash.AddFontMem(&ctx.fs, name, slice) +CreateFontMem :: proc(ctx: ^Context, name: string, slice: []byte, free_loaded_data: bool) -> int { + return fontstash.AddFontMem(&ctx.fs, name, slice, free_loaded_data) } // Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. @@ -2890,15 +2874,15 @@ TextIcon :: proc(ctx: ^Context, x, y: f32, codepoint: rune) -> f32 { x := x * scale y := y * scale switch fstate.ah { - case .LEFT: {} - case .CENTER: { - width := fontstash.CodepointWidth(font, codepoint, fscale) - x = math.round(x - width * 0.5) - } - case .RIGHT: { - width := fontstash.CodepointWidth(font, codepoint, fscale) - x -= width - } + case .LEFT: {} + + case .CENTER: + width := fontstash.CodepointWidth(font, codepoint, fscale) + x = math.round(x - width * 0.5) + + case .RIGHT: + width := fontstash.CodepointWidth(font, codepoint, fscale) + x -= width } // align vertically @@ -3190,35 +3174,30 @@ TextBreakLines :: proc( prev_iter = iter switch iter.codepoint { - case '\t', '\v', '\f', ' ', 0x00a0: { - // NBSP - type = .Space - } + case '\t', '\v', '\f', ' ', 0x00a0: + // NBSP + type = .Space - case '\n': { - type = pcodepoint == 13 ? .Space : .Newline - } - - case '\r': { - type = pcodepoint == 10 ? .Space : .Newline - } + case '\n': + type = pcodepoint == 13 ? .Space : .Newline + + case '\r': + type = pcodepoint == 10 ? .Space : .Newline - case 0x0085: { - // NEL - type = .Newline - } + case 0x0085: + // NEL + type = .Newline - case: { - if (iter.codepoint >= 0x4E00 && iter.codepoint <= 0x9FFF) || - (iter.codepoint >= 0x3000 && iter.codepoint <= 0x30FF) || - (iter.codepoint >= 0xFF00 && iter.codepoint <= 0xFFEF) || - (iter.codepoint >= 0x1100 && iter.codepoint <= 0x11FF) || - (iter.codepoint >= 0x3130 && iter.codepoint <= 0x318F) || - (iter.codepoint >= 0xAC00 && iter.codepoint <= 0xD7AF) { - type = .CJK - } else { - type = .Char - } + case: + if (iter.codepoint >= 0x4E00 && iter.codepoint <= 0x9FFF) || + (iter.codepoint >= 0x3000 && iter.codepoint <= 0x30FF) || + (iter.codepoint >= 0xFF00 && iter.codepoint <= 0xFFEF) || + (iter.codepoint >= 0x1100 && iter.codepoint <= 0x11FF) || + (iter.codepoint >= 0x3130 && iter.codepoint <= 0x318F) || + (iter.codepoint >= 0xAC00 && iter.codepoint <= 0xD7AF) { + type = .CJK + } else { + type = .Char } } @@ -3426,9 +3405,9 @@ TextBoxBounds :: proc( // Horizontal bounds switch halign { - case .LEFT: dx = 0 - case .CENTER: dx = breakRowWidth*0.5 - row.width*0.5 - case .RIGHT: dx = breakRowWidth - row.width + case .LEFT: dx = 0 + case .CENTER: dx = breakRowWidth*0.5 - row.width*0.5 + case .RIGHT: dx = breakRowWidth - row.width } rminx = x + row.minx + dx From 86fada718ebd8a11f3560f7b9585d74c8840f66f Mon Sep 17 00:00:00 2001 From: Skytrias Date: Sat, 24 Dec 2022 11:30:15 +0100 Subject: [PATCH 005/144] optional constants, starting GL2 fixes --- vendor/nanovg/gl/gl.odin | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/vendor/nanovg/gl/gl.odin b/vendor/nanovg/gl/gl.odin index 72b469ddb..9645c1696 100644 --- a/vendor/nanovg/gl/gl.odin +++ b/vendor/nanovg/gl/gl.odin @@ -26,9 +26,7 @@ CreateFlag :: enum { } CreateFlags :: bit_set[CreateFlag] -FRAG_BINDING :: 0 -USE_STATE_FILTER :: true -GL_UNIFORMARRAY_SIZE :: 11 +USE_STATE_FILTER :: #config(USE_STATE_FILTER, true) UniformLoc :: enum { VIEW_SIZE, @@ -92,6 +90,7 @@ Path :: struct { } when GL2_IMPLEMENTATION { + GL_UNIFORMARRAY_SIZE :: 11 FragUniforms :: struct #raw_union { using _: struct { scissorMat: [12]f32, // matrices are actually 3 vec4s @@ -128,10 +127,11 @@ when GL2_IMPLEMENTATION { } } -GL2_IMPLEMENTATION :: false -GL3_IMPLEMENTATION :: true -GLES2_IMPLEMENTATION :: false -GLES3_IMPLEMENTATION :: false +DEFAULT_IMPLEMENTATION_STRING :: #config(NANOVG_GL_IMPL, "GL3") +GL2_IMPLEMENTATION :: DEFAULT_IMPLEMENTATION_STRING == "GL2" +GL3_IMPLEMENTATION :: DEFAULT_IMPLEMENTATION_STRING == "GL3" +GLES2_IMPLEMENTATION :: DEFAULT_IMPLEMENTATION_STRING == "GLES2" +GLES3_IMPLEMENTATION :: DEFAULT_IMPLEMENTATION_STRING == "GLES3" when GL2_IMPLEMENTATION { GL2 :: true @@ -174,6 +174,7 @@ Context :: struct { fragBuf: u32, // USE_UNIFORMBUFFER fragSize: int, flags: CreateFlags, + frag_binding: u32, // Per frame buffers calls: [dynamic]Call, @@ -372,7 +373,7 @@ __renderCreate :: proc(uptr: rawptr) -> bool { when GL_USE_UNIFORMBUFFER { // Create UBOs - gl.UniformBlockBinding(ctx.shader.prog, u32(ctx.shader.loc[.FRAG]), FRAG_BINDING) + gl.UniformBlockBinding(ctx.shader.prog, u32(ctx.shader.loc[.FRAG]), ctx.frag_binding) gl.GenBuffers(1, &ctx.fragBuf) gl.GetIntegerv(gl.UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align) } @@ -436,7 +437,7 @@ __renderCreateTexture :: proc( when GL2 { if .GENERATE_MIPMAPS in imageFlags { - gl.TexParameteri(gl.TEXTURE_2D, GENERATE_MIPMAP, 1) + gl.TexParameteri(gl.TEXTURE_2D, gl.GENERATE_MIPMAP, 1) } } @@ -444,8 +445,7 @@ __renderCreateTexture :: proc( gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, i32(w), i32(h), 0, gl.RGBA, gl.UNSIGNED_BYTE, raw_data(data)) } else { when GLES2 || GL2 { - // TODO missing in odin - gl.TexImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, w, h, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, data) + gl.TexImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, i32(w), i32(h), 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, raw_data(data)) } else when GLES3 { gl.TexImage2D(gl.TEXTURE_2D, 0, gl.R8, i32(w), i32(h), 0, gl.RED, gl.UNSIGNED_BYTE, raw_data(data)) } else { @@ -783,7 +783,7 @@ __convertPaint :: proc( __setUniforms :: proc(ctx: ^Context, uniformOffset: int, image: int) { when GL_USE_UNIFORMBUFFER { - gl.BindBufferRange(gl.UNIFORM_BUFFER, FRAG_BINDING, ctx.fragBuf, uniformOffset, size_of(FragUniforms)) + gl.BindBufferRange(gl.UNIFORM_BUFFER, ctx.frag_binding, ctx.fragBuf, uniformOffset, size_of(FragUniforms)) } else { frag := __fragUniformPtr(ctx, uniformOffset) gl.Uniform4fv(ctx.shader.loc[.FRAG], GL_UNIFORMARRAY_SIZE, cast(^f32) frag) From f36e19e86fe88198fc1d17426afea577920efbf8 Mon Sep 17 00:00:00 2001 From: Skytrias Date: Sat, 24 Dec 2022 11:47:57 +0100 Subject: [PATCH 006/144] fix GL2 --- vendor/nanovg/gl/gl.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vendor/nanovg/gl/gl.odin b/vendor/nanovg/gl/gl.odin index 9645c1696..698943e2b 100644 --- a/vendor/nanovg/gl/gl.odin +++ b/vendor/nanovg/gl/gl.odin @@ -89,8 +89,9 @@ Path :: struct { strokeCount: int, } +GL_UNIFORMARRAY_SIZE :: 11 + when GL2_IMPLEMENTATION { - GL_UNIFORMARRAY_SIZE :: 11 FragUniforms :: struct #raw_union { using _: struct { scissorMat: [12]f32, // matrices are actually 3 vec4s @@ -415,7 +416,7 @@ __renderCreateTexture :: proc( // No mips. if .GENERATE_MIPMAPS in imageFlags { log.errorf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h); - excl(&imageFlags, nvg.Image_Flag.GENERATE_MIPMAPS) + excl(&imageFlags, ImageFlags { .GENERATE_MIPMAPS }) } } } @@ -643,8 +644,7 @@ __renderUpdateTexture :: proc( gl.TexSubImage2D(gl.TEXTURE_2D, 0, i32(x), i32(y), i32(w), i32(h), gl.RGBA, gl.UNSIGNED_BYTE, raw_data(data)) } else { when GLES2 || GL2 { - // TODO is missing in odin - // gl.TexSubImage2D(gl.TEXTURE_2D, 0, i32(x), i32(y), i32(w), i32(h), gl.LUMINANCE, gl.UNSIGNED_BYTE, raw_data(data)) + gl.TexSubImage2D(gl.TEXTURE_2D, 0, i32(x), i32(y), i32(w), i32(h), gl.LUMINANCE, gl.UNSIGNED_BYTE, raw_data(data)) } else { gl.TexSubImage2D(gl.TEXTURE_2D, 0, i32(x), i32(y), i32(w), i32(h), gl.RED, gl.UNSIGNED_BYTE, raw_data(data)) } From 249f42f05455fb71bd0b5e4acee445991a988699 Mon Sep 17 00:00:00 2001 From: "J.C. Moyer" Date: Sun, 21 May 2023 16:42:48 -0400 Subject: [PATCH 007/144] Add test for #2466 --- tests/issues/run.bat | 1 + tests/issues/run.sh | 1 + tests/issues/test_issue_2466.odin | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 tests/issues/test_issue_2466.odin diff --git a/tests/issues/run.bat b/tests/issues/run.bat index 87492bc29..bf49bc85b 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -12,6 +12,7 @@ set COMMON=-collection:tests=..\.. ..\..\..\odin test ..\test_issue_2056.odin %COMMON% -file || exit /b ..\..\..\odin test ..\test_issue_2087.odin %COMMON% -file || exit /b ..\..\..\odin build ..\test_issue_2113.odin %COMMON% -file -debug || exit /b +..\..\..\odin test ..\test_issue_2466.odin %COMMON% -file || exit /b @echo off diff --git a/tests/issues/run.sh b/tests/issues/run.sh index f894f2dae..bbcd6fb28 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -13,6 +13,7 @@ $ODIN test ../test_issue_1592.odin $COMMON -file $ODIN test ../test_issue_2056.odin $COMMON -file $ODIN test ../test_issue_2087.odin $COMMON -file $ODIN build ../test_issue_2113.odin $COMMON -file -debug +$ODIN test ../test_issue_2466.odin $COMMON -file set +x diff --git a/tests/issues/test_issue_2466.odin b/tests/issues/test_issue_2466.odin new file mode 100644 index 000000000..4810cfea9 --- /dev/null +++ b/tests/issues/test_issue_2466.odin @@ -0,0 +1,22 @@ +// Tests issue #2466 https://github.com/odin-lang/Odin/issues/2466 +package test_issues + +import "core:fmt" +import "core:testing" + +Bug :: struct { + val: int, + arr: []int, +} + +@test +test_compound_literal_local_reuse :: proc(t: ^testing.T) { + v: int = 123 + bug := Bug { + val = v, + arr = {42}, + } + testing.expect(t, bug.val == 123, fmt.tprintf("expected 123, found %d", bug.val)) + testing.expect(t, bug.arr[0] == 42, fmt.tprintf("expected 42, found %d", bug.arr[0])) +} + From 4d5a442d1f7d896e6628b68c0f03b07fa5239475 Mon Sep 17 00:00:00 2001 From: "J.C. Moyer" Date: Sun, 21 May 2023 16:43:34 -0400 Subject: [PATCH 008/144] Use compound literal storage for ValueDecl lvals Prior to this commit, if a variable was initialized using a compound literal, its associated storage would not be set. This commit makes the variable always take on the storage of the compound literal. Fixes #2466 --- src/llvm_backend_stmt.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 125913ac5..0d6f8a136 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -2303,6 +2303,7 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) { lb_add_entity(p->module, e, val); lb_add_debug_local_variable(p, val.value, e->type, e->token); lvals_preused[lval_index] = true; + lvals[lval_index] = *comp_lit_addr; } } } From 635842b322f3f12ed5a68f1dea4e3db45c0a3fee Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 7 Jun 2023 22:40:46 +0100 Subject: [PATCH 009/144] Add more text packages to `examples/all` --- core/text/edit/text_edit.odin | 7 ++++--- examples/all/all_main.odin | 6 ++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/core/text/edit/text_edit.odin b/core/text/edit/text_edit.odin index c49a5d0d1..8520ba674 100644 --- a/core/text/edit/text_edit.odin +++ b/core/text/edit/text_edit.odin @@ -113,15 +113,16 @@ set_text :: proc(s: ^State, text: string) { } -undo_state_push :: proc(s: ^State, undo: ^[dynamic]^Undo_State) { +undo_state_push :: proc(s: ^State, undo: ^[dynamic]^Undo_State) -> mem.Allocator_Error { text := string(s.builder.buf[:]) - item := (^Undo_State)(mem.alloc(size_of(Undo_State) + len(text), align_of(Undo_State), s.undo_text_allocator)) + item := (^Undo_State)(mem.alloc(size_of(Undo_State) + len(text), align_of(Undo_State), s.undo_text_allocator) or_return) item.selection = s.selection item.len = len(text) #no_bounds_check { runtime.copy(item.text[:len(text)], text) } - append(undo, item) + append(undo, item) or_return + return nil } undo :: proc(s: ^State, undo, redo: ^[dynamic]^Undo_State) { diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index 9515d2a00..07688cbc8 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -108,6 +108,9 @@ import sync "core:sync" import testing "core:testing" import scanner "core:text/scanner" import i18n "core:text/i18n" +import match "core:text/match" +import table "core:text/table" +import edit "core:text/edit" import thread "core:thread" import time "core:time" @@ -210,6 +213,9 @@ _ :: sync _ :: testing _ :: scanner _ :: i18n +_ :: match +_ :: table +_ :: edit _ :: thread _ :: time _ :: sysinfo From 9b15bda0552cf61dedb86924bbd48ac4fc8e25cd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 7 Jun 2023 22:48:39 +0100 Subject: [PATCH 010/144] Add missing packages to `examples/all` --- core/debug/pe/section.odin | 3 --- core/text/match/strlib.odin | 5 +++-- core/text/table/table.odin | 2 -- examples/all/all_main.odin | 19 ++++++++++++++++++- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/core/debug/pe/section.odin b/core/debug/pe/section.odin index 809da8bb4..926306dbb 100644 --- a/core/debug/pe/section.odin +++ b/core/debug/pe/section.odin @@ -1,8 +1,5 @@ package debug_pe -import "core:runtime" -import "core:io" - Section_Header32 :: struct { name: [8]u8, virtual_size: u32le, diff --git a/core/text/match/strlib.odin b/core/text/match/strlib.odin index b8c2861fa..654996bc7 100644 --- a/core/text/match/strlib.odin +++ b/core/text/match/strlib.odin @@ -266,6 +266,7 @@ match_balance :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) return INVALID, .Invalid_Pattern_Capture } + schar, ssize := utf8_peek(ms.src[s:]) or_return pchar, psize := utf8_peek(ms.pattern[p:]) or_return @@ -274,9 +275,9 @@ match_balance :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) return INVALID, .OK } - s_begin := s cont := 1 - s := s + ssize + s := s + s += ssize begin := pchar end, _ := utf8_peek(ms.pattern[p + psize:]) or_return diff --git a/core/text/table/table.odin b/core/text/table/table.odin index df93ee44e..2b60df98f 100644 --- a/core/text/table/table.odin +++ b/core/text/table/table.odin @@ -9,12 +9,10 @@ package text_table import "core:io" -import "core:os" import "core:fmt" import "core:mem" import "core:mem/virtual" import "core:runtime" -import "core:strings" Cell :: struct { text: string, diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index 07688cbc8..60850c806 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -19,6 +19,8 @@ import priority_queue "core:container/priority_queue" import queue "core:container/queue" import small_array "core:container/small_array" import lru "core:container/lru" +import list "core:container/intrusive/list" +import topological_sort "core:container/topological_sort" import crypto "core:crypto" import blake "core:crypto/blake" @@ -48,6 +50,8 @@ import crypto_util "core:crypto/util" import whirlpool "core:crypto/whirlpool" import x25519 "core:crypto/x25519" +import pe "core:debug/pe" + import dynlib "core:dynlib" import net "core:net" @@ -58,9 +62,11 @@ import hxa "core:encoding/hxa" import json "core:encoding/json" import varint "core:encoding/varint" import xml "core:encoding/xml" +import endian "core:encoding/endian" import fmt "core:fmt" import hash "core:hash" +import xxhash "core:hash/xxhash" import image "core:image" import netpbm "core:image/netpbm" @@ -80,9 +86,10 @@ import glm "core:math/linalg/glsl" import hlm "core:math/linalg/hlsl" import noise "core:math/noise" import rand "core:math/rand" +import ease "core:math/ease" import mem "core:mem" -// import virtual "core:mem/virtual" +import virtual "core:mem/virtual" import ast "core:odin/ast" import doc_format "core:odin/doc-format" @@ -91,6 +98,8 @@ import odin_parser "core:odin/parser" import odin_printer "core:odin/printer" import odin_tokenizer "core:odin/tokenizer" +import spall "core:prof/spall" + import os "core:os" import slashpath "core:path/slashpath" @@ -137,6 +146,8 @@ _ :: priority_queue _ :: queue _ :: small_array _ :: lru +_ :: list +_ :: topological_sort _ :: crypto _ :: blake _ :: blake2b @@ -164,6 +175,7 @@ _ :: tiger2 _ :: crypto_util _ :: whirlpool _ :: x25519 +_ :: pe _ :: dynlib _ :: net _ :: base32 @@ -173,8 +185,10 @@ _ :: hxa _ :: json _ :: varint _ :: xml +_ :: endian _ :: fmt _ :: hash +_ :: xxhash _ :: image _ :: netpbm _ :: png @@ -191,7 +205,9 @@ _ :: glm _ :: hlm _ :: noise _ :: rand +_ :: ease _ :: mem +_ :: virtual _ :: ast _ :: doc_format _ :: odin_format @@ -199,6 +215,7 @@ _ :: odin_parser _ :: odin_printer _ :: odin_tokenizer _ :: os +_ :: spall _ :: slashpath _ :: filepath _ :: reflect From d82bfa98a7c8f1e5ef3ba1e1251001b4823be4e6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 7 Jun 2023 23:01:08 +0100 Subject: [PATCH 011/144] Remove redundant comments --- vendor/darwin/Metal/MetalClasses.odin | 2189 +------------------------ 1 file changed, 85 insertions(+), 2104 deletions(-) diff --git a/vendor/darwin/Metal/MetalClasses.odin b/vendor/darwin/Metal/MetalClasses.odin index b10959c2b..17f22e1d3 100644 --- a/vendor/darwin/Metal/MetalClasses.odin +++ b/vendor/darwin/Metal/MetalClasses.odin @@ -6,23 +6,7 @@ _ :: mem //////////////////////////////////////////////////////////////////////////////// -/* -Class: - AccelerationStructureBoundingBoxGeometryDescriptor -Class Methods: - alloc - descriptor -Methods: - init - boundingBoxBuffer - boundingBoxBufferOffset - boundingBoxCount - boundingBoxStride - setBoundingBoxBuffer - setBoundingBoxBufferOffset - setBoundingBoxCount - setBoundingBoxStride -*/ + @(objc_class="MTLAccelerationStructureBoundingBoxGeometryDescriptor") AccelerationStructureBoundingBoxGeometryDescriptor :: struct { using _: NS.Copying(AccelerationStructureBoundingBoxGeometryDescriptor), using _: AccelerationStructureDescriptor } @@ -73,19 +57,6 @@ AccelerationStructureBoundingBoxGeometryDescriptor_setBoundingBoxStride :: #forc //////////////////////////////////////////////////////////////////////////////// -/* -Class: - MotionKeyframeData -Class Methods: - alloc - data -Methods: - init - buffer - setBuffer - offset - setOffset -*/ @(objc_class="MTLMotionKeyframeData") MotionKeyframeData :: struct { using _: NS.Object } @@ -121,10 +92,7 @@ MotionKeyframeData_setOffset :: #force_inline proc "c" (self: ^MotionKeyframeDat //////////////////////////////////////////////////////////////////////////////// -/* -Class: - AccelerationStructureMotionTriangleGeometryDescriptor -*/ + @(objc_class="MTLAccelerationStructureMotionTriangleGeometryDescriptor") AccelerationStructureMotionTriangleGeometryDescriptor :: struct { using _: NS.Copying(AccelerationStructureMotionTriangleGeometryDescriptor), using _: AccelerationStructureGeometryDescriptor } @@ -222,10 +190,7 @@ AccelerationStructureMotionTriangleGeometryDescriptor_setTransformationMatrixBuf //////////////////////////////////////////////////////////////////////////////// -/* -Class: - AccelerationStructureMotionBoundingBoxGeometryDescriptor -*/ + @(objc_class="MTLAccelerationStructureMotionBoundingBoxGeometryDescriptor") AccelerationStructureMotionBoundingBoxGeometryDescriptor :: struct { using _: NS.Copying(AccelerationStructureMotionBoundingBoxGeometryDescriptor), using _: AccelerationStructureGeometryDescriptor } @@ -279,16 +244,7 @@ AccelerationStructureMotionBoundingBoxGeometryDescriptor_setBoundingBoxCount :: //////////////////////////////////////////////////////////////////////////////// -/* -Class: - AccelerationStructureDescriptor -Class Methods: - alloc -Methods: - init - setUsage - usage -*/ + @(objc_class="MTLAccelerationStructureDescriptor") AccelerationStructureDescriptor :: struct { using _: NS.Copying(AccelerationStructureDescriptor) } @@ -311,28 +267,7 @@ AccelerationStructureDescriptor_usage :: #force_inline proc "c" (self: ^Accelera //////////////////////////////////////////////////////////////////////////////// -/* -Class: - AccelerationStructureGeometryDescriptor -Class Methods: - alloc -Methods: - init - allowDuplicateIntersectionFunctionInvocation - intersectionFunctionTableOffset - opaque - setAllowDuplicateIntersectionFunctionInvocation - setIntersectionFunctionTableOffset - setOpaque - primitiveDataBuffer - setPrimitiveDataBuffer - primitiveDataBufferOffset - setPrimitiveDataBufferOffset - primitiveDataStride - setPrimitiveDataStride - primitiveDataElementSize - setPrimitiveDataElementSize -*/ + @(objc_class="MTLAccelerationStructureGeometryDescriptor") AccelerationStructureGeometryDescriptor :: struct { using _: NS.Copying(AccelerationStructureGeometryDescriptor) } @@ -405,35 +340,7 @@ AccelerationStructureGeometryDescriptor_setPrimitiveDataElementSize :: #force_in //////////////////////////////////////////////////////////////////////////////// -/* -Class: - AccelerationStructureTriangleGeometryDescriptor -Class Methods: - alloc - descriptor -Methods: - init - indexBuffer - indexBufferOffset - indexType - setIndexBuffer - setIndexBufferOffset - setIndexType - setTriangleCount - setVertexBuffer - setVertexBufferOffset - setVertexStride - triangleCount - vertexBuffer - vertexBufferOffset - vertexStride - vertexFormat - setVertexFormat - transformationMatrixBuffer - setTransformationMatrixBuffer - transformationMatrixBufferOffset - setTransformationMatrixBufferOffset -*/ + @(objc_class="MTLAccelerationStructureTriangleGeometryDescriptor") AccelerationStructureTriangleGeometryDescriptor :: struct { using _: NS.Copying(AccelerationStructureTriangleGeometryDescriptor), using _: AccelerationStructureDescriptor } @@ -534,30 +441,7 @@ AccelerationStructureTriangleGeometryDescriptor_setTransformationMatrixBufferOff //////////////////////////////////////////////////////////////////////////////// -/* -Class: - Argument -Class Methods: - alloc -Methods: - init - access - arrayLength - bufferAlignment - bufferDataSize - bufferDataType - bufferPointerType - bufferStructType - index - isActive - isDepthTexture - name - textureDataType - textureType - threadgroupMemoryAlignment - threadgroupMemoryDataSize - type -*/ + @(objc_class="MTLArgument") Argument :: struct { using _: NS.Object } @@ -636,27 +520,7 @@ Argument_type :: #force_inline proc "c" (self: ^Argument) -> ArgumentType { //////////////////////////////////////////////////////////////////////////////// -/* -Class: - ArgumentDescriptor -Class Methods: - alloc - argumentDescriptor -Methods: - init - access - arrayLength - constantBlockAlignment - dataType - index - setAccess - setArrayLength - setConstantBlockAlignment - setDataType - setIndex - setTextureType - textureType -*/ + @(objc_class="MTLArgumentDescriptor") ArgumentDescriptor :: struct { using _: NS.Copying(ArgumentDescriptor) } @@ -723,22 +587,7 @@ ArgumentDescriptor_textureType :: #force_inline proc "c" (self: ^ArgumentDescrip //////////////////////////////////////////////////////////////////////////////// -/* -Class: - ArrayType -Class Methods: - alloc -Methods: - init - argumentIndexStride - arrayLength - elementArrayType - elementPointerType - elementStructType - elementTextureReferenceType - elementType - stride -*/ + @(objc_class="MTLArrayType") ArrayType :: struct { using _: Type } @@ -785,20 +634,7 @@ ArrayType_stride :: #force_inline proc "c" (self: ^ArrayType) -> NS.UInteger { //////////////////////////////////////////////////////////////////////////////// -/* -Class: - Attribute -Class Methods: - alloc -Methods: - init - attributeIndex - attributeType - isActive - isPatchControlPointData - isPatchData - name -*/ + @(objc_class="MTLAttribute") Attribute :: struct { using _: NS.Object } @@ -837,20 +673,7 @@ Attribute_name :: #force_inline proc "c" (self: ^Attribute) -> ^NS.String { //////////////////////////////////////////////////////////////////////////////// -/* -Class: - AttributeDescriptor -Class Methods: - alloc -Methods: - init - bufferIndex - format - offset - setBufferIndex - setFormat - setOffset -*/ + @(objc_class="MTLAttributeDescriptor") AttributeDescriptor :: struct { using _: NS.Copying(AttributeDescriptor) } @@ -889,16 +712,7 @@ AttributeDescriptor_setOffset :: #force_inline proc "c" (self: ^AttributeDescrip //////////////////////////////////////////////////////////////////////////////// -/* -Class: - AttributeDescriptorArray -Class Methods: - alloc -Methods: - init - objectAtIndexedSubscript - setObject -*/ + @(objc_class="MTLAttributeDescriptorArray") AttributeDescriptorArray :: struct { using _: NS.Object } @@ -921,16 +735,7 @@ AttributeDescriptorArray_setObject :: #force_inline proc "c" (self: ^AttributeDe //////////////////////////////////////////////////////////////////////////////// -/* -Class: - BinaryArchiveDescriptor -Class Methods: - alloc -Methods: - init - setUrl - url -*/ + @(objc_class="MTLBinaryArchiveDescriptor") BinaryArchiveDescriptor :: struct { using _: NS.Copying(BinaryArchiveDescriptor) } @@ -953,16 +758,7 @@ BinaryArchiveDescriptor_url :: #force_inline proc "c" (self: ^BinaryArchiveDescr //////////////////////////////////////////////////////////////////////////////// -/* -Class: - BlitPassDescriptor -Class Methods: - alloc - blitPassDescriptor -Methods: - init - sampleBufferAttachments -*/ + @(objc_class="MTLBlitPassDescriptor") BlitPassDescriptor :: struct { using _: NS.Copying(BlitPassDescriptor) } @@ -985,20 +781,7 @@ BlitPassDescriptor_sampleBufferAttachments :: #force_inline proc "c" (self: ^Bli //////////////////////////////////////////////////////////////////////////////// -/* -Class: - BlitPassSampleBufferAttachmentDescriptor -Class Methods: - alloc -Methods: - init - endOfEncoderSampleIndex - sampleBuffer - setEndOfEncoderSampleIndex - setSampleBuffer - setStartOfEncoderSampleIndex - startOfEncoderSampleIndex -*/ + @(objc_class="MTLBlitPassSampleBufferAttachmentDescriptor") BlitPassSampleBufferAttachmentDescriptor :: struct { using _: NS.Copying(BlitPassSampleBufferAttachmentDescriptor) } @@ -1037,16 +820,7 @@ BlitPassSampleBufferAttachmentDescriptor_startOfEncoderSampleIndex :: #force_inl //////////////////////////////////////////////////////////////////////////////// -/* -Class: - BlitPassSampleBufferAttachmentDescriptorArray -Class Methods: - alloc -Methods: - init - objectAtIndexedSubscript - setObject -*/ + @(objc_class="MTLBlitPassSampleBufferAttachmentDescriptorArray") BlitPassSampleBufferAttachmentDescriptorArray :: struct { using _: NS.Object } @@ -1069,20 +843,7 @@ BlitPassSampleBufferAttachmentDescriptorArray_setObject :: #force_inline proc "c //////////////////////////////////////////////////////////////////////////////// -/* -Class: - BufferLayoutDescriptor -Class Methods: - alloc -Methods: - init - setStepFunction - setStepRate - setStride - stepFunction - stepRate - stride -*/ + @(objc_class="MTLBufferLayoutDescriptor") BufferLayoutDescriptor :: struct { using _: NS.Copying(BufferLayoutDescriptor) } @@ -1121,16 +882,7 @@ BufferLayoutDescriptor_stride :: #force_inline proc "c" (self: ^BufferLayoutDesc //////////////////////////////////////////////////////////////////////////////// -/* -Class: - BufferLayoutDescriptorArray -Class Methods: - alloc -Methods: - init - objectAtIndexedSubscript - setObject -*/ + @(objc_class="MTLBufferLayoutDescriptorArray") BufferLayoutDescriptorArray :: struct { using _: NS.Object } @@ -1153,20 +905,7 @@ BufferLayoutDescriptorArray_setObject :: #force_inline proc "c" (self: ^BufferLa //////////////////////////////////////////////////////////////////////////////// -/* -Class: - CaptureDescriptor -Class Methods: - alloc -Methods: - init - captureObject - destination - outputURL - setCaptureObject - setDestination - setOutputURL -*/ + @(objc_class="MTLCaptureDescriptor") CaptureDescriptor :: struct { using _: NS.Copying(CaptureDescriptor) } @@ -1205,26 +944,7 @@ CaptureDescriptor_setOutputURL :: #force_inline proc "c" (self: ^CaptureDescript //////////////////////////////////////////////////////////////////////////////// -/* -Class: - CaptureManager -Class Methods: - alloc - sharedCaptureManager -Methods: - defaultCaptureScope - init - isCapturing - newCaptureScopeWithCommandQueue - newCaptureScopeWithDevice - setDefaultCaptureScope - startCaptureWithCommandQueue - startCaptureWithDescriptor - startCaptureWithDevice - startCaptureWithScope - stopCapture - supportsDestination -*/ + @(objc_class="MTLCaptureManager") CaptureManager :: struct { using _: NS.Object } @@ -1294,18 +1014,7 @@ CaptureManager_supportsDestination :: #force_inline proc "c" (self: ^CaptureMana //////////////////////////////////////////////////////////////////////////////// -/* -Class: - CommandBufferDescriptor -Class Methods: - alloc -Methods: - init - errorOptions - retainedReferences - setErrorOptions - setRetainedReferences -*/ + @(objc_class="MTLCommandBufferDescriptor") CommandBufferDescriptor :: struct { using _: NS.Copying(CommandBufferDescriptor) } @@ -1336,28 +1045,7 @@ CommandBufferDescriptor_setRetainedReferences :: #force_inline proc "c" (self: ^ //////////////////////////////////////////////////////////////////////////////// -/* -Class: - CompileOptions -Class Methods: - alloc -Methods: - init - fastMathEnabled - installName - languageVersion - libraries - libraryType - preprocessorMacros - preserveInvariance - setFastMathEnabled - setInstallName - setLanguageVersion - setLibraries - setLibraryType - setPreprocessorMacros - setPreserveInvariance -*/ + @(objc_class="MTLCompileOptions") CompileOptions :: struct { using _: NS.Copying(CompileOptions) } @@ -1437,18 +1125,7 @@ CompileOptions_setOptimizationLevel :: #force_inline proc "c" (self: ^CompileOpt //////////////////////////////////////////////////////////////////////////////// -/* -Class: - ComputePassDescriptor -Class Methods: - alloc - computePassDescriptor -Methods: - init - dispatchType - sampleBufferAttachments - setDispatchType -*/ + @(objc_class="MTLComputePassDescriptor") ComputePassDescriptor :: struct { using _: NS.Copying(ComputePassDescriptor) } @@ -1479,20 +1156,7 @@ ComputePassDescriptor_setDispatchType :: #force_inline proc "c" (self: ^ComputeP //////////////////////////////////////////////////////////////////////////////// -/* -Class: - ComputePassSampleBufferAttachmentDescriptor -Class Methods: - alloc -Methods: - init - endOfEncoderSampleIndex - sampleBuffer - setEndOfEncoderSampleIndex - setSampleBuffer - setStartOfEncoderSampleIndex - startOfEncoderSampleIndex -*/ + @(objc_class="MTLComputePassSampleBufferAttachmentDescriptor") ComputePassSampleBufferAttachmentDescriptor :: struct { using _: NS.Copying(ComputePassSampleBufferAttachmentDescriptor) } @@ -1531,16 +1195,7 @@ ComputePassSampleBufferAttachmentDescriptor_startOfEncoderSampleIndex :: #force_ //////////////////////////////////////////////////////////////////////////////// -/* -Class: - ComputePassSampleBufferAttachmentDescriptorArray -Class Methods: - alloc -Methods: - init - objectAtIndexedSubscript - setObject -*/ + @(objc_class="MTLComputePassSampleBufferAttachmentDescriptorArray") ComputePassSampleBufferAttachmentDescriptorArray :: struct { using _: NS.Object } @@ -1563,38 +1218,7 @@ ComputePassSampleBufferAttachmentDescriptorArray_setObject :: #force_inline proc //////////////////////////////////////////////////////////////////////////////// -/* -Class: - ComputePipelineDescriptor -Class Methods: - alloc -Methods: - init - binaryArchives - buffers - computeFunction - insertLibraries - label - linkedFunctions - maxCallStackDepth - maxTotalThreadsPerThreadgroup - reset - setBinaryArchives - setComputeFunction - setInsertLibraries - setLabel - setLinkedFunctions - setMaxCallStackDepth - setMaxTotalThreadsPerThreadgroup - setStageInputDescriptor - setSupportAddingBinaryFunctions - setSupportIndirectCommandBuffers - setThreadGroupSizeIsMultipleOfThreadExecutionWidth - stageInputDescriptor - supportAddingBinaryFunctions - supportIndirectCommandBuffers - threadGroupSizeIsMultipleOfThreadExecutionWidth -*/ + @(objc_class="MTLComputePipelineDescriptor") ComputePipelineDescriptor :: struct { using _: NS.Copying(ComputePipelineDescriptor) } @@ -1710,15 +1334,7 @@ ComputePipelineDescriptor_gpuResourceID :: #force_inline proc "c" (self: ^Comput //////////////////////////////////////////////////////////////////////////////// -/* -Class: - ComputePipelineReflection -Class Methods: - alloc -Methods: - init - arguments -*/ + @(objc_class="MTLComputePipelineReflection") ComputePipelineReflection :: struct { using _: NS.Object } @@ -1741,22 +1357,7 @@ ComputePipelineReflection_arguments :: #force_inline proc "c" (self: ^ComputePip //////////////////////////////////////////////////////////////////////////////// -/* -Class: - CounterSampleBufferDescriptor -Class Methods: - alloc -Methods: - init - counterSet - label - sampleCount - setCounterSet - setLabel - setSampleCount - setStorageMode - storageMode -*/ + @(objc_class="MTLCounterSampleBufferDescriptor") CounterSampleBufferDescriptor :: struct { using _: NS.Copying(CounterSampleBufferDescriptor) } @@ -1803,24 +1404,7 @@ CounterSampleBufferDescriptor_storageMode :: #force_inline proc "c" (self: ^Coun //////////////////////////////////////////////////////////////////////////////// -/* -Class: - DepthStencilDescriptor -Class Methods: - alloc -Methods: - init - backFaceStencil - depthCompareFunction - frontFaceStencil - isDepthWriteEnabled - label - setBackFaceStencil - setDepthCompareFunction - setDepthWriteEnabled - setFrontFaceStencil - setLabel -*/ + @(objc_class="MTLDepthStencilDescriptor") DepthStencilDescriptor :: struct { using _: NS.Copying(DepthStencilDescriptor) } @@ -1875,18 +1459,7 @@ DepthStencilDescriptor_setLabel :: #force_inline proc "c" (self: ^DepthStencilDe //////////////////////////////////////////////////////////////////////////////// -/* -Class: - FunctionConstant -Class Methods: - alloc -Methods: - init - index - name - required - type -*/ + @(objc_class="MTLFunctionConstant") FunctionConstant :: struct { using _: NS.Copying(FunctionConstant) } @@ -1917,18 +1490,7 @@ FunctionConstant_type :: #force_inline proc "c" (self: ^FunctionConstant) -> Dat //////////////////////////////////////////////////////////////////////////////// -/* -Class: - FunctionConstantValues -Class Methods: - alloc -Methods: - init - reset - setConstantValue - setConstantValue - setConstantValues -*/ + @(objc_class="MTLFunctionConstantValues") FunctionConstantValues :: struct { using _: NS.Copying(FunctionConstantValues) } @@ -1959,23 +1521,7 @@ FunctionConstantValues_setConstantValues :: #force_inline proc "c" (self: ^Funct //////////////////////////////////////////////////////////////////////////////// -/* -Class: - FunctionDescriptor -Class Methods: - alloc - functionDescriptor -Methods: - init - constantValues - name - options - setConstantValues - setName - setOptions - setSpecializedName - specializedName -*/ + @(objc_class="MTLFunctionDescriptor") FunctionDescriptor :: struct { using _: NS.Copying(FunctionDescriptor) } @@ -2026,14 +1572,7 @@ FunctionDescriptor_specializedName :: #force_inline proc "c" (self: ^FunctionDes //////////////////////////////////////////////////////////////////////////////// -/* -Class: - IntersectionFunctionDescriptor -Class Methods: - alloc -Methods: - init -*/ + @(objc_class="MTLIntersectionFunctionDescriptor") IntersectionFunctionDescriptor :: struct { using _: NS.Copying(IntersectionFunctionDescriptor) } @@ -2048,26 +1587,7 @@ IntersectionFunctionDescriptor_init :: #force_inline proc "c" (self: ^Intersecti //////////////////////////////////////////////////////////////////////////////// -/* -Class: - HeapDescriptor -Class Methods: - alloc -Methods: - init - cpuCacheMode - hazardTrackingMode - resourceOptions - setCpuCacheMode - setHazardTrackingMode - setResourceOptions - setSize - setStorageMode - setType - size - storageMode - type -*/ + @(objc_class="MTLHeapDescriptor") HeapDescriptor :: struct { using _: NS.Copying(HeapDescriptor) } @@ -2140,26 +1660,7 @@ HeapDescriptor_type :: #force_inline proc "c" (self: ^HeapDescriptor) -> HeapTyp //////////////////////////////////////////////////////////////////////////////// -/* -Class: - IndirectCommandBufferDescriptor -Class Methods: - alloc -Methods: - init - commandTypes - inheritBuffers - inheritPipelineState - maxFragmentBufferBindCount - maxKernelBufferBindCount - maxVertexBufferBindCount - setCommandTypes - setInheritBuffers - setInheritPipelineState - setMaxFragmentBufferBindCount - setMaxKernelBufferBindCount - setMaxVertexBufferBindCount -*/ + @(objc_class="MTLIndirectCommandBufferDescriptor") IndirectCommandBufferDescriptor :: struct { using _: NS.Copying(IndirectCommandBufferDescriptor) } @@ -2222,25 +1723,7 @@ IndirectCommandBufferDescriptor_setMaxVertexBufferBindCount :: #force_inline pro //////////////////////////////////////////////////////////////////////////////// -/* -Class: - InstanceAccelerationStructureDescriptor -Class Methods: - alloc - descriptor -Methods: - init - instanceCount - instanceDescriptorBuffer - instanceDescriptorBufferOffset - instanceDescriptorStride - instancedAccelerationStructures - setInstanceCount - setInstanceDescriptorBuffer - setInstanceDescriptorBufferOffset - setInstanceDescriptorStride - setInstancedAccelerationStructures -*/ + @(objc_class="MTLInstanceAccelerationStructureDescriptor") InstanceAccelerationStructureDescriptor :: struct { using _: NS.Copying(InstanceAccelerationStructureDescriptor), using _: AccelerationStructureDescriptor } @@ -2339,17 +1822,7 @@ InstanceAccelerationStructureDescriptor_setMotionTransformCount :: #force_inline //////////////////////////////////////////////////////////////////////////////// -/* -Class: - IntersectionFunctionTableDescriptor -Class Methods: - alloc - intersectionFunctionTableDescriptor -Methods: - init - functionCount - setFunctionCount -*/ + @(objc_class="MTLIntersectionFunctionTableDescriptor") IntersectionFunctionTableDescriptor :: struct { using _: NS.Copying(IntersectionFunctionTableDescriptor) } @@ -2376,21 +1849,7 @@ IntersectionFunctionTableDescriptor_setFunctionCount :: #force_inline proc "c" ( //////////////////////////////////////////////////////////////////////////////// -/* -Class: - LinkedFunctions -Class Methods: - alloc - linkedFunctions -Methods: - init - binaryFunctions - functions - groups - setBinaryFunctions - setFunctions - setGroups -*/ + @(objc_class="MTLLinkedFunctions") LinkedFunctions :: struct { using _: NS.Copying(LinkedFunctions) } @@ -2433,16 +1892,7 @@ LinkedFunctions_setGroups :: #force_inline proc "c" (self: ^LinkedFunctions, gro //////////////////////////////////////////////////////////////////////////////// -/* -Class: - PipelineBufferDescriptor -Class Methods: - alloc -Methods: - init - mutability - setMutability -*/ + @(objc_class="MTLPipelineBufferDescriptor") PipelineBufferDescriptor :: struct { using _: NS.Copying(PipelineBufferDescriptor) } @@ -2465,16 +1915,7 @@ PipelineBufferDescriptor_setMutability :: #force_inline proc "c" (self: ^Pipelin //////////////////////////////////////////////////////////////////////////////// -/* -Class: - PipelineBufferDescriptorArray -Class Methods: - alloc -Methods: - init - objectAtIndexedSubscript - setObject -*/ + @(objc_class="MTLPipelineBufferDescriptorArray") PipelineBufferDescriptorArray :: struct { using _: NS.Object } @@ -2497,21 +1938,7 @@ PipelineBufferDescriptorArray_setObject :: #force_inline proc "c" (self: ^Pipeli //////////////////////////////////////////////////////////////////////////////// -/* -Class: - PointerType -Class Methods: - alloc -Methods: - init - access - alignment - dataSize - elementArrayType - elementIsArgumentBuffer - elementStructType - elementType -*/ + @(objc_class="MTLPointerType") PointerType :: struct { using _: Type } @@ -2554,17 +1981,7 @@ PointerType_elementType :: #force_inline proc "c" (self: ^PointerType) -> DataTy //////////////////////////////////////////////////////////////////////////////// -/* -Class: - PrimitiveAccelerationStructureDescriptor -Class Methods: - alloc - descriptor -Methods: - init - geometryDescriptors - setGeometryDescriptors -*/ + @(objc_class="MTLPrimitiveAccelerationStructureDescriptor") PrimitiveAccelerationStructureDescriptor :: struct { using _: NS.Copying(PrimitiveAccelerationStructureDescriptor), using _: AccelerationStructureDescriptor } @@ -2639,16 +2056,7 @@ PrimitiveAccelerationStructureDescriptor_setMotionKeyframeCount :: #force_inline //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RasterizationRateLayerArray -Class Methods: - alloc -Methods: - init - objectAtIndexedSubscript - setObject -*/ + @(objc_class="MTLRasterizationRateLayerArray") RasterizationRateLayerArray :: struct { using _: NS.Object } @@ -2671,21 +2079,7 @@ RasterizationRateLayerArray_setObject :: #force_inline proc "c" (self: ^Rasteriz //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RasterizationRateLayerDescriptor -Class Methods: - alloc -Methods: - horizontal - horizontalSampleStorage - init - initWithSampleCount - initWithSampleCount - sampleCount - vertical - verticalSampleStorage -*/ + @(objc_class="MTLRasterizationRateLayerDescriptor") RasterizationRateLayerDescriptor :: struct { using _: NS.Copying(RasterizationRateLayerDescriptor) } @@ -2728,25 +2122,7 @@ RasterizationRateLayerDescriptor_verticalSampleStorage :: #force_inline proc "c" //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RasterizationRateMapDescriptor -Class Methods: - alloc - rasterizationRateMapDescriptorWithScreenSize - rasterizationRateMapDescriptorWithScreenSize - rasterizationRateMapDescriptorWithScreenSize -Methods: - init - label - layerAtIndex - layerCount - layers - screenSize - setLabel - setLayer - setScreenSize -*/ + @(objc_class="MTLRasterizationRateMapDescriptor") RasterizationRateMapDescriptor :: struct { using _: NS.Copying(RasterizationRateMapDescriptor) } @@ -2805,16 +2181,7 @@ RasterizationRateMapDescriptor_setScreenSize :: #force_inline proc "c" (self: ^R //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RasterizationRateSampleArray -Class Methods: - alloc -Methods: - init - objectAtIndexedSubscript - setObject -*/ + @(objc_class="MTLRasterizationRateSampleArray") RasterizationRateSampleArray :: struct { using _: NS.Object } @@ -2837,36 +2204,7 @@ RasterizationRateSampleArray_setObject :: #force_inline proc "c" (self: ^Rasteri //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RenderPassAttachmentDescriptor -Class Methods: - alloc -Methods: - init - depthPlane - level - loadAction - resolveDepthPlane - resolveLevel - resolveSlice - resolveTexture - setDepthPlane - setLevel - setLoadAction - setResolveDepthPlane - setResolveLevel - setResolveSlice - setResolveTexture - setSlice - setStoreAction - setStoreActionOptions - setTexture - slice - storeAction - storeActionOptions - texture -*/ + @(objc_class="MTLRenderPassAttachmentDescriptor") RenderPassAttachmentDescriptor :: struct { using _: NS.Copying(RenderPassAttachmentDescriptor) } @@ -2969,16 +2307,7 @@ RenderPassAttachmentDescriptor_texture :: #force_inline proc "c" (self: ^RenderP //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RenderPassColorAttachmentDescriptor -Class Methods: - alloc -Methods: - init - clearColor - setClearColor -*/ + @(objc_class="MTLRenderPassColorAttachmentDescriptor") RenderPassColorAttachmentDescriptor :: struct { using _: NS.Copying(RenderPassColorAttachmentDescriptor), using _: RenderPassAttachmentDescriptor } @@ -3001,16 +2330,7 @@ RenderPassColorAttachmentDescriptor_setClearColor :: #force_inline proc "c" (sel //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RenderPassColorAttachmentDescriptorArray -Class Methods: - alloc -Methods: - init - objectAtIndexedSubscript - setObject -*/ + @(objc_class="MTLRenderPassColorAttachmentDescriptorArray") RenderPassColorAttachmentDescriptorArray :: struct { using _: NS.Object } @@ -3033,18 +2353,7 @@ RenderPassColorAttachmentDescriptorArray_setObject :: #force_inline proc "c" (se //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RenderPassDepthAttachmentDescriptor -Class Methods: - alloc -Methods: - init - clearDepth - depthResolveFilter - setClearDepth - setDepthResolveFilter -*/ + @(objc_class="MTLRenderPassDepthAttachmentDescriptor") RenderPassDepthAttachmentDescriptor :: struct { using _: NS.Copying(RenderPassDepthAttachmentDescriptor), using _: RenderPassAttachmentDescriptor } @@ -3075,43 +2384,7 @@ RenderPassDepthAttachmentDescriptor_setDepthResolveFilter :: #force_inline proc //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RenderPassDescriptor -Class Methods: - alloc - renderPassDescriptor -Methods: - init - colorAttachments - defaultRasterSampleCount - depthAttachment - getSamplePositions - imageblockSampleLength - rasterizationRateMap - renderTargetArrayLength - renderTargetHeight - renderTargetWidth - sampleBufferAttachments - setDefaultRasterSampleCount - setDepthAttachment - setImageblockSampleLength - setRasterizationRateMap - setRenderTargetArrayLength - setRenderTargetHeight - setRenderTargetWidth - setSamplePositions - setStencilAttachment - setThreadgroupMemoryLength - setTileHeight - setTileWidth - setVisibilityResultBuffer - stencilAttachment - threadgroupMemoryLength - tileHeight - tileWidth - visibilityResultBuffer -*/ + @(objc_class="MTLRenderPassDescriptor") RenderPassDescriptor :: struct { using _: NS.Copying(RenderPassDescriptor), using _: AccelerationStructureDescriptor } @@ -3242,24 +2515,8 @@ RenderPassDescriptor_visibilityResultBuffer :: #force_inline proc "c" (self: ^Re //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RenderPassSampleBufferAttachmentDescriptor -Class Methods: - alloc -Methods: - init - endOfFragmentSampleIndex - endOfVertexSampleIndex - sampleBuffer - setEndOfFragmentSampleIndex - setEndOfVertexSampleIndex - setSampleBuffer - setStartOfFragmentSampleIndex - setStartOfVertexSampleIndex - startOfFragmentSampleIndex - startOfVertexSampleIndex -*/ + + @(objc_class="MTLRenderPassSampleBufferAttachmentDescriptor") RenderPassSampleBufferAttachmentDescriptor :: struct { using _: NS.Copying(RenderPassSampleBufferAttachmentDescriptor) } @@ -3314,16 +2571,7 @@ RenderPassSampleBufferAttachmentDescriptor_startOfVertexSampleIndex :: #force_in //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RenderPassSampleBufferAttachmentDescriptorArray -Class Methods: - alloc -Methods: - init - objectAtIndexedSubscript - setObject -*/ + @(objc_class="MTLRenderPassSampleBufferAttachmentDescriptorArray") RenderPassSampleBufferAttachmentDescriptorArray :: struct { using _: NS.Object } @@ -3346,18 +2594,7 @@ RenderPassSampleBufferAttachmentDescriptorArray_setObject :: #force_inline proc //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RenderPassStencilAttachmentDescriptor -Class Methods: - alloc -Methods: - init - clearStencil - setClearStencil - setStencilResolveFilter - stencilResolveFilter -*/ + @(objc_class="MTLRenderPassStencilAttachmentDescriptor") RenderPassStencilAttachmentDescriptor :: struct { using _: NS.Copying(RenderPassStencilAttachmentDescriptor) } @@ -3388,32 +2625,7 @@ RenderPassStencilAttachmentDescriptor_stencilResolveFilter :: #force_inline proc //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RenderPipelineColorAttachmentDescriptor -Class Methods: - alloc -Methods: - init - alphaBlendOperation - destinationAlphaBlendFactor - destinationRGBBlendFactor - isBlendingEnabled - pixelFormat - rgbBlendOperation - setAlphaBlendOperation - setBlendingEnabled - setDestinationAlphaBlendFactor - setDestinationRGBBlendFactor - setPixelFormat - setRgbBlendOperation - setSourceAlphaBlendFactor - setSourceRGBBlendFactor - setWriteMask - sourceAlphaBlendFactor - sourceRGBBlendFactor - writeMask -*/ + @(objc_class="MTLRenderPipelineColorAttachmentDescriptor") RenderPipelineColorAttachmentDescriptor :: struct { using _: NS.Copying(RenderPipelineColorAttachmentDescriptor), using _: RenderPassAttachmentDescriptor } @@ -3500,16 +2712,7 @@ RenderPipelineColorAttachmentDescriptor_writeMask :: #force_inline proc "c" (sel //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RenderPipelineColorAttachmentDescriptorArray -Class Methods: - alloc -Methods: - init - objectAtIndexedSubscript - setObject -*/ + @(objc_class="MTLRenderPipelineColorAttachmentDescriptorArray") RenderPipelineColorAttachmentDescriptorArray :: struct { using _: NS.Object } @@ -3532,62 +2735,7 @@ RenderPipelineColorAttachmentDescriptorArray_setObject :: #force_inline proc "c" //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RenderPipelineDescriptor -Class Methods: - alloc -Methods: - init - binaryArchives - colorAttachments - depthAttachmentPixelFormat - fragmentBuffers - fragmentFunction - inputPrimitiveTopology - isAlphaToCoverageEnabled - isAlphaToOneEnabled - isRasterizationEnabled - isTessellationFactorScaleEnabled - label - maxTessellationFactor - maxVertexAmplificationCount - rasterSampleCount - reset - sampleCount - setAlphaToCoverageEnabled - setAlphaToOneEnabled - setBinaryArchives - setDepthAttachmentPixelFormat - setFragmentFunction - setInputPrimitiveTopology - setLabel - setMaxTessellationFactor - setMaxVertexAmplificationCount - setRasterSampleCount - setRasterizationEnabled - setSampleCount - setStencilAttachmentPixelFormat - setSupportIndirectCommandBuffers - setTessellationControlPointIndexType - setTessellationFactorFormat - setTessellationFactorScaleEnabled - setTessellationFactorStepFunction - setTessellationOutputWindingOrder - setTessellationPartitionMode - setVertexDescriptor - setVertexFunction - stencilAttachmentPixelFormat - supportIndirectCommandBuffers - tessellationControlPointIndexType - tessellationFactorFormat - tessellationFactorStepFunction - tessellationOutputWindingOrder - tessellationPartitionMode - vertexBuffers - vertexDescriptor - vertexFunction -*/ + @(objc_class="MTLRenderPipelineDescriptor") RenderPipelineDescriptor :: struct { using _: NS.Copying(RenderPipelineDescriptor) } @@ -3889,17 +3037,7 @@ RenderPipelineDescriptor_rasterizationEnabled :: #force_inline proc "c" (self: ^ //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RenderPipelineReflection -Class Methods: - alloc -Methods: - init - fragmentArguments - tileArguments - vertexArguments -*/ + @(objc_class="MTLRenderPipelineReflection") RenderPipelineReflection :: struct { using _: NS.Object } @@ -3947,16 +3085,7 @@ RenderPipelineReflection_meshBindings :: #force_inline proc "c" (self: ^RenderPi //////////////////////////////////////////////////////////////////////////////// -/* -Class: - ResourceStatePassDescriptor -Class Methods: - alloc - resourceStatePassDescriptor -Methods: - init - sampleBufferAttachments -*/ + @(objc_class="MTLResourceStatePassDescriptor") ResourceStatePassDescriptor :: struct { using _: NS.Copying(ResourceStatePassDescriptor) } @@ -3979,20 +3108,7 @@ ResourceStatePassDescriptor_sampleBufferAttachments :: #force_inline proc "c" (s //////////////////////////////////////////////////////////////////////////////// -/* -Class: - ResourceStatePassSampleBufferAttachmentDescriptor -Class Methods: - alloc -Methods: - init - endOfEncoderSampleIndex - sampleBuffer - setEndOfEncoderSampleIndex - setSampleBuffer - setStartOfEncoderSampleIndex - startOfEncoderSampleIndex -*/ + @(objc_class="MTLResourceStatePassSampleBufferAttachmentDescriptor") ResourceStatePassSampleBufferAttachmentDescriptor :: struct { using _: NS.Copying(ResourceStatePassSampleBufferAttachmentDescriptor) } @@ -4031,16 +3147,7 @@ ResourceStatePassSampleBufferAttachmentDescriptor_startOfEncoderSampleIndex :: # //////////////////////////////////////////////////////////////////////////////// -/* -Class: - ResourceStatePassSampleBufferAttachmentDescriptorArray -Class Methods: - alloc -Methods: - init - objectAtIndexedSubscript - setObject -*/ + @(objc_class="MTLResourceStatePassSampleBufferAttachmentDescriptorArray") ResourceStatePassSampleBufferAttachmentDescriptorArray :: struct { using _: NS.Object } @@ -4063,44 +3170,7 @@ ResourceStatePassSampleBufferAttachmentDescriptorArray_setObject :: #force_inlin //////////////////////////////////////////////////////////////////////////////// -/* -Class: - SamplerDescriptor -Class Methods: - alloc -Methods: - init - borderColor - compareFunction - label - lodAverage - lodMaxClamp - lodMinClamp - magFilter - maxAnisotropy - minFilter - mipFilter - normalizedCoordinates - rAddressMode - sAddressMode - setBorderColor - setCompareFunction - setLabel - setLodAverage - setLodMaxClamp - setLodMinClamp - setMagFilter - setMaxAnisotropy - setMinFilter - setMipFilter - setNormalizedCoordinates - setRAddressMode - setSAddressMode - setSupportArgumentBuffers - setTAddressMode - supportArgumentBuffers - tAddressMode -*/ + @(objc_class="MTLSamplerDescriptor") SamplerDescriptor :: struct { using _: NS.Copying(SamplerDescriptor) } @@ -4235,15 +3305,7 @@ SamplerDescriptor_tAddressMode :: #force_inline proc "c" (self: ^SamplerDescript //////////////////////////////////////////////////////////////////////////////// -/* -Class: - SharedEventHandle -Class Methods: - alloc -Methods: - init - label -*/ + @(objc_class="MTLSharedEventHandle") SharedEventHandle :: struct { using _: NS.Object } @@ -4262,16 +3324,7 @@ SharedEventHandle_label :: #force_inline proc "c" (self: ^SharedEventHandle) -> //////////////////////////////////////////////////////////////////////////////// -/* -Class: - SharedEventListener -Class Methods: - alloc -Methods: - dispatchQueue - init - initWithDispatchQueue -*/ + @(objc_class="MTLSharedEventListener") SharedEventListener :: struct { using _: NS.Object } @@ -4294,16 +3347,7 @@ SharedEventListener_initWithDispatchQueue :: #force_inline proc "c" (self: ^Shar //////////////////////////////////////////////////////////////////////////////// -/* -Class: - SharedTextureHandle -Class Methods: - alloc -Methods: - init - device - label -*/ + @(objc_class="MTLSharedTextureHandle") SharedTextureHandle :: struct { using _: NS.Object } @@ -4326,22 +3370,7 @@ SharedTextureHandle_label :: #force_inline proc "c" (self: ^SharedTextureHandle) //////////////////////////////////////////////////////////////////////////////// -/* -Class: - StageInputOutputDescriptor -Class Methods: - alloc - stageInputOutputDescriptor -Methods: - init - attributes - indexBufferIndex - indexType - layouts - reset - setIndexBufferIndex - setIndexType -*/ + @(objc_class="MTLStageInputOutputDescriptor") StageInputOutputDescriptor :: struct { using _: NS.Copying(StageInputOutputDescriptor) } @@ -4388,26 +3417,7 @@ StageInputOutputDescriptor_stageInputOutputDescriptor :: #force_inline proc "c" //////////////////////////////////////////////////////////////////////////////// -/* -Class: - StencilDescriptor -Class Methods: - alloc -Methods: - init - depthFailureOperation - depthStencilPassOperation - readMask - setDepthFailureOperation - setDepthStencilPassOperation - setReadMask - setStencilCompareFunction - setStencilFailureOperation - setWriteMask - stencilCompareFunction - stencilFailureOperation - writeMask -*/ + @(objc_class="MTLStencilDescriptor") StencilDescriptor :: struct { using _: NS.Copying(StencilDescriptor) } @@ -4470,22 +3480,7 @@ StencilDescriptor_writeMask :: #force_inline proc "c" (self: ^StencilDescriptor) //////////////////////////////////////////////////////////////////////////////// -/* -Class: - StructMember -Class Methods: - alloc -Methods: - init - argumentIndex - arrayType - dataType - name - offset - pointerType - structType - textureReferenceType -*/ + @(objc_class="MTLStructMember") StructMember :: struct { using _: NS.Object } @@ -4532,16 +3527,7 @@ StructMember_textureReferenceType :: #force_inline proc "c" (self: ^StructMember //////////////////////////////////////////////////////////////////////////////// -/* -Class: - StructType -Class Methods: - alloc -Methods: - init - memberByName - members -*/ + @(objc_class="MTLStructType") StructType :: struct { using _: Type } @@ -4564,47 +3550,7 @@ StructType_members :: #force_inline proc "c" (self: ^StructType) -> ^NS.Array { //////////////////////////////////////////////////////////////////////////////// -/* -Class: - TextureDescriptor -Class Methods: - alloc - texture2DDescriptorWithPixelFormat - textureBufferDescriptorWithPixelFormat - textureCubeDescriptorWithPixelFormat -Methods: - init - allowGPUOptimizedContents - arrayLength - cpuCacheMode - depth - hazardTrackingMode - height - mipmapLevelCount - pixelFormat - resourceOptions - sampleCount - setAllowGPUOptimizedContents - setArrayLength - setCpuCacheMode - setDepth - setHazardTrackingMode - setHeight - setMipmapLevelCount - setPixelFormat - setResourceOptions - setSampleCount - setStorageMode - setSwizzle - setTextureType - setUsage - setWidth - storageMode - swizzle - textureType - usage - width -*/ + @(objc_class="MTLTextureDescriptor") TextureDescriptor :: struct { using _: NS.Copying(TextureDescriptor) } @@ -4760,18 +3706,7 @@ TextureDescriptor_setCompressionType :: #force_inline proc "c" (self: ^TextureDe //////////////////////////////////////////////////////////////////////////////// -/* -Class: - TextureReferenceType -Class Methods: - alloc -Methods: - init - access - isDepthTexture - textureDataType - textureType -*/ + @(objc_class="MTLTextureReferenceType") TextureReferenceType :: struct { using _: Type } @@ -4802,16 +3737,7 @@ TextureReferenceType_textureType :: #force_inline proc "c" (self: ^TextureRefere //////////////////////////////////////////////////////////////////////////////// -/* -Class: - TileRenderPipelineColorAttachmentDescriptor -Class Methods: - alloc -Methods: - init - pixelFormat - setPixelFormat -*/ + @(objc_class="MTLTileRenderPipelineColorAttachmentDescriptor") TileRenderPipelineColorAttachmentDescriptor :: struct { using _: NS.Copying(TileRenderPipelineColorAttachmentDescriptor) } @@ -4834,16 +3760,7 @@ TileRenderPipelineColorAttachmentDescriptor_setPixelFormat :: #force_inline proc //////////////////////////////////////////////////////////////////////////////// -/* -Class: - TileRenderPipelineColorAttachmentDescriptorArray -Class Methods: - alloc -Methods: - init - objectAtIndexedSubscript - setObject -*/ + @(objc_class="MTLTileRenderPipelineColorAttachmentDescriptorArray") TileRenderPipelineColorAttachmentDescriptorArray :: struct { using _: NS.Object } @@ -4866,29 +3783,7 @@ TileRenderPipelineColorAttachmentDescriptorArray_setObject :: #force_inline proc //////////////////////////////////////////////////////////////////////////////// -/* -Class: - TileRenderPipelineDescriptor -Class Methods: - alloc -Methods: - init - binaryArchives - colorAttachments - label - maxTotalThreadsPerThreadgroup - rasterSampleCount - reset - setBinaryArchives - setLabel - setMaxTotalThreadsPerThreadgroup - setRasterSampleCount - setThreadgroupSizeMatchesTileSize - setTileFunction - threadgroupSizeMatchesTileSize - tileBuffers - tileFunction -*/ + @(objc_class="MTLTileRenderPipelineDescriptor") TileRenderPipelineDescriptor :: struct { using _: NS.Copying(TileRenderPipelineDescriptor) } @@ -4963,15 +3858,7 @@ TileRenderPipelineDescriptor_tileFunction :: #force_inline proc "c" (self: ^Tile //////////////////////////////////////////////////////////////////////////////// -/* -Class: - Type -Class Methods: - alloc -Methods: - init - dataType -*/ + @(objc_class="MTLType") Type :: struct { using _: NS.Object } @@ -4990,20 +3877,7 @@ Type_dataType :: #force_inline proc "c" (self: ^Type) -> DataType { //////////////////////////////////////////////////////////////////////////////// -/* -Class: - VertexAttribute -Class Methods: - alloc -Methods: - init - attributeIndex - attributeType - isActive - isPatchControlPointData - isPatchData - name -*/ + @(objc_class="MTLVertexAttribute") VertexAttribute :: struct { using _: NS.Object } @@ -5042,20 +3916,7 @@ VertexAttribute_name :: #force_inline proc "c" (self: ^VertexAttribute) -> ^NS.S //////////////////////////////////////////////////////////////////////////////// -/* -Class: - VertexAttributeDescriptor -Class Methods: - alloc -Methods: - init - bufferIndex - format - offset - setBufferIndex - setFormat - setOffset -*/ + @(objc_class="MTLVertexAttributeDescriptor") VertexAttributeDescriptor :: struct { using _: NS.Copying(VertexAttributeDescriptor) } @@ -5094,16 +3955,7 @@ VertexAttributeDescriptor_setOffset :: #force_inline proc "c" (self: ^VertexAttr //////////////////////////////////////////////////////////////////////////////// -/* -Class: - VertexAttributeDescriptorArray -Class Methods: - alloc -Methods: - init - objectAtIndexedSubscript - setObject -*/ + @(objc_class="MTLVertexAttributeDescriptorArray") VertexAttributeDescriptorArray :: struct { using _: NS.Object } @@ -5126,20 +3978,7 @@ VertexAttributeDescriptorArray_setObject :: #force_inline proc "c" (self: ^Verte //////////////////////////////////////////////////////////////////////////////// -/* -Class: - VertexBufferLayoutDescriptor -Class Methods: - alloc -Methods: - init - setStepFunction - setStepRate - setStride - stepFunction - stepRate - stride -*/ + @(objc_class="MTLVertexBufferLayoutDescriptor") VertexBufferLayoutDescriptor :: struct { using _: NS.Copying(VertexBufferLayoutDescriptor) } @@ -5178,16 +4017,6 @@ VertexBufferLayoutDescriptor_stride :: #force_inline proc "c" (self: ^VertexBuff //////////////////////////////////////////////////////////////////////////////// -/* -Class: - VertexBufferLayoutDescriptorArray -Class Methods: - alloc -Methods: - init - objectAtIndexedSubscript - setObject -*/ @(objc_class="MTLVertexBufferLayoutDescriptorArray") VertexBufferLayoutDescriptorArray :: struct { using _: NS.Object } @@ -5210,18 +4039,6 @@ VertexBufferLayoutDescriptorArray_setObject :: #force_inline proc "c" (self: ^Ve //////////////////////////////////////////////////////////////////////////////// -/* -Class: - VertexDescriptor -Class Methods: - alloc - vertexDescriptor -Methods: - init - attributes - layouts - reset -*/ @(objc_class="MTLVertexDescriptor") VertexDescriptor :: struct { using _: NS.Copying(VertexDescriptor) } @@ -5252,17 +4069,7 @@ VertexDescriptor_vertexDescriptor :: #force_inline proc "c" () -> ^VertexDescrip //////////////////////////////////////////////////////////////////////////////// -/* -Class: - VisibleFunctionTableDescriptor -Class Methods: - alloc - visibleFunctionTableDescriptor -Methods: - init - functionCount - setFunctionCount -*/ + @(objc_class="MTLVisibleFunctionTableDescriptor") VisibleFunctionTableDescriptor :: struct { using _: NS.Copying(VisibleFunctionTableDescriptor) } @@ -5289,14 +4096,7 @@ VisibleFunctionTableDescriptor_visibleFunctionTableDescriptor :: #force_inline p //////////////////////////////////////////////////////////////////////////////// -/* -Class: - AccelerationStructure -Class Methods: -Methods: - size - getResourceID -*/ + @(objc_class="MTLAccelerationStructure") AccelerationStructure :: struct { using _: Resource } @@ -5312,25 +4112,7 @@ AccelerationStructure_getResourceID :: #force_inline proc "c" (self: ^Accelerati //////////////////////////////////////////////////////////////////////////////// -/* -Class: - AccelerationStructureCommandEncoder -Class Methods: -Methods: - buildAccelerationStructure - copyAccelerationStructure - copyAndCompactAccelerationStructure - refitAccelerationStructure - refitAccelerationStructureWithOptions - sampleCountersInBuffer - updateFence - useHeap - useHeaps - useResource - useResources - waitForFence - writeCompactedAccelerationStructureSize -*/ + @(objc_class="MTLAccelerationStructureCommandEncoder") AccelerationStructureCommandEncoder :: struct { using _: CommandEncoder } @@ -5590,38 +4372,6 @@ ObjectPayloadBinding_objectPayloadDataSize :: #force_inline proc "c" (self: ^Obj //////////////////////////////////////////////////////////////////////////////// -/* -Class: - ArgumentEncoder -Class Methods: -Methods: - alignment - constantDataAtIndex - device - encodedLength - label - newArgumentEncoderForBufferAtIndex - setAccelerationStructure - setArgumentBuffer - setArgumentBuffer - setBuffer - setBuffers - setComputePipelineState - setComputePipelineStates - setIndirectCommandBuffer - setIndirectCommandBuffers - setIntersectionFunctionTable - setIntersectionFunctionTables - setLabel - setRenderPipelineState - setRenderPipelineStates - setSamplerState - setSamplerStates - setTexture - setTextures - setVisibleFunctionTable - setVisibleFunctionTables -*/ @(objc_class="MTLArgumentEncoder") ArgumentEncoder :: struct { using _: NS.Object } @@ -5740,19 +4490,6 @@ ArgumentEncoder_setVisibleFunctionTables :: #force_inline proc "odin" (self: ^Ar //////////////////////////////////////////////////////////////////////////////// -/* -Class: - BinaryArchive -Class Methods: -Methods: - addComputePipelineFunctionsWithDescriptor - addRenderPipelineFunctionsWithDescriptor - addTileRenderPipelineFunctionsWithDescriptor - device - label - serializeToURL - setLabel -*/ @(objc_class="MTLBinaryArchive") BinaryArchive :: struct { using _: NS.Copying(BinaryArchive) } @@ -5798,37 +4535,6 @@ BinaryArchive_addFunction :: #force_inline proc "contextless" (self: ^BinaryArch //////////////////////////////////////////////////////////////////////////////// -/* -Class: - BlitCommandEncoder -Class Methods: -Methods: - copyFromBuffer - copyFromBuffer - copyFromBuffer - copyFromTexture - copyFromTexture - copyFromTexture - copyFromTexture - copyFromTexture - copyIndirectCommandBuffer - fillBuffer - generateMipmapsForTexture - getTextureAccessCounters - optimizeContentsForCPUAccess - optimizeContentsForCPUAccess - optimizeContentsForGPUAccess - optimizeContentsForGPUAccess - optimizeIndirectCommandBuffer - resetCommandsInBuffer - resetTextureAccessCounters - resolveCounters - sampleCountersInBuffer - synchronizeResource - synchronizeTexture - updateFence - waitForFence -*/ @(objc_class="MTLBlitCommandEncoder") BlitCommandEncoder :: struct { using _: CommandEncoder } @@ -5935,20 +4641,6 @@ BlitCommandEncoder_waitForFence :: #force_inline proc "c" (self: ^BlitCommandEnc //////////////////////////////////////////////////////////////////////////////// -/* -Class: - Buffer -Class Methods: -Methods: - addDebugMarker - contents - didModifyRange - length - newRemoteBufferViewForDevice - newTextureWithDescriptor - remoteStorageBuffer - removeAllDebugMarkers -*/ @(objc_class="MTLBuffer") Buffer :: struct { using _: Resource } @@ -6013,18 +4705,6 @@ Buffer_gpuAddress :: #force_inline proc "c" (self: ^Buffer) -> u64 { //////////////////////////////////////////////////////////////////////////////// -/* -Class: - CaptureScope -Class Methods: -Methods: - beginScope - commandQueue - device - endScope - label - setLabel -*/ @(objc_class="MTLCaptureScope") CaptureScope :: struct { using _: NS.Object } @@ -6055,48 +4735,6 @@ CaptureScope_setLabel :: #force_inline proc "c" (self: ^CaptureScope, label: ^NS //////////////////////////////////////////////////////////////////////////////// -/* -Class: - CommandBuffer -Class Methods: -Methods: - GPUEndTime - GPUStartTime - accelerationStructureCommandEncoder - addCompletedHandler - addScheduledHandler - blitCommandEncoder - blitCommandEncoderWithDescriptor - commandQueue - commit - computeCommandEncoder - computeCommandEncoderWithDescriptor - computeCommandEncoderWithDispatchType - device - encodeSignalEvent - encodeWaitForEvent - enqueue - error - errorOptions - kernelEndTime - kernelStartTime - label - logs - parallelRenderCommandEncoderWithDescriptor - popDebugGroup - presentDrawable - presentDrawable - presentDrawable - pushDebugGroup - renderCommandEncoderWithDescriptor - resourceStateCommandEncoder - resourceStateCommandEncoderWithDescriptor - retainedReferences - setLabel - status - waitUntilCompleted - waitUntilScheduled -*/ @(objc_class="MTLCommandBuffer") CommandBuffer :: struct { using _: NS.Object } @@ -6251,15 +4889,6 @@ CommandBuffer_waitUntilScheduled :: #force_inline proc "c" (self: ^CommandBuffer //////////////////////////////////////////////////////////////////////////////// -/* -Class: - CommandBufferEncoderInfo -Class Methods: -Methods: - debugSignposts - errorState - label -*/ @(objc_class="MTLCommandBufferEncoderInfo") CommandBufferEncoderInfo :: struct { using _: NS.Object } @@ -6278,19 +4907,6 @@ CommandBufferEncoderInfo_label :: #force_inline proc "c" (self: ^CommandBufferEn //////////////////////////////////////////////////////////////////////////////// -/* -Class: - CommandEncoder -Class Methods: -Methods: - device - endEncoding - insertDebugSignpost - label - popDebugGroup - pushDebugGroup - setLabel -*/ @(objc_class="MTLCommandEncoder") CommandEncoder :: struct { using _: NS.Object } @@ -6325,19 +4941,6 @@ CommandEncoder_setLabel :: #force_inline proc "c" (self: ^CommandEncoder, label: //////////////////////////////////////////////////////////////////////////////// -/* -Class: - CommandQueue -Class Methods: -Methods: - commandBuffer - commandBufferWithDescriptor - commandBufferWithUnretainedReferences - device - insertDebugCaptureBoundary - label - setLabel -*/ @(objc_class="MTLCommandQueue") CommandQueue :: struct { using _: NS.Object } @@ -6372,47 +4975,6 @@ CommandQueue_setLabel :: #force_inline proc "c" (self: ^CommandQueue, label: ^NS //////////////////////////////////////////////////////////////////////////////// -/* -Class: - ComputeCommandEncoder -Class Methods: -Methods: - dispatchThreadgroups - dispatchThreadgroupsWithIndirectBuffer - dispatchThreads - dispatchType - executeCommandsInBuffer - executeCommandsInBuffer - memoryBarrierWithResources - memoryBarrierWithScope - sampleCountersInBuffer - setAccelerationStructure - setBuffer - setBufferOffset - setBuffers - setBytes - setComputePipelineState - setImageblockWidth - setIntersectionFunctionTable - setIntersectionFunctionTables - setSamplerState - setSamplerState - setSamplerStates - setSamplerStates - setStageInRegion - setStageInRegionWithIndirectBuffer - setTexture - setTextures - setThreadgroupMemoryLength - setVisibleFunctionTable - setVisibleFunctionTables - updateFence - useHeap - useHeaps - useResource - useResources - waitForFence -*/ @(objc_class="MTLComputeCommandEncoder") ComputeCommandEncoder :: struct { using _: CommandEncoder } @@ -6562,23 +5124,6 @@ ComputeCommandEncoder_waitForFence :: #force_inline proc "c" (self: ^ComputeComm //////////////////////////////////////////////////////////////////////////////// -/* -Class: - ComputePipelineState -Class Methods: -Methods: - device - functionHandleWithFunction - imageblockMemoryLengthForDimensions - label - maxTotalThreadsPerThreadgroup - newComputePipelineStateWithAdditionalBinaryFunctions - newIntersectionFunctionTableWithDescriptor - newVisibleFunctionTableWithDescriptor - staticThreadgroupMemoryLength - supportIndirectCommandBuffers - threadExecutionWidth -*/ @(objc_class="MTLComputePipelineState") ComputePipelineState :: struct { using _: NS.Object } @@ -6630,13 +5175,6 @@ ComputePipelineState_threadExecutionWidth :: #force_inline proc "c" (self: ^Comp //////////////////////////////////////////////////////////////////////////////// -/* -Class: - Counter -Class Methods: -Methods: - name -*/ @(objc_class="MTLCounter") Counter :: struct { using _: NS.Object } @@ -6647,16 +5185,6 @@ Counter_name :: #force_inline proc "c" (self: ^Counter) -> ^NS.String { //////////////////////////////////////////////////////////////////////////////// -/* -Class: - CounterSampleBuffer -Class Methods: -Methods: - device - label - resolveCounterRange - sampleCount -*/ @(objc_class="MTLCounterSampleBuffer") CounterSampleBuffer :: struct { using _: NS.Object } @@ -6679,14 +5207,6 @@ CounterSampleBuffer_sampleCount :: #force_inline proc "c" (self: ^CounterSampleB //////////////////////////////////////////////////////////////////////////////// -/* -Class: - CounterSet -Class Methods: -Methods: - counters - name -*/ @(objc_class="MTLCounterSet") CounterSet :: struct { using _: NS.Object } @@ -6701,14 +5221,6 @@ CounterSet_name :: #force_inline proc "c" (self: ^CounterSet) -> ^NS.String { //////////////////////////////////////////////////////////////////////////////// -/* -Class: - DepthStencilState -Class Methods: -Methods: - device - label -*/ @(objc_class="MTLDepthStencilState") DepthStencilState :: struct { using _: NS.Object } @@ -6723,107 +5235,6 @@ DepthStencilState_label :: #force_inline proc "c" (self: ^DepthStencilState) -> //////////////////////////////////////////////////////////////////////////////// -/* -Class: - Device -Class Methods: -Methods: - accelerationStructureSizesWithDescriptor - areBarycentricCoordsSupported - areProgrammableSamplePositionsSupported - areRasterOrderGroupsSupported - argumentBuffersSupport - convertSparsePixelRegions - convertSparseTileRegions - counterSets - currentAllocatedSize - getDefaultSamplePositions - hasUnifiedMemory - heapBufferSizeAndAlignWithLength - heapTextureSizeAndAlignWithDescriptor - isDepth24Stencil8PixelFormatSupported - isHeadless - isLowPower - isRemovable - location - locationNumber - maxArgumentBufferSamplerCount - maxBufferLength - maxThreadgroupMemoryLength - maxThreadsPerThreadgroup - maxTransferRate - minimumLinearTextureAlignmentForPixelFormat - minimumTextureBufferAlignmentForPixelFormat - name - newAccelerationStructureWithDescriptor - newAccelerationStructureWithSize - newArgumentEncoderWithArguments - newBinaryArchiveWithDescriptor - newBufferWithBytes - newBufferWithBytesNoCopy - newBufferWithLength - newCommandQueue - newCommandQueueWithMaxCommandBufferCount - newComputePipelineStateWithDescriptor - newComputePipelineStateWithDescriptor - newComputePipelineStateWithFunction - newComputePipelineStateWithFunction - newComputePipelineStateWithFunction - newComputePipelineStateWithFunction - newCounterSampleBufferWithDescriptor - newDefaultLibrary - newDefaultLibraryWithBundle - newDepthStencilStateWithDescriptor - newDynamicLibrary - newDynamicLibraryWithURL - newEvent - newFence - newHeapWithDescriptor - newIndirectCommandBufferWithDescriptor - newLibraryWithData - newLibraryWithFile - newLibraryWithSource - newLibraryWithSource - newLibraryWithURL - newRasterizationRateMapWithDescriptor - newRenderPipelineStateWithDescriptor - newRenderPipelineStateWithDescriptor - newRenderPipelineStateWithDescriptor - newRenderPipelineStateWithDescriptor - newRenderPipelineStateWithTileDescriptor - newRenderPipelineStateWithTileDescriptor - newSamplerState - newSharedEvent - newSharedEventWithHandle - newSharedTextureWithDescriptor - newSharedTextureWithHandle - newTextureWithDescriptor - newTextureWithDescriptor - peerCount - peerGroupID - peerIndex - readWriteTextureSupport - recommendedMaxWorkingSetSize - registryID - sampleTimestamps - sparseTileSizeInBytes - sparseTileSizeWithTextureType - supports32BitFloatFiltering - supports32BitMSAA - supportsBCTextureCompression - supportsCounterSampling - supportsDynamicLibraries - supportsFamily - supportsFeatureSet - supportsFunctionPointers - supportsPullModelInterpolation - supportsQueryTextureLOD - supportsRasterizationRateMapWithLayerCount - supportsRaytracing - supportsShaderBarycentricCoordinates - supportsTextureSampleCount - supportsVertexAmplificationCount -*/ @(objc_class="MTLDevice") Device :: struct { using _: NS.Object } @@ -7319,18 +5730,6 @@ Device_newIOCommandQueue :: #force_inline proc "contextless" (self: ^Device, des //////////////////////////////////////////////////////////////////////////////// -/* -Class: - Drawable -Class Methods: -Methods: - addPresentedHandler - drawableID - present - presentAfterMinimumDuration - presentAtTime - presentedTime -*/ @(objc_class="MTLDrawable") Drawable :: struct { using _: NS.Object } @@ -7361,17 +5760,6 @@ Drawable_presentedTime :: #force_inline proc "c" (self: ^Drawable) -> CFTimeInte //////////////////////////////////////////////////////////////////////////////// -/* -Class: - DynamicLibrary -Class Methods: -Methods: - device - installName - label - serializeToURL - setLabel -*/ @(objc_class="MTLDynamicLibrary") DynamicLibrary :: struct { using _: NS.Object } @@ -7399,15 +5787,6 @@ DynamicLibrary_setLabel :: #force_inline proc "c" (self: ^DynamicLibrary, label: //////////////////////////////////////////////////////////////////////////////// -/* -Class: - Event -Class Methods: -Methods: - device - label - setLabel -*/ @(objc_class="MTLEvent") Event :: struct { using _: NS.Object } @@ -7426,15 +5805,6 @@ Event_setLabel :: #force_inline proc "c" (self: ^Event, label: ^NS.String) { //////////////////////////////////////////////////////////////////////////////// -/* -Class: - Fence -Class Methods: -Methods: - device - label - setLabel -*/ @(objc_class="MTLFence") Fence :: struct { using _: NS.Object } @@ -7453,25 +5823,6 @@ Fence_setLabel :: #force_inline proc "c" (self: ^Fence, label: ^NS.String) { //////////////////////////////////////////////////////////////////////////////// -/* -Class: - Function -Class Methods: -Methods: - device - functionConstantsDictionary - functionType - label - name - newArgumentEncoderWithBufferIndex - newArgumentEncoderWithBufferIndex - options - patchControlPointCount - patchType - setLabel - stageInputAttributes - vertexAttributes -*/ @(objc_class="MTLFunction") Function :: struct { using _: NS.Object } @@ -7530,15 +5881,6 @@ Function_vertexAttributes :: #force_inline proc "c" (self: ^Function) -> ^NS.Arr //////////////////////////////////////////////////////////////////////////////// -/* -Class: - FunctionHandle -Class Methods: -Methods: - device - functionType - name -*/ @(objc_class="MTLFunctionHandle") FunctionHandle :: struct { using _: NS.Object } @@ -7557,26 +5899,13 @@ FunctionHandle_name :: #force_inline proc "c" (self: ^FunctionHandle) -> ^NS.Str //////////////////////////////////////////////////////////////////////////////// -/* -Class: - LogContainer -*/ @(objc_class="MTLLogContainer") LogContainer :: struct { using _: NS.FastEnumeration } //////////////////////////////////////////////////////////////////////////////// -/* -Class: - FunctionLog -Class Methods: -Methods: - debugLocation - encoderLabel - function - type -*/ + @(objc_class="MTLFunctionLog") FunctionLog :: struct { using _: NS.Object } @@ -7599,16 +5928,6 @@ FunctionLog_type :: #force_inline proc "c" (self: ^FunctionLog) -> FunctionLogTy //////////////////////////////////////////////////////////////////////////////// -/* -Class: - FunctionLogDebugLocation -Class Methods: -Methods: - URL - column - functionName - line -*/ @(objc_class="MTLFunctionLogDebugLocation") FunctionLogDebugLocation :: struct { using _: NS.Object } @@ -7631,29 +5950,7 @@ FunctionLogDebugLocation_line :: #force_inline proc "c" (self: ^FunctionLogDebug //////////////////////////////////////////////////////////////////////////////// -/* -Class: - Heap -Class Methods: -Methods: - cpuCacheMode - currentAllocatedSize - device - hazardTrackingMode - label - maxAvailableSizeWithAlignment - newBufferWithLength - newBufferWithLength - newTextureWithDescriptor - newTextureWithDescriptor - resourceOptions - setLabel - setPurgeableState - size - storageMode - type - usedSize -*/ + @(objc_class="MTLHeap") Heap :: struct { using _: NS.Object } @@ -7764,16 +6061,6 @@ Heap_usedSize :: #force_inline proc "c" (self: ^Heap) -> NS.UInteger { //////////////////////////////////////////////////////////////////////////////// -/* -Class: - IndirectCommandBuffer -Class Methods: -Methods: - indirectComputeCommandAtIndex - indirectRenderCommandAtIndex - resetWithRange - size -*/ @(objc_class="MTLIndirectCommandBuffer") IndirectCommandBuffer :: struct { using _: Resource } @@ -7811,22 +6098,6 @@ IndirectCommandBuffer_gpuResourceID :: #force_inline proc "c" (self: ^IndirectCo //////////////////////////////////////////////////////////////////////////////// -/* -Class: - IndirectComputeCommand -Class Methods: -Methods: - clearBarrier - concurrentDispatchThreadgroups - concurrentDispatchThreads - reset - setBarrier - setComputePipelineState - setImageblockWidth - setKernelBuffer - setStageInRegion - setThreadgroupMemoryLength -*/ @(objc_class="MTLIndirectComputeCommand") IndirectComputeCommand :: struct { using _: NS.Object } @@ -7873,20 +6144,6 @@ IndirectComputeCommand_setThreadgroupMemoryLength :: #force_inline proc "c" (sel //////////////////////////////////////////////////////////////////////////////// -/* -Class: - IndirectRenderCommand -Class Methods: -Methods: - drawIndexedPatches - drawIndexedPrimitives - drawPatches - drawPrimitives - reset - setFragmentBuffer - setRenderPipelineState - setVertexBuffer -*/ @(objc_class="MTLIndirectRenderCommand") IndirectRenderCommand :: struct { using _: NS.Object } @@ -7925,20 +6182,6 @@ IndirectRenderCommand_setVertexBuffer :: #force_inline proc "c" (self: ^Indirect //////////////////////////////////////////////////////////////////////////////// -/* -Class: - IntersectionFunctionTable -Class Methods: -Methods: - setBuffer - setBuffers - setFunction - setFunctions - setOpaqueTriangleIntersectionFunctionWithSignature - setOpaqueTriangleIntersectionFunctionWithSignature - setVisibleFunctionTable - setVisibleFunctionTables -*/ @(objc_class="MTLIntersectionFunctionTable") IntersectionFunctionTable :: struct { using _: Resource } @@ -8142,30 +6385,6 @@ IOCommandBuffer_error :: #force_inline proc "c" (self: ^IOCommandBuffer) -> ^NS. //////////////////////////////////////////////////////////////////////////////// - - - -//////////////////////////////////////////////////////////////////////////////// - -/* -Class: - Library -Class Methods: -Methods: - device - functionNames - installName - label - newFunctionWithDescriptor - newFunctionWithDescriptor - newFunctionWithName - newFunctionWithName - newFunctionWithName - newIntersectionFunctionWithDescriptor - newIntersectionFunctionWithDescriptor - setLabel - type -*/ @(objc_class="MTLLibrary") Library :: struct { using _: NS.Object } @@ -8236,19 +6455,6 @@ Library_type :: #force_inline proc "c" (self: ^Library) -> LibraryType { //////////////////////////////////////////////////////////////////////////////// -/* -Class: - ParallelRenderCommandEncoder -Class Methods: -Methods: - renderCommandEncoder - setColorStoreAction - setColorStoreActionOptions - setDepthStoreAction - setDepthStoreActionOptions - setStencilStoreAction - setStencilStoreActionOptions -*/ @(objc_class="MTLParallelRenderCommandEncoder") ParallelRenderCommandEncoder :: struct { using _: CommandEncoder } @@ -8283,22 +6489,6 @@ ParallelRenderCommandEncoder_setStencilStoreActionOptions :: #force_inline proc //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RasterizationRateMap -Class Methods: -Methods: - copyParameterDataToBuffer - device - label - layerCount - mapPhysicalToScreenCoordinates - mapScreenToPhysicalCoordinates - parameterBufferSizeAndAlign - physicalGranularity - physicalSizeForLayer - screenSize -*/ @(objc_class="MTLRasterizationRateMap") RasterizationRateMap :: struct { using _: NS.Object } @@ -8346,98 +6536,6 @@ RasterizationRateMap_screenSize :: #force_inline proc "c" (self: ^RasterizationR //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RenderCommandEncoder -Class Methods: -Methods: - dispatchThreadsPerTile - drawIndexedPatches - drawIndexedPatches - drawIndexedPrimitives - drawIndexedPrimitives - drawIndexedPrimitives - drawIndexedPrimitives - drawPatches - drawPatches - drawPrimitives - drawPrimitives - drawPrimitives - drawPrimitives - executeCommandsInBuffer - executeCommandsInBuffer - memoryBarrierWithResources - memoryBarrierWithScope - sampleCountersInBuffer - setBlendColorRed - setColorStoreAction - setColorStoreActionOptions - setCullMode - setDepthBias - setDepthClipMode - setDepthStencilState - setDepthStoreAction - setDepthStoreActionOptions - setFragmentBuffer - setFragmentBufferOffset - setFragmentBuffers - setFragmentBytes - setFragmentSamplerState - setFragmentSamplerState - setFragmentSamplerStates - setFragmentSamplerStates - setFragmentTexture - setFragmentTextures - setFrontFacingWinding - setRenderPipelineState - setScissorRect - setScissorRects - setStencilFrontReferenceValue - setStencilReferenceValue - setStencilStoreAction - setStencilStoreActionOptions - setTessellationFactorBuffer - setTessellationFactorScale - setThreadgroupMemoryLength - setTileBuffer - setTileBufferOffset - setTileBuffers - setTileBytes - setTileSamplerState - setTileSamplerState - setTileSamplerStates - setTileSamplerStates - setTileTexture - setTileTextures - setTriangleFillMode - setVertexAmplificationCount - setVertexBuffer - setVertexBufferOffset - setVertexBuffers - setVertexBytes - setVertexSamplerState - setVertexSamplerState - setVertexSamplerStates - setVertexSamplerStates - setVertexTexture - setVertexTextures - setViewport - setViewports - setVisibilityResultMode - textureBarrier - tileHeight - tileWidth - updateFence - useHeap - useHeap - useHeaps - useHeaps - useResource - useResource - useResources - useResources - waitForFence -*/ @(objc_class="MTLRenderCommandEncoder") RenderCommandEncoder :: struct { using _: CommandEncoder } @@ -8890,10 +6988,6 @@ RenderCommandEncoder_drawMeshThreadgroupsWithIndirectBuffer :: #force_inline pro //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RenderPipelineFunctionsDescriptor -*/ @(objc_class="MTLRenderPipelineFunctionsDescriptor") RenderPipelineFunctionsDescriptor :: struct { using _: NS.Copying(RenderPipelineFunctionsDescriptor) } @@ -8935,19 +7029,6 @@ RenderPipelineFunctionsDescriptor_setTileAdditionalBinaryFunctions :: #force_inl //////////////////////////////////////////////////////////////////////////////// -/* -Class: - RenderPipelineState -Class Methods: -Methods: - device - imageblockMemoryLengthForDimensions - imageblockSampleLength - label - maxTotalThreadsPerThreadgroup - supportIndirectCommandBuffers - threadgroupSizeMatchesTileSize -*/ @(objc_class="MTLRenderPipelineState") RenderPipelineState :: struct { using _: NS.Object } @@ -9031,25 +7112,6 @@ RenderPipelineState_gpuResourceID :: #force_inline proc "c" (self: ^RenderPipeli //////////////////////////////////////////////////////////////////////////////// -/* -Class: - Resource -Class Methods: -Methods: - allocatedSize - cpuCacheMode - device - hazardTrackingMode - heap - heapOffset - isAliasable - label - makeAliasable - resourceOptions - setLabel - setPurgeableState - storageMode -*/ @(objc_class="MTLResource") Resource :: struct { using _: NS.Object } @@ -9108,17 +7170,6 @@ Resource_storageMode :: #force_inline proc "c" (self: ^Resource) -> StorageMode //////////////////////////////////////////////////////////////////////////////// -/* -Class: - ResourceStateCommandEncoder -Class Methods: -Methods: - updateFence - updateTextureMapping - updateTextureMapping - updateTextureMappings - waitForFence -*/ @(objc_class="MTLResourceStateCommandEncoder") ResourceStateCommandEncoder :: struct { using _: CommandEncoder } @@ -9157,14 +7208,6 @@ ResourceStateCommandEncoder_moveTextureMappingsFromTexture :: #force_inline proc //////////////////////////////////////////////////////////////////////////////// -/* -Class: - SamplerState -Class Methods: -Methods: - device - label -*/ @(objc_class="MTLSamplerState") SamplerState :: struct { using _: NS.Object } @@ -9183,16 +7226,6 @@ SamplerState_gpuResourceID :: #force_inline proc "c" (self: ^SamplerState) -> Re //////////////////////////////////////////////////////////////////////////////// -/* -Class: - SharedEvent -Class Methods: -Methods: - newSharedEventHandle - notifyListener - setSignaledValue - signaledValue -*/ @(objc_class="MTLSharedEvent") SharedEvent :: struct { using _: Event } @@ -9215,47 +7248,6 @@ SharedEvent_signaledValue :: #force_inline proc "c" (self: ^SharedEvent) -> u64 //////////////////////////////////////////////////////////////////////////////// -/* -Class: - Texture -Class Methods: -Methods: - allowGPUOptimizedContents - arrayLength - buffer - bufferBytesPerRow - bufferOffset - depth - firstMipmapInTail - getBytes - getBytes - height - iosurface - iosurfacePlane - isFramebufferOnly - isShareable - isSparse - mipmapLevelCount - newRemoteTextureViewForDevice - newSharedTextureHandle - newTextureViewWithPixelFormat - newTextureViewWithPixelFormat - newTextureViewWithPixelFormat - parentRelativeLevel - parentRelativeSlice - parentTexture - pixelFormat - remoteStorageTexture - replaceRegion - replaceRegion - rootResource - sampleCount - swizzle - tailSizeInBytes - textureType - usage - width -*/ @(objc_class="MTLTexture") Texture :: struct { using _: Resource } @@ -9419,14 +7411,6 @@ Texture_gpuResourceID :: #force_inline proc "c" (self: ^Texture) -> ResourceID { //////////////////////////////////////////////////////////////////////////////// -/* -Class: - VisibleFunctionTable -Class Methods: -Methods: - setFunction - setFunctions -*/ @(objc_class="MTLVisibleFunctionTable") VisibleFunctionTable :: struct { using _: Resource } @@ -9446,7 +7430,4 @@ VisibleFunctionTable_gpuResourceID :: #force_inline proc "c" (self: ^VisibleFunc -// TODO: Entire FunctionStitching API (which appears not to be in been missed from the generator) - - From d37699f51a658d8dab5e0c62e1fea4cf8883cb4d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 7 Jun 2023 23:07:31 +0100 Subject: [PATCH 012/144] Add bsd to mem/virtual --- core/mem/virtual/virtual_bsd.odin | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 core/mem/virtual/virtual_bsd.odin diff --git a/core/mem/virtual/virtual_bsd.odin b/core/mem/virtual/virtual_bsd.odin new file mode 100644 index 000000000..103e48074 --- /dev/null +++ b/core/mem/virtual/virtual_bsd.odin @@ -0,0 +1,24 @@ +//+build freebsd, openbsd +//+private +package mem_virtual + + + +_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { + return nil, nil +} + +_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error { + return nil +} +_decommit :: proc "contextless" (data: rawptr, size: uint) { +} +_release :: proc "contextless" (data: rawptr, size: uint) { +} +_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool { + return false +} + +_platform_memory_init :: proc() { + +} From 21c1618d945a16ad5dcaa7ae4917672712564a6a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 8 Jun 2023 00:28:35 +0100 Subject: [PATCH 013/144] Add botan libraries to all/all_vendor.odin --- examples/all/all_vendor.odin | 39 ++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/examples/all/all_vendor.odin b/examples/all/all_vendor.odin index 22c55c14e..fe1080e20 100644 --- a/examples/all/all_vendor.odin +++ b/examples/all/all_vendor.odin @@ -1,6 +1,23 @@ package all -import botan "vendor:botan" +import botan_bindings "vendor:botan/bindings" +import botan_blake2b "vendor:botan/blake2b" +import gost "vendor:botan/gost" +import keccak "vendor:botan/keccak" +import md4 "vendor:botan/md4" +import md5 "vendor:botan/md5" +import ripemd "vendor:botan/ripemd" +import sha1 "vendor:botan/sha1" +import sha2 "vendor:botan/sha2" +import sha3 "vendor:botan/sha3" +import shake "vendor:botan/shake" +import siphash "vendor:botan/siphash" +import skein512 "vendor:botan/skein512" +import sm3 "vendor:botan/sm3" +import streebog "vendor:botan/streebog" +import tiger "vendor:botan/tiger" +import whirlpool "vendor:botan/whirlpool" + import cgltf "vendor:cgltf" // import commonmark "vendor:commonmark" import ENet "vendor:ENet" @@ -30,7 +47,25 @@ import CA "vendor:darwin/QuartzCore" // NOTE(bill): only one can be checked at a time import lua_5_4 "vendor:lua/5.4" -_ :: botan +_ :: botan_bindings +_ :: botan_blake2b +_ :: gost +_ :: keccak +_ :: md4 +_ :: md5 +_ :: ripemd +_ :: sha1 +_ :: sha2 +_ :: sha3 +_ :: shake +_ :: siphash +_ :: skein512 +_ :: sm3 +_ :: streebog +_ :: tiger +_ :: whirlpool + + _ :: cgltf // _ :: commonmark _ :: ENet From fed0c2ea26665d183818b07a884d3b48c1a60e36 Mon Sep 17 00:00:00 2001 From: James Duran Date: Wed, 7 Jun 2023 21:55:08 -0700 Subject: [PATCH 014/144] Fix Timeval for darwin and linux --- core/net/socket_darwin.odin | 8 ++++---- core/net/socket_linux.odin | 8 ++++---- core/os/os_darwin.odin | 2 +- core/os/os_linux.odin | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/net/socket_darwin.odin b/core/net/socket_darwin.odin index f00be9915..081892afd 100644 --- a/core/net/socket_darwin.odin +++ b/core/net/socket_darwin.odin @@ -268,9 +268,9 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca t, ok := value.(time.Duration) if !ok do panic("set_option() value must be a time.Duration here", loc) - nanos := time.duration_nanoseconds(t) - timeval_value.nanoseconds = int(nanos % 1e9) - timeval_value.seconds = (nanos - i64(timeval_value.nanoseconds)) / 1e9 + micros := i64(time.duration_microseconds(t)) + timeval_value.microseconds = int(micros % 1e6) + timeval_value.seconds = (micros - i64(timeval_value.microseconds)) / 1e6 ptr = &timeval_value len = size_of(timeval_value) @@ -368,4 +368,4 @@ _sockaddr_to_endpoint :: proc(native_addr: ^os.SOCKADDR_STORAGE_LH) -> (ep: Endp panic("native_addr is neither IP4 or IP6 address") } return -} \ No newline at end of file +} diff --git a/core/net/socket_linux.odin b/core/net/socket_linux.odin index 690e09ab7..b7141e8ba 100644 --- a/core/net/socket_linux.odin +++ b/core/net/socket_linux.odin @@ -283,9 +283,9 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca t, ok := value.(time.Duration) if !ok do panic("set_option() value must be a time.Duration here", loc) - nanos := time.duration_nanoseconds(t) - timeval_value.nanoseconds = int(nanos % 1e9) - timeval_value.seconds = (nanos - i64(timeval_value.nanoseconds)) / 1e9 + micros := i64(time.duration_microseconds(t)) + timeval_value.microseconds = int(micros % 1e6) + timeval_value.seconds = (micros - i64(timeval_value.microseconds)) / 1e6 ptr = &timeval_value len = size_of(timeval_value) @@ -404,4 +404,4 @@ _sockaddr_basic_to_endpoint :: proc(native_addr: ^os.SOCKADDR) -> (ep: Endpoint) panic("native_addr is neither IP4 or IP6 address") } return -} \ No newline at end of file +} diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin index a2d68aeed..adb438f60 100644 --- a/core/os/os_darwin.odin +++ b/core/os/os_darwin.odin @@ -355,7 +355,7 @@ in6_addr :: struct #packed { Timeval :: struct { seconds: i64, - nanoseconds: int, + microseconds: int, } Linger :: struct { diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index 3dc48087a..7a0a4b1dd 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -241,7 +241,7 @@ socklen_t :: c.int Timeval :: struct { seconds: i64, - nanoseconds: int, + microseconds: int, } // "Argv" arguments converted to Odin strings @@ -1086,4 +1086,4 @@ fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Errno) { return 0, _get_errno(result) } return result, ERROR_NONE -} \ No newline at end of file +} From 3f6775e29b2378d189cca72733cf8953681281e4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 8 Jun 2023 16:34:36 +0100 Subject: [PATCH 015/144] Update to new io interface --- core/bufio/read_writer.odin | 66 ++-- core/bufio/reader.odin | 58 +--- core/bufio/writer.odin | 59 ++-- core/bytes/buffer.odin | 82 ++--- core/bytes/reader.odin | 59 ++-- core/compress/common.odin | 5 +- core/crypto/blake/blake.odin | 8 +- core/crypto/blake2b/blake2b.odin | 2 +- core/crypto/blake2s/blake2s.odin | 2 +- core/crypto/gost/gost.odin | 2 +- core/crypto/groestl/groestl.odin | 8 +- core/crypto/haval/haval.odin | 30 +- core/crypto/jh/jh.odin | 8 +- core/crypto/keccak/keccak.odin | 8 +- core/crypto/md2/md2.odin | 2 +- core/crypto/md4/md4.odin | 2 +- core/crypto/md5/md5.odin | 2 +- core/crypto/ripemd/ripemd.odin | 8 +- core/crypto/sha1/sha1.odin | 2 +- core/crypto/sha2/sha2.odin | 8 +- core/crypto/sha3/sha3.odin | 8 +- core/crypto/shake/shake.odin | 4 +- core/crypto/sm3/sm3.odin | 2 +- core/crypto/streebog/streebog.odin | 4 +- core/crypto/tiger/tiger.odin | 6 +- core/crypto/tiger2/tiger2.odin | 6 +- core/crypto/whirlpool/whirlpool.odin | 2 +- core/io/conv.odin | 74 +---- core/io/io.odin | 419 +++++++++----------------- core/io/multi.odin | 88 +++--- core/io/util.odin | 79 ++--- core/os/stream.odin | 90 +++--- core/strings/builder.odin | 41 +-- core/strings/reader.odin | 58 ++-- vendor/botan/blake2b/blake2b.odin | 2 +- vendor/botan/gost/gost.odin | 2 +- vendor/botan/keccak/keccak.odin | 2 +- vendor/botan/md4/md4.odin | 2 +- vendor/botan/md5/md5.odin | 2 +- vendor/botan/ripemd/ripemd.odin | 2 +- vendor/botan/sha1/sha1.odin | 2 +- vendor/botan/sha2/sha2.odin | 8 +- vendor/botan/sha3/sha3.odin | 8 +- vendor/botan/shake/shake.odin | 4 +- vendor/botan/skein512/skein512.odin | 6 +- vendor/botan/sm3/sm3.odin | 2 +- vendor/botan/streebog/streebog.odin | 4 +- vendor/botan/tiger/tiger.odin | 6 +- vendor/botan/whirlpool/whirlpool.odin | 2 +- 49 files changed, 504 insertions(+), 852 deletions(-) diff --git a/core/bufio/read_writer.odin b/core/bufio/read_writer.odin index f9ae1ed45..3e6bd3aa0 100644 --- a/core/bufio/read_writer.odin +++ b/core/bufio/read_writer.odin @@ -14,51 +14,29 @@ read_writer_init :: proc(rw: ^Read_Writer, r: ^Reader, w: ^Writer) { } read_writer_to_stream :: proc(rw: ^Read_Writer) -> (s: io.Stream) { - s.stream_data = rw - s.stream_vtable = &_read_writer_vtable + s.procedure = _read_writer_procedure + s.data = rw return } @(private) -_read_writer_vtable := io.Stream_VTable{ - impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { - b := (^Read_Writer)(s.stream_data).r - return reader_read(b, p) - }, - impl_unread_byte = proc(s: io.Stream) -> io.Error { - b := (^Read_Writer)(s.stream_data).r - return reader_unread_byte(b) - }, - impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) { - b := (^Read_Writer)(s.stream_data).r - return reader_read_rune(b) - }, - impl_unread_rune = proc(s: io.Stream) -> io.Error { - b := (^Read_Writer)(s.stream_data).r - return reader_unread_rune(b) - }, - impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) { - b := (^Read_Writer)(s.stream_data).r - return reader_write_to(b, w) - }, - impl_flush = proc(s: io.Stream) -> io.Error { - b := (^Read_Writer)(s.stream_data).w - return writer_flush(b) - }, - impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { - b := (^Read_Writer)(s.stream_data).w - return writer_write(b, p) - }, - impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error { - b := (^Read_Writer)(s.stream_data).w - return writer_write_byte(b, c) - }, - impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) { - b := (^Read_Writer)(s.stream_data).w - return writer_write_rune(b, r) - }, - impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) { - b := (^Read_Writer)(s.stream_data).w - return writer_read_from(b, r) - }, -} +_read_writer_procedure := proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { + rw := (^Read_Writer)(stream_data) + n_int: int + #partial switch mode { + case .Flush: + err = writer_flush(rw.w) + return + case .Read: + n_int, err = reader_read(rw.r, p) + n = i64(n_int) + return + case .Write: + n_int, err = writer_write(rw.w, p) + n = i64(n_int) + return + case .Query: + return io.query_utility({.Flush, .Read, .Write, .Query}) + } + return 0, .Empty +} \ No newline at end of file diff --git a/core/bufio/reader.odin b/core/bufio/reader.odin index 6bfc4cd9d..dc4e02c02 100644 --- a/core/bufio/reader.odin +++ b/core/bufio/reader.odin @@ -311,18 +311,6 @@ reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) { } m: i64 - if nr, ok := io.to_writer_to(b.rd); ok { - m, err = io.write_to(nr, w) - n += m - return n, err - } - - if nw, ok := io.to_reader_from(w); ok { - m, err = io.read_from(nw, b.rd) - n += m - return n, err - } - if b.w-b.r < len(b.buf) { if err = _reader_read_new_chunk(b); err != nil { return @@ -352,48 +340,28 @@ reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) { // reader_to_stream converts a Reader into an io.Stream reader_to_stream :: proc(b: ^Reader) -> (s: io.Stream) { - s.stream_data = b - s.stream_vtable = &_reader_vtable + s.data = b + s.procedure = _reader_proc return } @(private) -_reader_vtable := io.Stream_VTable{ - impl_destroy = proc(s: io.Stream) -> io.Error { - b := (^Reader)(s.stream_data) +_reader_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { + b := (^Reader)(stream_data) + #partial switch mode { + case .Read: + return io._i64_err(reader_read(b, p)) + case .Destroy: reader_destroy(b) - return nil - }, - impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { - b := (^Reader)(s.stream_data) - return reader_read(b, p) - }, - impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) { - b := (^Reader)(s.stream_data) - return reader_read_byte(b) - }, - impl_unread_byte = proc(s: io.Stream) -> io.Error { - b := (^Reader)(s.stream_data) - return reader_unread_byte(b) - }, - impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) { - b := (^Reader)(s.stream_data) - return reader_read_rune(b) - }, - impl_unread_rune = proc(s: io.Stream) -> io.Error { - b := (^Reader)(s.stream_data) - return reader_unread_rune(b) - }, - impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) { - b := (^Reader)(s.stream_data) - return reader_write_to(b, w) - }, + return + case .Query: + return io.query_utility({.Read, .Destroy, .Query}) + } + return 0, .Empty } - - // // Utility procedures // diff --git a/core/bufio/writer.odin b/core/bufio/writer.odin index ed0d557c5..b4234af43 100644 --- a/core/bufio/writer.odin +++ b/core/bufio/writer.odin @@ -173,14 +173,6 @@ writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) { if b.err != nil { return 0, b.err } - if writer_buffered(b) == 0 { - if w, ok := io.to_reader_from(b.wr); !ok { - n, err = io.read_from(w, r) - b.err = err - return - } - } - for { if writer_available(b) == 0 { writer_flush(b) or_return @@ -222,46 +214,35 @@ writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) { // writer_to_stream converts a Writer into an io.Stream writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) { - s.stream_data = b - s.stream_vtable = &_writer_vtable + s.data = b + s.procedure = _writer_proc return } // writer_to_stream converts a Writer into an io.Stream writer_to_writer :: proc(b: ^Writer) -> (s: io.Writer) { - s.stream_data = b - s.stream_vtable = &_writer_vtable - return + return {writer_to_stream(b)} } - @(private) -_writer_vtable := io.Stream_VTable{ - impl_destroy = proc(s: io.Stream) -> io.Error { - b := (^Writer)(s.stream_data) +_writer_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { + b := (^Writer)(stream_data) + #partial switch mode { + case .Flush: + err = writer_flush(b) + return + case .Write: + n_int: int + n_int, err = writer_write(b, p) + n = i64(n_int) + return + case .Destroy: writer_destroy(b) - return nil - }, - impl_flush = proc(s: io.Stream) -> io.Error { - b := (^Writer)(s.stream_data) - return writer_flush(b) - }, - impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { - b := (^Writer)(s.stream_data) - return writer_write(b, p) - }, - impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error { - b := (^Writer)(s.stream_data) - return writer_write_byte(b, c) - }, - impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) { - b := (^Writer)(s.stream_data) - return writer_write_rune(b, r) - }, - impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) { - b := (^Writer)(s.stream_data) - return writer_read_from(b, r) - }, + return + case .Query: + return io.query_utility({.Flush, .Write, .Destroy, .Query}) + } + return 0, .Empty } diff --git a/core/bytes/buffer.odin b/core/bytes/buffer.odin index b60a8e877..4375d8195 100644 --- a/core/bytes/buffer.odin +++ b/core/bytes/buffer.odin @@ -375,69 +375,31 @@ buffer_read_from :: proc(b: ^Buffer, r: io.Reader) -> (n: i64, err: io.Error) #n buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) { - s.stream_data = b - s.stream_vtable = &_buffer_vtable + s.data = b + s.procedure = _buffer_proc return } @(private) -_buffer_vtable := io.Stream_VTable{ - impl_size = proc(s: io.Stream) -> i64 { - b := (^Buffer)(s.stream_data) - return i64(buffer_capacity(b)) - }, - impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { - b := (^Buffer)(s.stream_data) - return buffer_read(b, p) - }, - impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { - b := (^Buffer)(s.stream_data) - return buffer_read_at(b, p, int(offset)) - }, - impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) { - b := (^Buffer)(s.stream_data) - return buffer_read_byte(b) - }, - impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) { - b := (^Buffer)(s.stream_data) - return buffer_read_rune(b) - }, - impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { - b := (^Buffer)(s.stream_data) - return buffer_write(b, p) - }, - impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { - b := (^Buffer)(s.stream_data) - return buffer_write_at(b, p, int(offset)) - }, - impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error { - b := (^Buffer)(s.stream_data) - return buffer_write_byte(b, c) - }, - impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) { - b := (^Buffer)(s.stream_data) - return buffer_write_rune(b, r) - }, - impl_unread_byte = proc(s: io.Stream) -> io.Error { - b := (^Buffer)(s.stream_data) - return buffer_unread_byte(b) - }, - impl_unread_rune = proc(s: io.Stream) -> io.Error { - b := (^Buffer)(s.stream_data) - return buffer_unread_rune(b) - }, - impl_destroy = proc(s: io.Stream) -> io.Error { - b := (^Buffer)(s.stream_data) +_buffer_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { + b := (^Buffer)(stream_data) + #partial switch mode { + case .Read: + return io._i64_err(buffer_read(b, p)) + case .Read_At: + return io._i64_err(buffer_read_at(b, p, int(offset))) + case .Write: + return io._i64_err(buffer_write(b, p)) + case .Write_At: + return io._i64_err(buffer_write_at(b, p, int(offset))) + case .Size: + n = i64(buffer_capacity(b)) + return + case .Destroy: buffer_destroy(b) - return nil - }, - impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) { - b := (^Buffer)(s.stream_data) - return buffer_write_to(b, w) - }, - impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) { - b := (^Buffer)(s.stream_data) - return buffer_read_from(b, r) - }, + return + case .Query: + return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Size, .Destroy}) + } + return 0, .Empty } - diff --git a/core/bytes/reader.odin b/core/bytes/reader.odin index 7c37f3061..4b18345ba 100644 --- a/core/bytes/reader.odin +++ b/core/bytes/reader.odin @@ -16,8 +16,8 @@ reader_init :: proc(r: ^Reader, s: []byte) { } reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) { - s.stream_data = r - s.stream_vtable = &_reader_vtable + s.data = r + s.procedure = _reader_proc return } @@ -137,41 +137,22 @@ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) { @(private) -_reader_vtable := io.Stream_VTable{ - impl_size = proc(s: io.Stream) -> i64 { - r := (^Reader)(s.stream_data) - return reader_size(r) - }, - impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { - r := (^Reader)(s.stream_data) - return reader_read(r, p) - }, - impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) { - r := (^Reader)(s.stream_data) - return reader_read_at(r, p, off) - }, - impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) { - r := (^Reader)(s.stream_data) - return reader_read_byte(r) - }, - impl_unread_byte = proc(s: io.Stream) -> io.Error { - r := (^Reader)(s.stream_data) - return reader_unread_byte(r) - }, - impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) { - r := (^Reader)(s.stream_data) - return reader_read_rune(r) - }, - impl_unread_rune = proc(s: io.Stream) -> io.Error { - r := (^Reader)(s.stream_data) - return reader_unread_rune(r) - }, - impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { - r := (^Reader)(s.stream_data) - return reader_seek(r, offset, whence) - }, - impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) { - r := (^Reader)(s.stream_data) - return reader_write_to(r, w) - }, +_reader_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { + r := (^Reader)(stream_data) + #partial switch mode { + case .Read: + return io._i64_err(reader_read(r, p)) + case .Read_At: + return io._i64_err(reader_read_at(r, p, offset)) + case .Seek: + n, err = reader_seek(r, offset, whence) + return + case .Size: + n = reader_size(r) + return + case .Query: + return io.query_utility({.Read, .Read_At, .Seek, .Size, .Query}) + } + return 0, .Empty } + diff --git a/core/compress/common.odin b/core/compress/common.odin index 6bcc46bf2..2d00ed359 100644 --- a/core/compress/common.odin +++ b/core/compress/common.odin @@ -188,7 +188,8 @@ input_size_from_memory :: proc(z: ^Context_Memory_Input) -> (res: i64, err: Erro } input_size_from_stream :: proc(z: ^Context_Stream_Input) -> (res: i64, err: Error) { - return io.size(z.input), nil + res, _ = io.size(z.input) + return } input_size :: proc{input_size_from_memory, input_size_from_stream} @@ -215,7 +216,7 @@ read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int // TODO: REMOVE ALL USE OF context.temp_allocator here // the is literally no need for it b := make([]u8, size, context.temp_allocator) - _, e := z.input->impl_read(b[:]) + _, e := io.read({z.input}, b[:]) if e == .None { return b, .None } diff --git a/core/crypto/blake/blake.odin b/core/crypto/blake/blake.odin index 5fc0a02b9..67f3c0d35 100644 --- a/core/crypto/blake/blake.odin +++ b/core/crypto/blake/blake.odin @@ -70,7 +70,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -149,7 +149,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -228,7 +228,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -307,7 +307,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/blake2b/blake2b.odin b/core/crypto/blake2b/blake2b.odin index e75d74197..82dabc542 100644 --- a/core/crypto/blake2b/blake2b.odin +++ b/core/crypto/blake2b/blake2b.odin @@ -77,7 +77,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { _blake2.update(&ctx, buf[:read]) } diff --git a/core/crypto/blake2s/blake2s.odin b/core/crypto/blake2s/blake2s.odin index 831335081..a767d4d70 100644 --- a/core/crypto/blake2s/blake2s.odin +++ b/core/crypto/blake2s/blake2s.odin @@ -77,7 +77,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { _blake2.update(&ctx, buf[:read]) } diff --git a/core/crypto/gost/gost.odin b/core/crypto/gost/gost.odin index 1d0274fae..fa2df862f 100644 --- a/core/crypto/gost/gost.odin +++ b/core/crypto/gost/gost.odin @@ -65,7 +65,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/groestl/groestl.odin b/core/crypto/groestl/groestl.odin index 8e5a2440d..604a3eb9b 100644 --- a/core/crypto/groestl/groestl.odin +++ b/core/crypto/groestl/groestl.odin @@ -70,7 +70,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -149,7 +149,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -228,7 +228,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -307,7 +307,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/haval/haval.odin b/core/crypto/haval/haval.odin index 811ecf95d..76cfbecbe 100644 --- a/core/crypto/haval/haval.odin +++ b/core/crypto/haval/haval.odin @@ -79,7 +79,7 @@ hash_stream_128_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -164,7 +164,7 @@ hash_stream_128_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -249,7 +249,7 @@ hash_stream_128_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -334,7 +334,7 @@ hash_stream_160_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -419,7 +419,7 @@ hash_stream_160_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -504,7 +504,7 @@ hash_stream_160_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -589,7 +589,7 @@ hash_stream_192_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -674,7 +674,7 @@ hash_stream_192_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -759,7 +759,7 @@ hash_stream_192_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -844,7 +844,7 @@ hash_stream_224_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -929,7 +929,7 @@ hash_stream_224_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -1014,7 +1014,7 @@ hash_stream_224_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -1099,7 +1099,7 @@ hash_stream_256_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -1184,7 +1184,7 @@ hash_stream_256_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -1270,7 +1270,7 @@ hash_stream_256_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) diff --git a/core/crypto/jh/jh.odin b/core/crypto/jh/jh.odin index 42c2d1d34..6b4a73b46 100644 --- a/core/crypto/jh/jh.odin +++ b/core/crypto/jh/jh.odin @@ -70,7 +70,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -149,7 +149,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -228,7 +228,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -307,7 +307,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/keccak/keccak.odin b/core/crypto/keccak/keccak.odin index aeb5aac52..c09c9ddbe 100644 --- a/core/crypto/keccak/keccak.odin +++ b/core/crypto/keccak/keccak.odin @@ -77,7 +77,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } @@ -159,7 +159,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } @@ -241,7 +241,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } @@ -323,7 +323,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } diff --git a/core/crypto/md2/md2.odin b/core/crypto/md2/md2.odin index 711e6e9f6..693e2c6b0 100644 --- a/core/crypto/md2/md2.odin +++ b/core/crypto/md2/md2.odin @@ -64,7 +64,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/md4/md4.odin b/core/crypto/md4/md4.odin index b2651225b..1f161e1cb 100644 --- a/core/crypto/md4/md4.odin +++ b/core/crypto/md4/md4.odin @@ -68,7 +68,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/md5/md5.odin b/core/crypto/md5/md5.odin index 30a556102..db04e67df 100644 --- a/core/crypto/md5/md5.odin +++ b/core/crypto/md5/md5.odin @@ -67,7 +67,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/ripemd/ripemd.odin b/core/crypto/ripemd/ripemd.odin index 702d29037..257f4144e 100644 --- a/core/crypto/ripemd/ripemd.odin +++ b/core/crypto/ripemd/ripemd.odin @@ -69,7 +69,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -145,7 +145,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -221,7 +221,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -297,7 +297,7 @@ hash_stream_320 :: proc(s: io.Stream) -> ([DIGEST_SIZE_320]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/sha1/sha1.odin b/core/crypto/sha1/sha1.odin index b0dbd7dc8..2e1991444 100644 --- a/core/crypto/sha1/sha1.odin +++ b/core/crypto/sha1/sha1.odin @@ -67,7 +67,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/sha2/sha2.odin b/core/crypto/sha2/sha2.odin index 9792a4cb8..cbd3a6b39 100644 --- a/core/crypto/sha2/sha2.odin +++ b/core/crypto/sha2/sha2.odin @@ -74,7 +74,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -153,7 +153,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -232,7 +232,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -311,7 +311,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/sha3/sha3.odin b/core/crypto/sha3/sha3.odin index 1202f8b23..ec6fbc971 100644 --- a/core/crypto/sha3/sha3.odin +++ b/core/crypto/sha3/sha3.odin @@ -73,7 +73,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } @@ -152,7 +152,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } @@ -231,7 +231,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } @@ -310,7 +310,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } diff --git a/core/crypto/shake/shake.odin b/core/crypto/shake/shake.odin index 525dcfbd3..50ef756aa 100644 --- a/core/crypto/shake/shake.odin +++ b/core/crypto/shake/shake.odin @@ -73,7 +73,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } @@ -155,7 +155,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } diff --git a/core/crypto/sm3/sm3.odin b/core/crypto/sm3/sm3.odin index 74c9f22e2..b35e20e37 100644 --- a/core/crypto/sm3/sm3.odin +++ b/core/crypto/sm3/sm3.odin @@ -66,7 +66,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/streebog/streebog.odin b/core/crypto/streebog/streebog.odin index f85977cba..aa4dd7906 100644 --- a/core/crypto/streebog/streebog.odin +++ b/core/crypto/streebog/streebog.odin @@ -70,7 +70,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -146,7 +146,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/tiger/tiger.odin b/core/crypto/tiger/tiger.odin index cf6159fad..a4982d7c4 100644 --- a/core/crypto/tiger/tiger.odin +++ b/core/crypto/tiger/tiger.odin @@ -71,7 +71,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { _tiger.update(&ctx, buf[:read]) } @@ -150,7 +150,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { _tiger.update(&ctx, buf[:read]) } @@ -229,7 +229,7 @@ hash_stream_192 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { _tiger.update(&ctx, buf[:read]) } diff --git a/core/crypto/tiger2/tiger2.odin b/core/crypto/tiger2/tiger2.odin index e8f2c4edb..432387bdf 100644 --- a/core/crypto/tiger2/tiger2.odin +++ b/core/crypto/tiger2/tiger2.odin @@ -71,7 +71,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { _tiger.update(&ctx, buf[:read]) } @@ -150,7 +150,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { _tiger.update(&ctx, buf[:read]) } @@ -229,7 +229,7 @@ hash_stream_192 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { _tiger.update(&ctx, buf[:read]) } diff --git a/core/crypto/whirlpool/whirlpool.odin b/core/crypto/whirlpool/whirlpool.odin index 0cfef7c6b..8ae6f413e 100644 --- a/core/crypto/whirlpool/whirlpool.odin +++ b/core/crypto/whirlpool/whirlpool.odin @@ -66,7 +66,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = s->impl_read(buf) + read, _ = io.read({s}, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/io/conv.odin b/core/io/conv.odin index 39a72d69d..2b1351e18 100644 --- a/core/io/conv.odin +++ b/core/io/conv.odin @@ -2,123 +2,79 @@ package io to_reader :: proc(s: Stream) -> (r: Reader, ok: bool = true) #optional_ok { r.stream = s - if s.stream_vtable == nil || s.impl_read == nil { - ok = false - } + ok = .Read in query(s) return } to_writer :: proc(s: Stream) -> (w: Writer, ok: bool = true) #optional_ok { w.stream = s - if s.stream_vtable == nil || s.impl_write == nil { - ok = false - } + ok = .Write in query(s) return } to_closer :: proc(s: Stream) -> (c: Closer, ok: bool = true) #optional_ok { c.stream = s - if s.stream_vtable == nil || s.impl_close == nil { - ok = false - } + ok = .Close in query(s) return } to_flusher :: proc(s: Stream) -> (f: Flusher, ok: bool = true) #optional_ok { f.stream = s - if s.stream_vtable == nil || s.impl_flush == nil { - ok = false - } + ok = .Flush in query(s) return } to_seeker :: proc(s: Stream) -> (seeker: Seeker, ok: bool = true) #optional_ok { seeker.stream = s - if s.stream_vtable == nil || s.impl_seek == nil { - ok = false - } + ok = .Seek in query(s) return } to_read_writer :: proc(s: Stream) -> (r: Read_Writer, ok: bool = true) #optional_ok { r.stream = s - if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil { - ok = false - } + ok = query(s) >= {.Read, .Write} return } to_read_closer :: proc(s: Stream) -> (r: Read_Closer, ok: bool = true) #optional_ok { r.stream = s - if s.stream_vtable == nil || s.impl_read == nil || s.impl_close == nil { - ok = false - } + ok = query(s) >= {.Read, .Close} return } to_read_write_closer :: proc(s: Stream) -> (r: Read_Write_Closer, ok: bool = true) #optional_ok { r.stream = s - if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_close == nil { - ok = false - } + ok = query(s) >= {.Read, .Write, .Close} return } to_read_write_seeker :: proc(s: Stream) -> (r: Read_Write_Seeker, ok: bool = true) #optional_ok { r.stream = s - if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_seek == nil { - ok = false - } + ok = query(s) >= {.Read, .Write, .Seek} return } to_write_flusher :: proc(s: Stream) -> (w: Write_Flusher, ok: bool = true) #optional_ok { w.stream = s - if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil { - ok = false - } + ok = query(s) >= {.Write, .Flush} return } to_write_flush_closer :: proc(s: Stream) -> (w: Write_Flush_Closer, ok: bool = true) #optional_ok { w.stream = s - if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil || s.impl_close == nil { - ok = false - } + ok = query(s) >= {.Write, .Flush, .Close} return } to_reader_at :: proc(s: Stream) -> (r: Reader_At, ok: bool = true) #optional_ok { r.stream = s - if s.stream_vtable == nil || s.impl_read_at == nil { - ok = false - } + ok = query(s) >= {.Read_At} return } to_writer_at :: proc(s: Stream) -> (w: Writer_At, ok: bool = true) #optional_ok { w.stream = s - if s.stream_vtable == nil || s.impl_write_at == nil { - ok = false - } - return -} -to_reader_from :: proc(s: Stream) -> (r: Reader_From, ok: bool = true) #optional_ok { - r.stream = s - if s.stream_vtable == nil || s.impl_read_from == nil { - ok = false - } - return -} -to_writer_to :: proc(s: Stream) -> (w: Writer_To, ok: bool = true) #optional_ok { - w.stream = s - if s.stream_vtable == nil || s.impl_write_to == nil { - ok = false - } + ok = query(s) >= {.Write_At} return } to_write_closer :: proc(s: Stream) -> (w: Write_Closer, ok: bool = true) #optional_ok { w.stream = s - if s.stream_vtable == nil || s.impl_write == nil || s.impl_close == nil { - ok = false - } + ok = query(s) >= {.Write, .Close} return } to_write_seeker :: proc(s: Stream) -> (w: Write_Seeker, ok: bool = true) #optional_ok { w.stream = s - if s.stream_vtable == nil || s.impl_write == nil || s.impl_seek == nil { - ok = false - } + ok = query(s) >= {.Write, .Seek} return } diff --git a/core/io/io.odin b/core/io/io.odin index 7faa500b1..868e16204 100644 --- a/core/io/io.odin +++ b/core/io/io.odin @@ -53,54 +53,27 @@ Error :: enum i32 { Empty = -1, } -Close_Proc :: proc(using s: Stream) -> Error -Flush_Proc :: proc(using s: Stream) -> Error -Seek_Proc :: proc(using s: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error) -Size_Proc :: proc(using s: Stream) -> i64 -Read_Proc :: proc(using s: Stream, p: []byte) -> (n: int, err: Error) -Read_At_Proc :: proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error) -Read_From_Proc :: proc(using s: Stream, r: Reader) -> (n: i64, err: Error) -Read_Byte_Proc :: proc(using s: Stream) -> (byte, Error) -Read_Rune_Proc :: proc(using s: Stream) -> (ch: rune, size: int, err: Error) -Unread_Byte_Proc :: proc(using s: Stream) -> Error -Unread_Rune_Proc :: proc(using s: Stream) -> Error -Write_Proc :: proc(using s: Stream, p: []byte) -> (n: int, err: Error) -Write_At_Proc :: proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error) -Write_To_Proc :: proc(using s: Stream, w: Writer) -> (n: i64, err: Error) -Write_Byte_Proc :: proc(using s: Stream, c: byte) -> Error -Write_Rune_Proc :: proc(using s: Stream, r: rune) -> (size: int, err: Error) -Destroy_Proc :: proc(using s: Stream) -> Error +Stream_Mode :: enum { + Close, + Flush, + Read, + Read_At, + Write, + Write_At, + Seek, + Size, + Destroy, + Query, // query what modes are available +} +Stream_Mode_Set :: distinct bit_set[Stream_Mode; i64] + +Stream_Proc :: #type proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) Stream :: struct { - using stream_vtable: ^Stream_VTable, - stream_data: rawptr, + procedure: Stream_Proc, + data: rawptr, } -Stream_VTable :: struct { - impl_close: Close_Proc, - impl_flush: Flush_Proc, - - impl_seek: Seek_Proc, - impl_size: Size_Proc, - - impl_read: Read_Proc, - impl_read_at: Read_At_Proc, - impl_read_byte: Read_Byte_Proc, - impl_read_rune: Read_Rune_Proc, - impl_write_to: Write_To_Proc, - - impl_write: Write_Proc, - impl_write_at: Write_At_Proc, - impl_write_byte: Write_Byte_Proc, - impl_write_rune: Write_Rune_Proc, - impl_read_from: Read_From_Proc, - - impl_unread_byte: Unread_Byte_Proc, - impl_unread_rune: Unread_Rune_Proc, - - impl_destroy: Destroy_Proc, -} - Reader :: struct {using stream: Stream} Writer :: struct {using stream: Stream} @@ -120,70 +93,66 @@ Write_Flush_Closer :: struct {using stream: Stream} Reader_At :: struct {using stream: Stream} Writer_At :: struct {using stream: Stream} -Reader_From :: struct {using stream: Stream} -Writer_To :: struct {using stream: Stream} -destroy :: proc(s: Stream) -> Error { - close_err := close({s}) - if s.stream_vtable != nil && s.impl_destroy != nil { - return s->impl_destroy() +destroy :: proc(s: Stream) -> (err: Error) { + _ = flush({s}) + _ = close({s}) + if s.procedure != nil { + _, err = s.procedure(s.data, .Destroy, nil, 0, nil) + } else { + err = .Empty } - if close_err != .None { - return close_err - } - return .Empty + return } +query :: proc(s: Stream) -> (set: Stream_Mode_Set) { + if s.procedure != nil { + n, _ := s.procedure(s.data, .Query, nil, 0, nil) + set = transmute(Stream_Mode_Set)n + if set != nil { + set += {.Query} + } + } + return +} + +query_utility :: #force_inline proc "contextless" (set: Stream_Mode_Set) -> (n: i64, err: Error) { + return transmute(i64)set, nil +} + +_i64_err :: #force_inline proc "contextless" (n: int, err: Error) -> (i64, Error) { + return i64(n), err +} + + // read reads up to len(p) bytes into s. It returns the number of bytes read and any error if occurred. // // When read encounters an .EOF or error after successfully reading n > 0 bytes, it returns the number of // bytes read along with the error. read :: proc(s: Reader, p: []byte, n_read: ^int = nil) -> (n: int, err: Error) { - if s.stream_vtable != nil { - if s.impl_read != nil { - n, err = s->impl_read(p) - if n_read != nil { - n_read^ += n - } - return - } else if s.impl_read_byte != nil { - bytes_read := 0 - defer if n_read != nil { - n_read^ += bytes_read - } - for _, i in p { - p[i] = s->impl_read_byte() or_return - bytes_read += 1 - } - return - } + if s.procedure != nil { + n64: i64 + n64, err = s.procedure(s.data, .Read, p, 0, nil) + n = int(n64) + if n_read != nil { n_read^ += n } + } else { + err = .Empty } - return 0, .Empty + return } // write writes up to len(p) bytes into s. It returns the number of bytes written and any error if occurred. write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Error) { - if s.stream_vtable != nil { - if s.impl_write != nil { - n, err = s->impl_write(p) - if n_written != nil { - n_written^ += n - } - return - } else if s.impl_write_byte != nil { - bytes_written := 0 - defer if n_written != nil { - n_written^ += bytes_written - } - for c in p { - s->impl_write_byte(c) or_return - bytes_written += 1 - } - return - } + if s.procedure != nil { + n64: i64 + n64, err = s.procedure(s.data, .Write, p, 0, nil) + n = int(n64) + if n_written != nil { n_written^ += n } + } else { + err = .Empty } - return 0, .Empty + return } // seek sets the offset of the next read or write to offset. @@ -194,57 +163,46 @@ write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Erro // // seek returns the new offset to the start of the file/stream, and any error if occurred. seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error) { - if s.stream_vtable != nil && s.impl_seek != nil { - return s->impl_seek(offset, whence) + if s.procedure != nil { + n, err = s.procedure(s.data, .Seek, nil, offset, whence) + } else { + err = .Empty } - return 0, .Empty + return } // The behaviour of close after the first call is stream implementation defined. // Different streams may document their own behaviour. -close :: proc(s: Closer) -> Error { - if s.stream_vtable != nil && s.impl_close != nil { - return s->impl_close() +close :: proc(s: Closer) -> (err: Error) { + if s.procedure != nil { + _, err = s.procedure(s.data, .Close, nil, 0, nil) } - // Instead of .Empty, .None is fine in this case - return .None + return } -flush :: proc(s: Flusher) -> Error { - if s.stream_vtable != nil && s.impl_flush != nil { - return s->impl_flush() +flush :: proc(s: Flusher) -> (err: Error) { + if s.procedure != nil { + _, err = s.procedure(s.data, .Flush, nil, 0, nil) } - // Instead of .Empty, .None is fine in this case - return .None + return } // size returns the size of the stream. If the stream does not support querying its size, 0 will be returned. -size :: proc(s: Stream) -> i64 { - if s.stream_vtable == nil { - return 0 +size :: proc(s: Stream) -> (n: i64, err: Error) { + if s.procedure != nil { + n, err = s.procedure(s.data, .Size, nil, 0, nil) + if err == .Empty { + seeker := Seeker{s} + n = 0 + curr := seek(seeker, 0, .Current) or_return + end := seek(seeker, 0, .End) or_return + seek(seeker, curr, .Start) or_return + n = end + } + } else { + err = .Empty } - if s.impl_size != nil { - return s->impl_size() - } - if s.impl_seek == nil { - return 0 - } - - curr, end: i64 - err: Error - if curr, err = s->impl_seek(0, .Current); err != nil { - return 0 - } - - if end, err = s->impl_seek(0, .End); err != nil { - return 0 - } - - if _, err = s->impl_seek(curr, .Start); err != nil { - return 0 - } - - return end + return } @@ -256,29 +214,26 @@ size :: proc(s: Stream) -> i64 { // // If n == len(p), err may be either nil or .EOF read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n: int, err: Error) { - defer if n_read != nil { - n_read^ += n - } - - if r.stream_vtable == nil { - return 0, .Empty - } - if r.impl_read_at != nil { - return r->impl_read_at(p, offset) - } - if r.impl_seek == nil || r.impl_read == nil { - return 0, .Empty - } - - curr_offset := r->impl_seek(offset, .Current) or_return - - n, err = r->impl_read(p) - _, err1 := r->impl_seek(curr_offset, .Start) - if err1 != nil && err == nil { - err = err1 + if r.procedure != nil { + n64: i64 + n64, err = r.procedure(r.data, .Read_At, p, offset, nil) + if err != .Empty { + n = int(n64) + } else { + seeker := Seeker{r} + reader := Reader{r} + curr := seek(seeker, offset, .Current) or_return + n, err = read(reader, p) + _, err1 := seek(seeker, curr, .Start) + if err1 != nil && err == nil { + err = err1 + } + } + if n_read != nil { n_read^ += n } + } else { + err = .Empty } return - } // write_at writes len(p) bytes into p starting with the provided offset in the underlying Writer_At stream w. @@ -287,97 +242,41 @@ read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n: // If write_at is writing to a Writer_At which has a seek offset, then write_at should not affect the underlying // seek offset. write_at :: proc(w: Writer_At, p: []byte, offset: i64, n_written: ^int = nil) -> (n: int, err: Error) { - defer if n_written != nil { - n_written^ += n - } - - if w.stream_vtable == nil { - return 0, .Empty - } - if w.impl_write_at != nil { - return w->impl_write_at(p, offset) - } - if w.impl_seek == nil || w.impl_write == nil { - return 0, .Empty - } - - curr_offset: i64 - curr_offset, err = w->impl_seek(offset, .Current) - if err != nil { - return 0, err - } - - n, err = w->impl_write(p) - _, err1 := w->impl_seek(curr_offset, .Start) - if err1 != nil && err == nil { - err = err1 + if w.procedure != nil { + n64: i64 + n64, err = w.procedure(w.data, .Write_At, p, offset, nil) + if err != .Empty { + n = int(n64) + } else { + seeker := Seeker{w} + writer := Writer{w} + curr := seek(seeker, offset, .Current) or_return + n, err = write(writer, p) + _, err1 := seek(seeker, curr, .Start) + if err1 != nil && err == nil { + err = err1 + } + } + if n_written != nil { n_written^ += n } + } else { + err = .Empty } return } -write_to :: proc(r: Writer_To, w: Writer) -> (n: i64, err: Error) { - if r.stream_vtable == nil || w.stream_vtable == nil { - return 0, .Empty - } - if r.impl_write_to != nil { - return r->impl_write_to(w) - } - return 0, .Empty -} -read_from :: proc(w: Reader_From, r: Reader) -> (n: i64, err: Error) { - if r.stream_vtable == nil || w.stream_vtable == nil { - return 0, .Empty - } - if r.impl_read_from != nil { - return w->impl_read_from(r) - } - return 0, .Empty -} - - // read_byte reads and returns the next byte from r. read_byte :: proc(r: Reader, n_read: ^int = nil) -> (b: byte, err: Error) { - defer if err == nil && n_read != nil { - n_read^ += 1 - } - - if r.stream_vtable == nil { - return 0, .Empty - } - if r.impl_read_byte != nil { - return r->impl_read_byte() - } - if r.impl_read == nil { - return 0, .Empty - } - buf: [1]byte - _, err = r->impl_read(buf[:]) - return buf[0], err + _, err = read(r, buf[:], n_read) + b = buf[0] + return } write_byte :: proc(w: Writer, c: byte, n_written: ^int = nil) -> Error { - return _write_byte(auto_cast w, c, n_written) -} - -@(private) -_write_byte :: proc(w: Writer, c: byte, n_written: ^int = nil) -> (err: Error) { - defer if err == nil && n_written != nil { - n_written^ += 1 - } - if w.stream_vtable == nil { - return .Empty - } - if w.impl_write_byte != nil { - return w->impl_write_byte(c) - } - if w.impl_write == nil { - return .Empty - } - - b := [1]byte{c} - _, err = w->impl_write(b[:]) - return err + buf: [1]byte + buf[0] = c + write(w, buf[:], n_written) or_return + return nil } // read_rune reads a single UTF-8 encoded Unicode codepoint and returns the rune and its size in bytes. @@ -385,19 +284,9 @@ read_rune :: proc(br: Reader, n_read: ^int = nil) -> (ch: rune, size: int, err: defer if err == nil && n_read != nil { n_read^ += size } - if br.stream_vtable == nil { - return 0, 0, .Empty - } - if br.impl_read_rune != nil { - return br->impl_read_rune() - } - if br.impl_read == nil { - return 0, 0, .Empty - } b: [utf8.UTF_MAX]byte - _, err = br->impl_read(b[:1]) - + _, err = read(br, b[:1]) s0 := b[0] ch = rune(s0) @@ -415,7 +304,7 @@ read_rune :: proc(br: Reader, n_read: ^int = nil) -> (ch: rune, size: int, err: return } sz := int(x&7) - size, err = br->impl_read(b[1:sz]) + size, err = read(br, b[1:sz]) if err != nil || size+1 < sz { ch = utf8.RUNE_ERROR return @@ -425,28 +314,6 @@ read_rune :: proc(br: Reader, n_read: ^int = nil) -> (ch: rune, size: int, err: return } -unread_byte :: proc(s: Stream) -> Error { - if s.stream_vtable == nil { - return .Empty - } - if s.impl_unread_byte != nil { - return s->impl_unread_byte() - } - if s.impl_seek != nil { - _, err := s->impl_seek(-1, .Current) - return err - } - - return .Empty -} -unread_rune :: proc(s: Writer) -> Error { - if s.stream_vtable != nil && s.impl_unread_rune != nil { - return s->impl_unread_rune() - } - return .Empty -} - - // write_string writes the contents of the string s to w. write_string :: proc(s: Writer, str: string, n_written: ^int = nil) -> (n: int, err: Error) { return write(s, transmute([]byte)str, n_written) @@ -457,14 +324,6 @@ write_rune :: proc(s: Writer, r: rune, n_written: ^int = nil) -> (size: int, err defer if err == nil && n_written != nil { n_written^ += size } - - if s.stream_vtable == nil { - return 0, .Empty - } - if s.impl_write_rune != nil { - return s->impl_write_rune(r) - } - if r < utf8.RUNE_SELF { err = write_byte(s, byte(r)) if err == nil { @@ -542,21 +401,15 @@ copy_n :: proc(dst: Writer, src: Reader, n: i64) -> (written: i64, err: Error) { @(private) _copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err: Error) { - if dst.stream_vtable == nil || src.stream_vtable == nil { + if dst.procedure == nil || src.procedure == nil { return 0, .Empty } - if src.impl_write_to != nil { - return src->impl_write_to(dst) - } - if src.impl_read_from != nil { - return dst->impl_read_from(src) - } buf := buf if buf == nil { DEFAULT_SIZE :: 4 * 1024 size := DEFAULT_SIZE - if src.stream_vtable == _limited_reader_vtable { - l := (^Limited_Reader)(src.stream_data) + if src.procedure == _limited_reader_proc { + l := (^Limited_Reader)(src.data) if i64(size) > l.n { if l.n < 1 { size = 1 diff --git a/core/io/multi.odin b/core/io/multi.odin index 64c533e37..e85114a7a 100644 --- a/core/io/multi.odin +++ b/core/io/multi.odin @@ -5,33 +5,37 @@ Multi_Reader :: struct { } @(private) -_multi_reader_vtable := &Stream_VTable{ - impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) { - mr := (^Multi_Reader)(s.stream_data) - for len(mr.readers) > 0 { - r := mr.readers[0] - n, err = read(r, p) - if err == .EOF { - ordered_remove(&mr.readers, 0) - } - if n > 0 || err != .EOF { - if err == .EOF && len(mr.readers) > 0 { - // Don't return EOF yet, more readers remain - err = nil - } - return - } +_multi_reader_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) { + if mode == .Query { + return query_utility({.Read, .Query}) + } else if mode != .Read { + return 0, .Empty + } + mr := (^Multi_Reader)(stream_data) + for len(mr.readers) > 0 { + r := mr.readers[0] + n, err = _i64_err(read(r, p)) + if err == .EOF { + ordered_remove(&mr.readers, 0) } - return 0, .EOF - }, + if n > 0 || err != .EOF { + if err == .EOF && len(mr.readers) > 0 { + // Don't return EOF yet, more readers remain + err = nil + } + return + } + } + return 0, .EOF } + multi_reader_init :: proc(mr: ^Multi_Reader, readers: ..Reader, allocator := context.allocator) -> (r: Reader) { all_readers := make([dynamic]Reader, 0, len(readers), allocator) for w in readers { - if w.stream_vtable == _multi_reader_vtable { - other := (^Multi_Reader)(w.stream_data) + if w.procedure == _multi_reader_proc { + other := (^Multi_Reader)(w.data) append(&all_readers, ..other.readers[:]) } else { append(&all_readers, w) @@ -40,8 +44,8 @@ multi_reader_init :: proc(mr: ^Multi_Reader, readers: ..Reader, allocator := con mr.readers = all_readers - r.stream_vtable = _multi_reader_vtable - r.stream_data = mr + r.procedure = _multi_reader_proc + r.data = mr return } @@ -55,38 +59,42 @@ Multi_Writer :: struct { } @(private) -_multi_writer_vtable := &Stream_VTable{ - impl_write = proc(s: Stream, p: []byte) -> (n: int, err: Error) { - mw := (^Multi_Writer)(s.stream_data) - for w in mw.writers { - n, err = write(w, p) - if err != nil { - return - } - if n != len(p) { - err = .Short_Write - return - } +_multi_writer_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) { + if mode == .Query { + return query_utility({.Write, .Query}) + } else if mode != .Write { + return 0, .Empty + } + mw := (^Multi_Writer)(stream_data) + for w in mw.writers { + n, err = _i64_err(write(w, p)) + if err != nil { + return } + if n != i64(len(p)) { + err = .Short_Write + return + } + } - return len(p), nil - }, + return i64(len(p)), nil } + multi_writer_init :: proc(mw: ^Multi_Writer, writers: ..Writer, allocator := context.allocator) -> (out: Writer) { mw.writers = make([dynamic]Writer, 0, len(writers), allocator) for w in writers { - if w.stream_vtable == _multi_writer_vtable { - other := (^Multi_Writer)(w.stream_data) + if w.procedure == _multi_writer_proc { + other := (^Multi_Writer)(w.data) append(&mw.writers, ..other.writers[:]) } else { append(&mw.writers, w) } } - out.stream_vtable = _multi_writer_vtable - out.stream_data = mw + out.procedure = _multi_writer_proc + out.data = mw return } diff --git a/core/io/util.odin b/core/io/util.odin index cfd7d3608..c77d0be9d 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -292,17 +292,21 @@ Tee_Reader :: struct { } @(private) -_tee_reader_vtable := &Stream_VTable{ - impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) { - t := (^Tee_Reader)(s.stream_data) - n, err = read(t.r, p) +_tee_reader_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) { + t := (^Tee_Reader)(stream_data) + #partial switch mode { + case .Read: + n, err = _i64_err(read(t.r, p)) if n > 0 { if wn, werr := write(t.w, p[:n]); werr != nil { - return wn, werr + return i64(wn), werr } } return - }, + case .Query: + return query_utility({.Read, .Query}) + } + return 0, .Empty } // tee_reader_init returns a Reader that writes to 'w' what it reads from 'r' @@ -317,8 +321,8 @@ tee_reader_init :: proc(t: ^Tee_Reader, r: Reader, w: Writer, allocator := conte } tee_reader_to_reader :: proc(t: ^Tee_Reader) -> (r: Reader) { - r.stream_data = t - r.stream_vtable = _tee_reader_vtable + r.data = t + r.procedure = _tee_reader_proc return } @@ -332,9 +336,10 @@ Limited_Reader :: struct { } @(private) -_limited_reader_vtable := &Stream_VTable{ - impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) { - l := (^Limited_Reader)(s.stream_data) +_limited_reader_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) { + l := (^Limited_Reader)(stream_data) + #partial switch mode { + case .Read: if l.n <= 0 { return 0, .EOF } @@ -342,10 +347,13 @@ _limited_reader_vtable := &Stream_VTable{ if i64(len(p)) > l.n { p = p[0:l.n] } - n, err = read(l.r, p) + n, err = _i64_err(read(l.r, p)) l.n -= i64(n) return - }, + case .Query: + return query_utility({.Read, .Query}) + } + return 0, .Empty } limited_reader_init :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader { @@ -355,8 +363,8 @@ limited_reader_init :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader { } limited_reader_to_reader :: proc(l: ^Limited_Reader) -> (r: Reader) { - r.stream_vtable = _limited_reader_vtable - r.stream_data = l + r.procedure = _limited_reader_proc + r.data = l return } @@ -375,15 +383,16 @@ section_reader_init :: proc(s: ^Section_Reader, r: Reader_At, off: i64, n: i64) return } section_reader_to_stream :: proc(s: ^Section_Reader) -> (out: Stream) { - out.stream_data = s - out.stream_vtable = _section_reader_vtable + out.data = s + out.procedure = _section_reader_proc return } @(private) -_section_reader_vtable := &Stream_VTable{ - impl_read = proc(stream: Stream, p: []byte) -> (n: int, err: Error) { - s := (^Section_Reader)(stream.stream_data) +_section_reader_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) { + s := (^Section_Reader)(stream_data) + #partial switch mode { + case .Read: if s.off >= s.limit { return 0, .EOF } @@ -391,13 +400,11 @@ _section_reader_vtable := &Stream_VTable{ if max := s.limit - s.off; i64(len(p)) > max { p = p[0:max] } - n, err = read_at(s.r, p, s.off) + n, err = _i64_err(read_at(s.r, p, s.off)) s.off += i64(n) return - }, - impl_read_at = proc(stream: Stream, p: []byte, off: i64) -> (n: int, err: Error) { - s := (^Section_Reader)(stream.stream_data) - p, off := p, off + case .Read_At: + p, off := p, offset if off < 0 || off >= s.limit - s.base { return 0, .EOF @@ -405,17 +412,15 @@ _section_reader_vtable := &Stream_VTable{ off += s.base if max := s.limit - off; i64(len(p)) > max { p = p[0:max] - n, err = read_at(s.r, p, off) + n, err = _i64_err(read_at(s.r, p, off)) if err == nil { err = .EOF } return } - return read_at(s.r, p, off) - }, - impl_seek = proc(stream: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error) { - s := (^Section_Reader)(stream.stream_data) + return _i64_err(read_at(s.r, p, off)) + case .Seek: offset := offset switch whence { case: @@ -433,10 +438,12 @@ _section_reader_vtable := &Stream_VTable{ s.off = offset n = offset - s.base return - }, - impl_size = proc(stream: Stream) -> i64 { - s := (^Section_Reader)(stream.stream_data) - return s.limit - s.base - }, -} + case .Size: + n = s.limit - s.base + return + case .Query: + return query_utility({.Read, .Read_At, .Seek, .Size, .Query}) + } + return 0, nil +} diff --git a/core/os/stream.odin b/core/os/stream.odin index 9506ed3a3..946854cc6 100644 --- a/core/os/stream.odin +++ b/core/os/stream.odin @@ -4,66 +4,52 @@ import "core:io" stream_from_handle :: proc(fd: Handle) -> io.Stream { s: io.Stream - s.stream_data = rawptr(uintptr(fd)) - s.stream_vtable = &_file_stream_vtable + s.data = rawptr(uintptr(fd)) + s.procedure = _file_stream_proc return s } @(private) -_file_stream_vtable := io.Stream_VTable{ - impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { - fd := Handle(uintptr(s.stream_data)) - os_err: Errno - n, os_err = read(fd, p) - return - }, - impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { - when ODIN_OS == .Windows || ODIN_OS == .WASI { - fd := Handle(uintptr(s.stream_data)) - os_err: Errno - n, os_err = read_at(fd, p, offset) - } - return - }, - impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { - fd := Handle(uintptr(s.stream_data)) - os_err: Errno - n, os_err = write(fd, p) - return - }, - impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { - when ODIN_OS == .Windows || ODIN_OS == .WASI { - fd := Handle(uintptr(s.stream_data)) - os_err: Errno - n, os_err = write_at(fd, p, offset) - _ = os_err - } - return - }, - impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { - fd := Handle(uintptr(s.stream_data)) - n, os_err := seek(fd, offset, int(whence)) - _ = os_err - return n, nil - }, - impl_size = proc(s: io.Stream) -> i64 { - fd := Handle(uintptr(s.stream_data)) - sz, _ := file_size(fd) - return sz - }, - impl_flush = proc(s: io.Stream) -> io.Error { +_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { + fd := Handle(uintptr(stream_data)) + n_int: int + os_err: Errno + switch mode { + case .Close: + close(fd) + case .Flush: when ODIN_OS == .Windows { - fd := Handle(uintptr(s.stream_data)) flush(fd) } else { // TOOD(bill): other operating systems } - return nil - }, - impl_close = proc(s: io.Stream) -> io.Error { - fd := Handle(uintptr(s.stream_data)) - close(fd) - return nil - }, + case .Read: + n_int, os_err = read(fd, p) + n = i64(n_int) + if os_err != 0 { + err = .Unknown + } + case .Read_At: + n_int, os_err = read_at(fd, p, offset) + n = i64(n_int) + case .Write: + n_int, os_err = write(fd, p) + n = i64(n_int) + case .Write_At: + n_int, os_err = write_at(fd, p, offset) + n = i64(n_int) + case .Seek: + n, os_err = seek(fd, offset, int(whence)) + case .Size: + n, os_err = file_size(fd) + case .Destroy: + err = .Empty + case .Query: + return io.query_utility({.Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query}) + } + if err == nil && os_err != 0 { + err = .Unknown + } + return } diff --git a/core/strings/builder.odin b/core/strings/builder.odin index edde4b297..28c56f6f9 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -164,36 +164,27 @@ builder_init :: proc{ builder_init_len_cap, } @(private) -_builder_stream_vtable_obj := io.Stream_VTable{ - impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { - b := (^Builder)(s.stream_data) - n = write_bytes(b, p) - if n < len(p) { +_builder_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { + b := (^Builder)(stream_data) + #partial switch mode { + case .Write: + n = i64(write_bytes(b, p)) + if n < i64(len(p)) { err = .EOF } return - }, - impl_write_byte = proc(s: io.Stream, c: byte) -> (err: io.Error) { - b := (^Builder)(s.stream_data) - n := write_byte(b, c) - if n == 0 { - err = .EOF - } + case .Size: + n = i64(len(b.buf)) return - }, - impl_size = proc(s: io.Stream) -> i64 { - b := (^Builder)(s.stream_data) - return i64(len(b.buf)) - }, - impl_destroy = proc(s: io.Stream) -> io.Error { - b := (^Builder)(s.stream_data) + case .Destroy: builder_destroy(b) - return .None - }, + return + case .Query: + return io.query_utility({.Write, .Size, .Destroy, .Query}) + } + return 0, .Empty } -// NOTE(dweiler): Work around a miscompilation bug on Linux still. -@(private) -_builder_stream_vtable := &_builder_stream_vtable_obj + /* Returns an io.Stream from a Builder @@ -204,7 +195,7 @@ Returns: - res: the io.Stream */ to_stream :: proc(b: ^Builder) -> (res: io.Stream) { - return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b} + return io.Stream{procedure=_builder_stream_proc, data=b} } /* Returns an io.Writer from a Builder diff --git a/core/strings/reader.odin b/core/strings/reader.odin index 081e59b4b..bb49bf917 100644 --- a/core/strings/reader.odin +++ b/core/strings/reader.odin @@ -35,8 +35,8 @@ Returns: - s: An io.Stream for the given Reader */ reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) { - s.stream_data = r - s.stream_vtable = &_reader_vtable + s.data = r + s.procedure = _reader_proc return } /* @@ -294,41 +294,21 @@ This VTable is used by the Reader struct to provide its functionality as an `io.Stream`. */ @(private) -_reader_vtable := io.Stream_VTable{ - impl_size = proc(s: io.Stream) -> i64 { - r := (^Reader)(s.stream_data) - return reader_size(r) - }, - impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { - r := (^Reader)(s.stream_data) - return reader_read(r, p) - }, - impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) { - r := (^Reader)(s.stream_data) - return reader_read_at(r, p, off) - }, - impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) { - r := (^Reader)(s.stream_data) - return reader_read_byte(r) - }, - impl_unread_byte = proc(s: io.Stream) -> io.Error { - r := (^Reader)(s.stream_data) - return reader_unread_byte(r) - }, - impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) { - r := (^Reader)(s.stream_data) - return reader_read_rune(r) - }, - impl_unread_rune = proc(s: io.Stream) -> io.Error { - r := (^Reader)(s.stream_data) - return reader_unread_rune(r) - }, - impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { - r := (^Reader)(s.stream_data) - return reader_seek(r, offset, whence) - }, - impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) { - r := (^Reader)(s.stream_data) - return reader_write_to(r, w) - }, +_reader_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { + r := (^Reader)(stream_data) + #partial switch mode { + case .Size: + n = reader_size(r) + return + case .Read: + return io._i64_err(reader_read(r, p)) + case .Read_At: + return io._i64_err(reader_read_at(r, p, offset)) + case .Seek: + n, err = reader_seek(r, offset, whence) + return + case .Query: + return io.query_utility({.Size, .Read, .Read_At, .Seek, .Query}) + } + return 0, .Empty } diff --git a/vendor/botan/blake2b/blake2b.odin b/vendor/botan/blake2b/blake2b.odin index 18fd89bd8..f0b3fdfa8 100644 --- a/vendor/botan/blake2b/blake2b.odin +++ b/vendor/botan/blake2b/blake2b.odin @@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/gost/gost.odin b/vendor/botan/gost/gost.odin index bccc4d463..cf14ead15 100644 --- a/vendor/botan/gost/gost.odin +++ b/vendor/botan/gost/gost.odin @@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/keccak/keccak.odin b/vendor/botan/keccak/keccak.odin index 4c82edc92..40d563d58 100644 --- a/vendor/botan/keccak/keccak.odin +++ b/vendor/botan/keccak/keccak.odin @@ -69,7 +69,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/md4/md4.odin b/vendor/botan/md4/md4.odin index ddb7d5940..424756fae 100644 --- a/vendor/botan/md4/md4.odin +++ b/vendor/botan/md4/md4.odin @@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/md5/md5.odin b/vendor/botan/md5/md5.odin index 9ea489669..a23c4fd01 100644 --- a/vendor/botan/md5/md5.odin +++ b/vendor/botan/md5/md5.odin @@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/ripemd/ripemd.odin b/vendor/botan/ripemd/ripemd.odin index 33f0ba692..cf08aebcf 100644 --- a/vendor/botan/ripemd/ripemd.odin +++ b/vendor/botan/ripemd/ripemd.odin @@ -69,7 +69,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/sha1/sha1.odin b/vendor/botan/sha1/sha1.odin index 96520f09e..e33e5a51a 100644 --- a/vendor/botan/sha1/sha1.odin +++ b/vendor/botan/sha1/sha1.odin @@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/sha2/sha2.odin b/vendor/botan/sha2/sha2.odin index d583298ee..8283dde03 100644 --- a/vendor/botan/sha2/sha2.odin +++ b/vendor/botan/sha2/sha2.odin @@ -72,7 +72,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -151,7 +151,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -230,7 +230,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -309,7 +309,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/sha3/sha3.odin b/vendor/botan/sha3/sha3.odin index 5f82be49c..f736d6665 100644 --- a/vendor/botan/sha3/sha3.odin +++ b/vendor/botan/sha3/sha3.odin @@ -72,7 +72,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -151,7 +151,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -230,7 +230,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -309,7 +309,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/shake/shake.odin b/vendor/botan/shake/shake.odin index b973fee24..0a286c72e 100644 --- a/vendor/botan/shake/shake.odin +++ b/vendor/botan/shake/shake.odin @@ -70,7 +70,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -149,7 +149,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/skein512/skein512.odin b/vendor/botan/skein512/skein512.odin index 41ffaefff..7dbfaf438 100644 --- a/vendor/botan/skein512/skein512.odin +++ b/vendor/botan/skein512/skein512.odin @@ -72,7 +72,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -151,7 +151,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -230,7 +230,7 @@ hash_stream_slice :: proc(s: io.Stream, bit_size: int, allocator := context.allo defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/sm3/sm3.odin b/vendor/botan/sm3/sm3.odin index 52fe9a488..c23885445 100644 --- a/vendor/botan/sm3/sm3.odin +++ b/vendor/botan/sm3/sm3.odin @@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/streebog/streebog.odin b/vendor/botan/streebog/streebog.odin index fdc07923f..45d535192 100644 --- a/vendor/botan/streebog/streebog.odin +++ b/vendor/botan/streebog/streebog.odin @@ -70,7 +70,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -149,7 +149,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/tiger/tiger.odin b/vendor/botan/tiger/tiger.odin index 3d7e064d0..90efd8142 100644 --- a/vendor/botan/tiger/tiger.odin +++ b/vendor/botan/tiger/tiger.odin @@ -71,7 +71,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -150,7 +150,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -229,7 +229,7 @@ hash_stream_192 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/whirlpool/whirlpool.odin b/vendor/botan/whirlpool/whirlpool.odin index c32ff20c0..15f5109d9 100644 --- a/vendor/botan/whirlpool/whirlpool.odin +++ b/vendor/botan/whirlpool/whirlpool.odin @@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = s->impl_read(buf) + i, _ = io.read({s}, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } From 9ee4b76cd950e5eef9d480831670ae7bcff64775 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 8 Jun 2023 16:38:57 +0100 Subject: [PATCH 016/144] Just make the `io.Reader` etc aliases --- core/bufio/writer.odin | 2 +- core/compress/common.odin | 2 +- core/crypto/blake/blake.odin | 8 ++-- core/crypto/blake2b/blake2b.odin | 2 +- core/crypto/blake2s/blake2s.odin | 2 +- core/crypto/gost/gost.odin | 2 +- core/crypto/groestl/groestl.odin | 8 ++-- core/crypto/haval/haval.odin | 30 +++++++------- core/crypto/jh/jh.odin | 8 ++-- core/crypto/keccak/keccak.odin | 8 ++-- core/crypto/md2/md2.odin | 2 +- core/crypto/md4/md4.odin | 2 +- core/crypto/md5/md5.odin | 2 +- core/crypto/ripemd/ripemd.odin | 8 ++-- core/crypto/sha1/sha1.odin | 2 +- core/crypto/sha2/sha2.odin | 8 ++-- core/crypto/sha3/sha3.odin | 8 ++-- core/crypto/shake/shake.odin | 4 +- core/crypto/sm3/sm3.odin | 2 +- core/crypto/streebog/streebog.odin | 4 +- core/crypto/tiger/tiger.odin | 6 +-- core/crypto/tiger2/tiger2.odin | 6 +-- core/crypto/whirlpool/whirlpool.odin | 2 +- core/fmt/fmt_os.odin | 10 ++--- core/io/conv.odin | 30 +++++++------- core/io/io.odin | 57 ++++++++++++--------------- vendor/botan/blake2b/blake2b.odin | 2 +- vendor/botan/gost/gost.odin | 2 +- vendor/botan/keccak/keccak.odin | 2 +- vendor/botan/md4/md4.odin | 2 +- vendor/botan/md5/md5.odin | 2 +- vendor/botan/ripemd/ripemd.odin | 2 +- vendor/botan/sha1/sha1.odin | 2 +- vendor/botan/sha2/sha2.odin | 8 ++-- vendor/botan/sha3/sha3.odin | 8 ++-- vendor/botan/shake/shake.odin | 4 +- vendor/botan/skein512/skein512.odin | 6 +-- vendor/botan/sm3/sm3.odin | 2 +- vendor/botan/streebog/streebog.odin | 4 +- vendor/botan/tiger/tiger.odin | 6 +-- vendor/botan/whirlpool/whirlpool.odin | 2 +- 41 files changed, 137 insertions(+), 142 deletions(-) diff --git a/core/bufio/writer.odin b/core/bufio/writer.odin index b4234af43..bfa8b804f 100644 --- a/core/bufio/writer.odin +++ b/core/bufio/writer.odin @@ -221,7 +221,7 @@ writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) { // writer_to_stream converts a Writer into an io.Stream writer_to_writer :: proc(b: ^Writer) -> (s: io.Writer) { - return {writer_to_stream(b)} + return writer_to_stream(b) } diff --git a/core/compress/common.odin b/core/compress/common.odin index 2d00ed359..bc56229c2 100644 --- a/core/compress/common.odin +++ b/core/compress/common.odin @@ -216,7 +216,7 @@ read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int // TODO: REMOVE ALL USE OF context.temp_allocator here // the is literally no need for it b := make([]u8, size, context.temp_allocator) - _, e := io.read({z.input}, b[:]) + _, e := io.read(z.input, b[:]) if e == .None { return b, .None } diff --git a/core/crypto/blake/blake.odin b/core/crypto/blake/blake.odin index 67f3c0d35..3685109e4 100644 --- a/core/crypto/blake/blake.odin +++ b/core/crypto/blake/blake.odin @@ -70,7 +70,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -149,7 +149,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -228,7 +228,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -307,7 +307,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/blake2b/blake2b.odin b/core/crypto/blake2b/blake2b.odin index 82dabc542..8f0770f82 100644 --- a/core/crypto/blake2b/blake2b.odin +++ b/core/crypto/blake2b/blake2b.odin @@ -77,7 +77,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { _blake2.update(&ctx, buf[:read]) } diff --git a/core/crypto/blake2s/blake2s.odin b/core/crypto/blake2s/blake2s.odin index a767d4d70..6a2d4ab9b 100644 --- a/core/crypto/blake2s/blake2s.odin +++ b/core/crypto/blake2s/blake2s.odin @@ -77,7 +77,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { _blake2.update(&ctx, buf[:read]) } diff --git a/core/crypto/gost/gost.odin b/core/crypto/gost/gost.odin index fa2df862f..5aca8ce95 100644 --- a/core/crypto/gost/gost.odin +++ b/core/crypto/gost/gost.odin @@ -65,7 +65,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/groestl/groestl.odin b/core/crypto/groestl/groestl.odin index 604a3eb9b..61460808f 100644 --- a/core/crypto/groestl/groestl.odin +++ b/core/crypto/groestl/groestl.odin @@ -70,7 +70,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -149,7 +149,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -228,7 +228,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -307,7 +307,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/haval/haval.odin b/core/crypto/haval/haval.odin index 76cfbecbe..b98facb33 100644 --- a/core/crypto/haval/haval.odin +++ b/core/crypto/haval/haval.odin @@ -79,7 +79,7 @@ hash_stream_128_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -164,7 +164,7 @@ hash_stream_128_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -249,7 +249,7 @@ hash_stream_128_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -334,7 +334,7 @@ hash_stream_160_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -419,7 +419,7 @@ hash_stream_160_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -504,7 +504,7 @@ hash_stream_160_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -589,7 +589,7 @@ hash_stream_192_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -674,7 +674,7 @@ hash_stream_192_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -759,7 +759,7 @@ hash_stream_192_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -844,7 +844,7 @@ hash_stream_224_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -929,7 +929,7 @@ hash_stream_224_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -1014,7 +1014,7 @@ hash_stream_224_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -1099,7 +1099,7 @@ hash_stream_256_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -1184,7 +1184,7 @@ hash_stream_256_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) @@ -1270,7 +1270,7 @@ hash_stream_256_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) ctx.str_len = u32(len(buf[:read])) if read > 0 { update(&ctx, buf[:read]) diff --git a/core/crypto/jh/jh.odin b/core/crypto/jh/jh.odin index 6b4a73b46..5dc6c4e6b 100644 --- a/core/crypto/jh/jh.odin +++ b/core/crypto/jh/jh.odin @@ -70,7 +70,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -149,7 +149,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -228,7 +228,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -307,7 +307,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/keccak/keccak.odin b/core/crypto/keccak/keccak.odin index c09c9ddbe..4c74858d2 100644 --- a/core/crypto/keccak/keccak.odin +++ b/core/crypto/keccak/keccak.odin @@ -77,7 +77,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } @@ -159,7 +159,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } @@ -241,7 +241,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } @@ -323,7 +323,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } diff --git a/core/crypto/md2/md2.odin b/core/crypto/md2/md2.odin index 693e2c6b0..4942183e1 100644 --- a/core/crypto/md2/md2.odin +++ b/core/crypto/md2/md2.odin @@ -64,7 +64,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/md4/md4.odin b/core/crypto/md4/md4.odin index 1f161e1cb..8efdbb5a5 100644 --- a/core/crypto/md4/md4.odin +++ b/core/crypto/md4/md4.odin @@ -68,7 +68,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/md5/md5.odin b/core/crypto/md5/md5.odin index db04e67df..858480b04 100644 --- a/core/crypto/md5/md5.odin +++ b/core/crypto/md5/md5.odin @@ -67,7 +67,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/ripemd/ripemd.odin b/core/crypto/ripemd/ripemd.odin index 257f4144e..f9edb121b 100644 --- a/core/crypto/ripemd/ripemd.odin +++ b/core/crypto/ripemd/ripemd.odin @@ -69,7 +69,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -145,7 +145,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -221,7 +221,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -297,7 +297,7 @@ hash_stream_320 :: proc(s: io.Stream) -> ([DIGEST_SIZE_320]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/sha1/sha1.odin b/core/crypto/sha1/sha1.odin index 2e1991444..599d1791e 100644 --- a/core/crypto/sha1/sha1.odin +++ b/core/crypto/sha1/sha1.odin @@ -67,7 +67,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/sha2/sha2.odin b/core/crypto/sha2/sha2.odin index cbd3a6b39..0f55c4be1 100644 --- a/core/crypto/sha2/sha2.odin +++ b/core/crypto/sha2/sha2.odin @@ -74,7 +74,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -153,7 +153,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -232,7 +232,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -311,7 +311,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/sha3/sha3.odin b/core/crypto/sha3/sha3.odin index ec6fbc971..5d8ad2106 100644 --- a/core/crypto/sha3/sha3.odin +++ b/core/crypto/sha3/sha3.odin @@ -73,7 +73,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } @@ -152,7 +152,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } @@ -231,7 +231,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } @@ -310,7 +310,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } diff --git a/core/crypto/shake/shake.odin b/core/crypto/shake/shake.odin index 50ef756aa..020ba68f3 100644 --- a/core/crypto/shake/shake.odin +++ b/core/crypto/shake/shake.odin @@ -73,7 +73,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } @@ -155,7 +155,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { _sha3.update(&ctx, buf[:read]) } diff --git a/core/crypto/sm3/sm3.odin b/core/crypto/sm3/sm3.odin index b35e20e37..9e684ff08 100644 --- a/core/crypto/sm3/sm3.odin +++ b/core/crypto/sm3/sm3.odin @@ -66,7 +66,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/streebog/streebog.odin b/core/crypto/streebog/streebog.odin index aa4dd7906..42da1e695 100644 --- a/core/crypto/streebog/streebog.odin +++ b/core/crypto/streebog/streebog.odin @@ -70,7 +70,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } @@ -146,7 +146,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/crypto/tiger/tiger.odin b/core/crypto/tiger/tiger.odin index a4982d7c4..614926129 100644 --- a/core/crypto/tiger/tiger.odin +++ b/core/crypto/tiger/tiger.odin @@ -71,7 +71,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { _tiger.update(&ctx, buf[:read]) } @@ -150,7 +150,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { _tiger.update(&ctx, buf[:read]) } @@ -229,7 +229,7 @@ hash_stream_192 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { _tiger.update(&ctx, buf[:read]) } diff --git a/core/crypto/tiger2/tiger2.odin b/core/crypto/tiger2/tiger2.odin index 432387bdf..ead874d56 100644 --- a/core/crypto/tiger2/tiger2.odin +++ b/core/crypto/tiger2/tiger2.odin @@ -71,7 +71,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { _tiger.update(&ctx, buf[:read]) } @@ -150,7 +150,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { _tiger.update(&ctx, buf[:read]) } @@ -229,7 +229,7 @@ hash_stream_192 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { _tiger.update(&ctx, buf[:read]) } diff --git a/core/crypto/whirlpool/whirlpool.odin b/core/crypto/whirlpool/whirlpool.odin index 8ae6f413e..cf0bf6490 100644 --- a/core/crypto/whirlpool/whirlpool.odin +++ b/core/crypto/whirlpool/whirlpool.odin @@ -66,7 +66,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) read := 1 for read > 0 { - read, _ = io.read({s}, buf) + read, _ = io.read(s, buf) if read > 0 { update(&ctx, buf[:read]) } diff --git a/core/fmt/fmt_os.odin b/core/fmt/fmt_os.odin index 861b0c3b9..299d8f017 100644 --- a/core/fmt/fmt_os.odin +++ b/core/fmt/fmt_os.odin @@ -12,7 +12,7 @@ fprint :: proc(fd: os.Handle, args: ..any, sep := " ") -> int { b: bufio.Writer defer bufio.writer_flush(&b) - bufio.writer_init_with_buf(&b, {os.stream_from_handle(fd)}, buf[:]) + bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:]) w := bufio.writer_to_writer(&b) return wprint(w=w, args=args, sep=sep) } @@ -23,7 +23,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int { b: bufio.Writer defer bufio.writer_flush(&b) - bufio.writer_init_with_buf(&b, {os.stream_from_handle(fd)}, buf[:]) + bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:]) w := bufio.writer_to_writer(&b) return wprintln(w=w, args=args, sep=sep) @@ -34,7 +34,7 @@ fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int { b: bufio.Writer defer bufio.writer_flush(&b) - bufio.writer_init_with_buf(&b, {os.stream_from_handle(fd)}, buf[:]) + bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:]) w := bufio.writer_to_writer(&b) return wprintf(w, fmt, ..args) @@ -44,7 +44,7 @@ fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info) -> (n: int, err: io b: bufio.Writer defer bufio.writer_flush(&b) - bufio.writer_init_with_buf(&b, {os.stream_from_handle(fd)}, buf[:]) + bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:]) w := bufio.writer_to_writer(&b) return wprint_type(w, info) @@ -54,7 +54,7 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) { b: bufio.Writer defer bufio.writer_flush(&b) - bufio.writer_init_with_buf(&b, {os.stream_from_handle(fd)}, buf[:]) + bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:]) w := bufio.writer_to_writer(&b) return wprint_typeid(w, id) diff --git a/core/io/conv.odin b/core/io/conv.odin index 2b1351e18..e3286baca 100644 --- a/core/io/conv.odin +++ b/core/io/conv.odin @@ -1,80 +1,80 @@ package io to_reader :: proc(s: Stream) -> (r: Reader, ok: bool = true) #optional_ok { - r.stream = s + r = s ok = .Read in query(s) return } to_writer :: proc(s: Stream) -> (w: Writer, ok: bool = true) #optional_ok { - w.stream = s + w = s ok = .Write in query(s) return } to_closer :: proc(s: Stream) -> (c: Closer, ok: bool = true) #optional_ok { - c.stream = s + c = s ok = .Close in query(s) return } to_flusher :: proc(s: Stream) -> (f: Flusher, ok: bool = true) #optional_ok { - f.stream = s + f = s ok = .Flush in query(s) return } to_seeker :: proc(s: Stream) -> (seeker: Seeker, ok: bool = true) #optional_ok { - seeker.stream = s + seeker = s ok = .Seek in query(s) return } to_read_writer :: proc(s: Stream) -> (r: Read_Writer, ok: bool = true) #optional_ok { - r.stream = s + r = s ok = query(s) >= {.Read, .Write} return } to_read_closer :: proc(s: Stream) -> (r: Read_Closer, ok: bool = true) #optional_ok { - r.stream = s + r = s ok = query(s) >= {.Read, .Close} return } to_read_write_closer :: proc(s: Stream) -> (r: Read_Write_Closer, ok: bool = true) #optional_ok { - r.stream = s + r = s ok = query(s) >= {.Read, .Write, .Close} return } to_read_write_seeker :: proc(s: Stream) -> (r: Read_Write_Seeker, ok: bool = true) #optional_ok { - r.stream = s + r = s ok = query(s) >= {.Read, .Write, .Seek} return } to_write_flusher :: proc(s: Stream) -> (w: Write_Flusher, ok: bool = true) #optional_ok { - w.stream = s + w = s ok = query(s) >= {.Write, .Flush} return } to_write_flush_closer :: proc(s: Stream) -> (w: Write_Flush_Closer, ok: bool = true) #optional_ok { - w.stream = s + w = s ok = query(s) >= {.Write, .Flush, .Close} return } to_reader_at :: proc(s: Stream) -> (r: Reader_At, ok: bool = true) #optional_ok { - r.stream = s + r = s ok = query(s) >= {.Read_At} return } to_writer_at :: proc(s: Stream) -> (w: Writer_At, ok: bool = true) #optional_ok { - w.stream = s + w = s ok = query(s) >= {.Write_At} return } to_write_closer :: proc(s: Stream) -> (w: Write_Closer, ok: bool = true) #optional_ok { - w.stream = s + w = s ok = query(s) >= {.Write, .Close} return } to_write_seeker :: proc(s: Stream) -> (w: Write_Seeker, ok: bool = true) #optional_ok { - w.stream = s + w = s ok = query(s) >= {.Write, .Seek} return } diff --git a/core/io/io.odin b/core/io/io.odin index 868e16204..566e13c54 100644 --- a/core/io/io.odin +++ b/core/io/io.odin @@ -75,29 +75,29 @@ Stream :: struct { data: rawptr, } -Reader :: struct {using stream: Stream} -Writer :: struct {using stream: Stream} -Closer :: struct {using stream: Stream} -Flusher :: struct {using stream: Stream} -Seeker :: struct {using stream: Stream} +Reader :: Stream +Writer :: Stream +Closer :: Stream +Flusher :: Stream +Seeker :: Stream -Read_Writer :: struct {using stream: Stream} -Read_Closer :: struct {using stream: Stream} -Read_Write_Closer :: struct {using stream: Stream} -Read_Write_Seeker :: struct {using stream: Stream} +Read_Writer :: Stream +Read_Closer :: Stream +Read_Write_Closer :: Stream +Read_Write_Seeker :: Stream -Write_Closer :: struct {using stream: Stream} -Write_Seeker :: struct {using stream: Stream} -Write_Flusher :: struct {using stream: Stream} -Write_Flush_Closer :: struct {using stream: Stream} +Write_Closer :: Stream +Write_Seeker :: Stream +Write_Flusher :: Stream +Write_Flush_Closer :: Stream -Reader_At :: struct {using stream: Stream} -Writer_At :: struct {using stream: Stream} +Reader_At :: Stream +Writer_At :: Stream destroy :: proc(s: Stream) -> (err: Error) { - _ = flush({s}) - _ = close({s}) + _ = flush(s) + _ = close(s) if s.procedure != nil { _, err = s.procedure(s.data, .Destroy, nil, 0, nil) } else { @@ -192,11 +192,10 @@ size :: proc(s: Stream) -> (n: i64, err: Error) { if s.procedure != nil { n, err = s.procedure(s.data, .Size, nil, 0, nil) if err == .Empty { - seeker := Seeker{s} n = 0 - curr := seek(seeker, 0, .Current) or_return - end := seek(seeker, 0, .End) or_return - seek(seeker, curr, .Start) or_return + curr := seek(s, 0, .Current) or_return + end := seek(s, 0, .End) or_return + seek(s, curr, .Start) or_return n = end } } else { @@ -220,11 +219,9 @@ read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n: if err != .Empty { n = int(n64) } else { - seeker := Seeker{r} - reader := Reader{r} - curr := seek(seeker, offset, .Current) or_return - n, err = read(reader, p) - _, err1 := seek(seeker, curr, .Start) + curr := seek(r, offset, .Current) or_return + n, err = read(r, p) + _, err1 := seek(r, curr, .Start) if err1 != nil && err == nil { err = err1 } @@ -248,11 +245,9 @@ write_at :: proc(w: Writer_At, p: []byte, offset: i64, n_written: ^int = nil) -> if err != .Empty { n = int(n64) } else { - seeker := Seeker{w} - writer := Writer{w} - curr := seek(seeker, offset, .Current) or_return - n, err = write(writer, p) - _, err1 := seek(seeker, curr, .Start) + curr := seek(w, offset, .Current) or_return + n, err = write(w, p) + _, err1 := seek(w, curr, .Start) if err1 != nil && err == nil { err = err1 } diff --git a/vendor/botan/blake2b/blake2b.odin b/vendor/botan/blake2b/blake2b.odin index f0b3fdfa8..6cc828caf 100644 --- a/vendor/botan/blake2b/blake2b.odin +++ b/vendor/botan/blake2b/blake2b.odin @@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/gost/gost.odin b/vendor/botan/gost/gost.odin index cf14ead15..5b3db31fe 100644 --- a/vendor/botan/gost/gost.odin +++ b/vendor/botan/gost/gost.odin @@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/keccak/keccak.odin b/vendor/botan/keccak/keccak.odin index 40d563d58..c08eaf598 100644 --- a/vendor/botan/keccak/keccak.odin +++ b/vendor/botan/keccak/keccak.odin @@ -69,7 +69,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/md4/md4.odin b/vendor/botan/md4/md4.odin index 424756fae..02c33dde9 100644 --- a/vendor/botan/md4/md4.odin +++ b/vendor/botan/md4/md4.odin @@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/md5/md5.odin b/vendor/botan/md5/md5.odin index a23c4fd01..9aaf96d27 100644 --- a/vendor/botan/md5/md5.odin +++ b/vendor/botan/md5/md5.odin @@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/ripemd/ripemd.odin b/vendor/botan/ripemd/ripemd.odin index cf08aebcf..ddb549350 100644 --- a/vendor/botan/ripemd/ripemd.odin +++ b/vendor/botan/ripemd/ripemd.odin @@ -69,7 +69,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/sha1/sha1.odin b/vendor/botan/sha1/sha1.odin index e33e5a51a..c39a41d0a 100644 --- a/vendor/botan/sha1/sha1.odin +++ b/vendor/botan/sha1/sha1.odin @@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/sha2/sha2.odin b/vendor/botan/sha2/sha2.odin index 8283dde03..4ce001a75 100644 --- a/vendor/botan/sha2/sha2.odin +++ b/vendor/botan/sha2/sha2.odin @@ -72,7 +72,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -151,7 +151,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -230,7 +230,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -309,7 +309,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/sha3/sha3.odin b/vendor/botan/sha3/sha3.odin index f736d6665..5dcb008ce 100644 --- a/vendor/botan/sha3/sha3.odin +++ b/vendor/botan/sha3/sha3.odin @@ -72,7 +72,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -151,7 +151,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -230,7 +230,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -309,7 +309,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/shake/shake.odin b/vendor/botan/shake/shake.odin index 0a286c72e..af577f316 100644 --- a/vendor/botan/shake/shake.odin +++ b/vendor/botan/shake/shake.odin @@ -70,7 +70,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -149,7 +149,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/skein512/skein512.odin b/vendor/botan/skein512/skein512.odin index 7dbfaf438..47529bc44 100644 --- a/vendor/botan/skein512/skein512.odin +++ b/vendor/botan/skein512/skein512.odin @@ -72,7 +72,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -151,7 +151,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -230,7 +230,7 @@ hash_stream_slice :: proc(s: io.Stream, bit_size: int, allocator := context.allo defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/sm3/sm3.odin b/vendor/botan/sm3/sm3.odin index c23885445..dd6da9e63 100644 --- a/vendor/botan/sm3/sm3.odin +++ b/vendor/botan/sm3/sm3.odin @@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/streebog/streebog.odin b/vendor/botan/streebog/streebog.odin index 45d535192..07c39684a 100644 --- a/vendor/botan/streebog/streebog.odin +++ b/vendor/botan/streebog/streebog.odin @@ -70,7 +70,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -149,7 +149,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/tiger/tiger.odin b/vendor/botan/tiger/tiger.odin index 90efd8142..960d4694b 100644 --- a/vendor/botan/tiger/tiger.odin +++ b/vendor/botan/tiger/tiger.odin @@ -71,7 +71,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -150,7 +150,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } @@ -229,7 +229,7 @@ hash_stream_192 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } diff --git a/vendor/botan/whirlpool/whirlpool.odin b/vendor/botan/whirlpool/whirlpool.odin index 15f5109d9..76d4d25d4 100644 --- a/vendor/botan/whirlpool/whirlpool.odin +++ b/vendor/botan/whirlpool/whirlpool.odin @@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) { defer delete(buf) i := 1 for i > 0 { - i, _ = io.read({s}, buf) + i, _ = io.read(s, buf) if i > 0 { botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i)) } From 6c040497efc1fafbdf5eae77454e7f1190217162 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 8 Jun 2023 16:45:13 +0100 Subject: [PATCH 017/144] Update os2 to new `io.Stream` --- core/os/os2/file_stream.odin | 100 ++++++++++++++-------------------- core/os/os2/path_windows.odin | 2 +- 2 files changed, 41 insertions(+), 61 deletions(-) diff --git a/core/os/os2/file_stream.odin b/core/os/os2/file_stream.odin index 7edbd68fa..91cab3d1c 100644 --- a/core/os/os2/file_stream.odin +++ b/core/os/os2/file_stream.odin @@ -3,17 +3,13 @@ package os2 import "core:io" to_stream :: proc(f: ^File) -> (s: io.Stream) { - s.stream_data = f - s.stream_vtable = &_file_stream_vtable + s.data = f + s.procedure = _file_stream_proc return } -to_writer :: proc(f: ^File) -> (s: io.Writer) { - return {to_stream(f)} -} -to_reader :: proc(f: ^File) -> (s: io.Reader) { - return {to_stream(f)} -} +to_writer :: to_stream +to_reader :: to_stream @(private) @@ -26,68 +22,52 @@ error_to_io_error :: proc(ferr: Error) -> io.Error { @(private) -_file_stream_vtable := io.Stream_VTable{ - impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { - f := (^File)(s.stream_data) - ferr: Error - n, ferr = read(f, p) +_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { + f := (^File)(stream_data) + ferr: Error + i: int + switch mode { + case .Read: + i, ferr = read(f, p) + n = i64(i) err = error_to_io_error(ferr) return - }, - impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { - f := (^File)(s.stream_data) - ferr: Error - n, ferr = read_at(f, p, offset) + case .Read_At: + i, ferr = read_at(f, p, offset) + n = i64(i) err = error_to_io_error(ferr) return - }, - impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) { - f := (^File)(s.stream_data) - ferr: Error - n, ferr = write_to(f, w) + case .Write: + i, ferr = write(f, p) + n = i64(i) err = error_to_io_error(ferr) return - }, - impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { - f := (^File)(s.stream_data) - ferr: Error - n, ferr = write(f, p) + case .Write_At: + i, ferr = write_at(f, p, offset) + n = i64(i) err = error_to_io_error(ferr) return - }, - impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { - f := (^File)(s.stream_data) - ferr: Error - n, ferr = write_at(f, p, offset) + case .Seek: + n, ferr = seek(f, offset, Seek_From(whence)) err = error_to_io_error(ferr) return - }, - impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) { - f := (^File)(s.stream_data) - ferr: Error - n, ferr = read_from(f, r) + case .Size: + n, ferr = file_size(f) err = error_to_io_error(ferr) return - }, - impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { - f := (^File)(s.stream_data) - n, ferr := seek(f, offset, Seek_From(whence)) - err := error_to_io_error(ferr) - return n, err - }, - impl_size = proc(s: io.Stream) -> i64 { - f := (^File)(s.stream_data) - sz, _ := file_size(f) - return sz - }, - impl_flush = proc(s: io.Stream) -> io.Error { - f := (^File)(s.stream_data) - ferr := flush(f) - return error_to_io_error(ferr) - }, - impl_close = proc(s: io.Stream) -> io.Error { - f := (^File)(s.stream_data) - ferr := close(f) - return error_to_io_error(ferr) - }, + case .Flush: + ferr = flush(f) + err = error_to_io_error(ferr) + return + case .Close: + ferr = close(f) + err = error_to_io_error(ferr) + return + case .Query: + return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Query}) + case .Destroy: + return 0, .Empty + } + return 0, .Empty } + diff --git a/core/os/os2/path_windows.odin b/core/os/os2/path_windows.odin index 2dc667822..a2306784e 100644 --- a/core/os/os2/path_windows.odin +++ b/core/os/os2/path_windows.odin @@ -23,7 +23,7 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { fix_root_directory :: proc(p: string) -> (s: string, allocated: bool, err: runtime.Allocator_Error) { if len(p) == len(`\\?\c:`) { if is_path_separator(p[0]) && is_path_separator(p[1]) && p[2] == '?' && is_path_separator(p[3]) && p[5] == ':' { - s = strings.concatenate_safe({p, `\`}, _file_allocator()) or_return + s = strings.concatenate({p, `\`}, _file_allocator()) or_return allocated = true return } From 145a7a24e8710a428f48db64c23544ac813a6814 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 8 Jun 2023 16:55:01 +0100 Subject: [PATCH 018/144] Use `io.Stream` as the internal interface for `os2.File` --- core/os/os2/file.odin | 42 +++++++-------------- core/os/os2/file_linux.odin | 66 +++++++++++++++++++++++++++------ core/os/os2/file_stream.odin | 58 ++--------------------------- core/os/os2/file_windows.odin | 69 +++++++++++++++++++++++++++++------ 4 files changed, 131 insertions(+), 104 deletions(-) diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin index eb6d9e366..6c1cef89f 100644 --- a/core/os/os2/file.odin +++ b/core/os/os2/file.odin @@ -5,13 +5,8 @@ import "core:time" import "core:runtime" File :: struct { - impl: _File, -} - -Seek_From :: enum { - Start = 0, // seek relative to the origin of the file - Current = 1, // seek relative to the current offset - End = 2, // seek relative to the end + impl: _File, + stream: io.Stream, } File_Mode :: distinct u32 @@ -72,45 +67,36 @@ fd :: proc(f: ^File) -> uintptr { return _fd(f) } - -close :: proc(f: ^File) -> Error { - return _close(f) -} - name :: proc(f: ^File) -> string { return _name(f) } -seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) { - return _seek(f, offset, whence) +close :: proc(f: ^File) -> Error { + return io.close(f.stream) +} + +seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) { + return io.seek(f.stream, offset, whence) } read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { - return _read(f, p) + return io.read(f.stream, p) } read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { - return _read_at(f, p, offset) -} - -read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) { - return _read_from(f, r) + return io.read_at(f.stream, p, offset) } write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { - return _write(f, p) + return io.write(f.stream, p) } write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { - return _write_at(f, p, offset) -} - -write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) { - return _write_to(f, w) + return io.write_at(f.stream, p, offset) } file_size :: proc(f: ^File) -> (n: i64, err: Error) { - return _file_size(f) + return io.size(f.stream) } @@ -119,7 +105,7 @@ sync :: proc(f: ^File) -> Error { } flush :: proc(f: ^File) -> Error { - return _flush(f) + return io.flush(f.stream) } truncate :: proc(f: ^File, size: i64) -> Error { diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index 890bbfc43..a43c1aadf 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -73,6 +73,10 @@ _new_file :: proc(fd: uintptr, _: string) -> ^File { file.impl.fd = int(fd) file.impl.allocator = _file_allocator() file.impl.name = _get_full_path(file.impl.fd, file.impl.allocator) + file.stream = { + data = file, + procedure = _file_stream_proc, + } return file } @@ -102,7 +106,7 @@ _name :: proc(f: ^File) -> string { return f.impl.name if f != nil else "" } -_seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) { +_seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) { res := unix.sys_lseek(f.impl.fd, offset, int(whence)) if res < 0 { return -1, _get_platform_error(int(res)) @@ -139,11 +143,6 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { return } -_read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) { - //TODO - return -} - _write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { if len(p) == 0 { return 0, nil @@ -173,11 +172,6 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { return } -_write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) { - //TODO - return -} - _file_size :: proc(f: ^File) -> (n: i64, err: Error) { s: _Stat = --- res := unix.sys_fstat(f.impl.fd, &s) @@ -366,3 +360,53 @@ _is_dir_fd :: proc(fd: int) -> bool { _temp_name_to_cstring :: proc(name: string) -> (cname: cstring) { return strings.clone_to_cstring(name, context.temp_allocator) } + + +@(private="package") +_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { + f := (^File)(stream_data) + ferr: Error + i: int + switch mode { + case .Read: + i, ferr = _read(f, p) + n = i64(i) + err = error_to_io_error(ferr) + return + case .Read_At: + i, ferr = _read_at(f, p, offset) + n = i64(i) + err = error_to_io_error(ferr) + return + case .Write: + i, ferr = _write(f, p) + n = i64(i) + err = error_to_io_error(ferr) + return + case .Write_At: + i, ferr = _write_at(f, p, offset) + n = i64(i) + err = error_to_io_error(ferr) + return + case .Seek: + n, ferr = _seek(f, offset, whence) + err = error_to_io_error(ferr) + return + case .Size: + n, ferr = _file_size(f) + err = error_to_io_error(ferr) + return + case .Flush: + ferr = _flush(f) + err = error_to_io_error(ferr) + return + case .Close, .Destroy: + ferr = _close(f) + err = error_to_io_error(ferr) + return + case .Query: + return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query}) + } + return 0, .Empty +} + diff --git a/core/os/os2/file_stream.odin b/core/os/os2/file_stream.odin index 91cab3d1c..84176928d 100644 --- a/core/os/os2/file_stream.odin +++ b/core/os/os2/file_stream.odin @@ -3,8 +3,10 @@ package os2 import "core:io" to_stream :: proc(f: ^File) -> (s: io.Stream) { - s.data = f - s.procedure = _file_stream_proc + if f != nil { + assert(f.stream.procedure != nil) + s = f.stream + } return } @@ -19,55 +21,3 @@ error_to_io_error :: proc(ferr: Error) -> io.Error { } return ferr.(io.Error) or_else .Unknown } - - -@(private) -_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { - f := (^File)(stream_data) - ferr: Error - i: int - switch mode { - case .Read: - i, ferr = read(f, p) - n = i64(i) - err = error_to_io_error(ferr) - return - case .Read_At: - i, ferr = read_at(f, p, offset) - n = i64(i) - err = error_to_io_error(ferr) - return - case .Write: - i, ferr = write(f, p) - n = i64(i) - err = error_to_io_error(ferr) - return - case .Write_At: - i, ferr = write_at(f, p, offset) - n = i64(i) - err = error_to_io_error(ferr) - return - case .Seek: - n, ferr = seek(f, offset, Seek_From(whence)) - err = error_to_io_error(ferr) - return - case .Size: - n, ferr = file_size(f) - err = error_to_io_error(ferr) - return - case .Flush: - ferr = flush(f) - err = error_to_io_error(ferr) - return - case .Close: - ferr = close(f) - err = error_to_io_error(ferr) - return - case .Query: - return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Query}) - case .Destroy: - return 0, .Empty - } - return 0, .Empty -} - diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index e4ae4856a..50938d92a 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -144,6 +144,11 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File { } f.impl.kind = kind + f.stream = { + data = f, + procedure = _file_stream_proc, + } + return f } @@ -181,7 +186,7 @@ _name :: proc(f: ^File) -> string { return f.impl.name if f != nil else "" } -_seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) { +_seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) { handle := _handle(f) if handle == win32.INVALID_HANDLE { return 0, .Invalid_File @@ -329,11 +334,6 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { return } -_read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) { - // TODO(bill) - return -} - _write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { if len(p) == 0 { return @@ -397,11 +397,6 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { return } -_write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) { - // TODO(bill) - return -} - _file_size :: proc(f: ^File) -> (n: i64, err: Error) { length: win32.LARGE_INTEGER handle := _handle(f) @@ -727,3 +722,55 @@ _is_dir :: proc(path: string) -> bool { } return false } + + +@(private="package") +_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { + f := (^File)(stream_data) + ferr: Error + i: int + switch mode { + case .Read: + i, ferr = _read(f, p) + n = i64(i) + err = error_to_io_error(ferr) + return + case .Read_At: + i, ferr = _read_at(f, p, offset) + n = i64(i) + err = error_to_io_error(ferr) + return + case .Write: + i, ferr = _write(f, p) + n = i64(i) + err = error_to_io_error(ferr) + return + case .Write_At: + i, ferr = _write_at(f, p, offset) + n = i64(i) + err = error_to_io_error(ferr) + return + case .Seek: + n, ferr = _seek(f, offset, whence) + err = error_to_io_error(ferr) + return + case .Size: + n, ferr = _file_size(f) + err = error_to_io_error(ferr) + return + case .Flush: + ferr = _flush(f) + err = error_to_io_error(ferr) + return + case .Close: + ferr = _close(f) + err = error_to_io_error(ferr) + return + case .Query: + return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Query}) + case .Destroy: + return 0, .Empty + } + return 0, .Empty +} + From 2a212a755659459c945ce0aec80ad73c690e76d1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 8 Jun 2023 17:00:38 +0100 Subject: [PATCH 019/144] Put stream into the impl --- core/os/os2/file.odin | 28 +++++++++++++--------------- core/os/os2/file_linux.odin | 4 +++- core/os/os2/file_stream.odin | 4 ++-- core/os/os2/file_windows.odin | 4 +++- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin index 6c1cef89f..4113675ca 100644 --- a/core/os/os2/file.odin +++ b/core/os/os2/file.odin @@ -5,8 +5,7 @@ import "core:time" import "core:runtime" File :: struct { - impl: _File, - stream: io.Stream, + impl: _File, } File_Mode :: distinct u32 @@ -72,40 +71,39 @@ name :: proc(f: ^File) -> string { } close :: proc(f: ^File) -> Error { - return io.close(f.stream) + return io.close(f.impl.stream) } seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) { - return io.seek(f.stream, offset, whence) + return io.seek(f.impl.stream, offset, whence) } read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { - return io.read(f.stream, p) + return io.read(f.impl.stream, p) } read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { - return io.read_at(f.stream, p, offset) + return io.read_at(f.impl.stream, p, offset) } write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { - return io.write(f.stream, p) + return io.write(f.impl.stream, p) } write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { - return io.write_at(f.stream, p, offset) + return io.write_at(f.impl.stream, p, offset) } file_size :: proc(f: ^File) -> (n: i64, err: Error) { - return io.size(f.stream) -} - - -sync :: proc(f: ^File) -> Error { - return _sync(f) + return io.size(f.impl.stream) } flush :: proc(f: ^File) -> Error { - return io.flush(f.stream) + return io.flush(f.impl.stream) +} + +sync :: proc(f: ^File) -> Error { + return _sync(f) } truncate :: proc(f: ^File, size: i64) -> Error { diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index a43c1aadf..8df19614f 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -33,6 +33,8 @@ _File :: struct { name: string, fd: int, allocator: runtime.Allocator, + + stream: io.Stream, } _file_allocator :: proc() -> runtime.Allocator { @@ -73,7 +75,7 @@ _new_file :: proc(fd: uintptr, _: string) -> ^File { file.impl.fd = int(fd) file.impl.allocator = _file_allocator() file.impl.name = _get_full_path(file.impl.fd, file.impl.allocator) - file.stream = { + file.impl.stream = { data = file, procedure = _file_stream_proc, } diff --git a/core/os/os2/file_stream.odin b/core/os/os2/file_stream.odin index 84176928d..da1e3344f 100644 --- a/core/os/os2/file_stream.odin +++ b/core/os/os2/file_stream.odin @@ -4,8 +4,8 @@ import "core:io" to_stream :: proc(f: ^File) -> (s: io.Stream) { if f != nil { - assert(f.stream.procedure != nil) - s = f.stream + assert(f.impl.stream.procedure != nil) + s = f.impl.stream } return } diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 50938d92a..5ca3eded8 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -38,6 +38,8 @@ _File :: struct { wname: win32.wstring, kind: _File_Kind, + stream: io.Stream, + allocator: runtime.Allocator, rw_mutex: sync.RW_Mutex, // read write calls @@ -144,7 +146,7 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File { } f.impl.kind = kind - f.stream = { + f.impl.stream = { data = f, procedure = _file_stream_proc, } From 420f93ce7853902bff8c69707efafb67626c585b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 8 Jun 2023 22:38:15 +0100 Subject: [PATCH 020/144] Ignore BSD's for stream.odin --- core/os/stream.odin | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/core/os/stream.odin b/core/os/stream.odin index 946854cc6..2b4c83663 100644 --- a/core/os/stream.odin +++ b/core/os/stream.odin @@ -31,14 +31,18 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, err = .Unknown } case .Read_At: - n_int, os_err = read_at(fd, p, offset) - n = i64(n_int) + when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD) { + n_int, os_err = read_at(fd, p, offset) + n = i64(n_int) + } case .Write: n_int, os_err = write(fd, p) n = i64(n_int) case .Write_At: - n_int, os_err = write_at(fd, p, offset) - n = i64(n_int) + when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD) { + n_int, os_err = write_at(fd, p, offset) + n = i64(n_int) + } case .Seek: n, os_err = seek(fd, offset, int(whence)) case .Size: @@ -46,7 +50,11 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, case .Destroy: err = .Empty case .Query: - return io.query_utility({.Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query}) + when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD { + return io.query_utility({.Close, .Flush, .Read, .Write, .Seek, .Size, .Query}) + } else { + return io.query_utility({.Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query}) + } } if err == nil && os_err != 0 { err = .Unknown From c3933cead49c1d4b8b91b899f2b7739ba2493027 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Sun, 11 Jun 2023 23:41:26 +0200 Subject: [PATCH 021/144] Replace `0` with `os.ERROR_NONE` in demo.odin --- examples/demo/demo.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 3b9aa73ca..4b02bcd53 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -352,7 +352,7 @@ control_flow :: proc() { if false { f, err := os.open("my_file.txt") - if err != 0 { + if err != os.ERROR_NONE { // handle error } defer os.close(f) From 454709559ba7c5ff9fe032b01ec06cb5f9ab0793 Mon Sep 17 00:00:00 2001 From: Rickard Andersson Date: Mon, 12 Jun 2023 13:38:12 +0300 Subject: [PATCH 022/144] fix(dns): don't exit early on no hosts in hosts file If we don't have any hosts specified we'll still not generate any overrides which is fine, but we'll continue onto actually trying to resolve the hostname we came into the function for initially. --- core/net/dns_unix.odin | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/net/dns_unix.odin b/core/net/dns_unix.odin index bbecc7476..e9b7bd066 100644 --- a/core/net/dns_unix.odin +++ b/core/net/dns_unix.odin @@ -44,9 +44,6 @@ _get_dns_records_os :: proc(hostname: string, type: DNS_Record_Type, allocator : if !hosts_ok { return nil, .Invalid_Hosts_Config_Error } - if len(hosts) == 0 { - return - } host_overrides := make([dynamic]DNS_Record) for host in hosts { @@ -80,4 +77,4 @@ _get_dns_records_os :: proc(hostname: string, type: DNS_Record_Type, allocator : } return get_dns_records_from_nameservers(hostname, type, name_servers, host_overrides[:]) -} \ No newline at end of file +} From a7f8c78358a0d7258a3175a82dbe8ebee008ea03 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 12 Jun 2023 12:52:17 +0100 Subject: [PATCH 023/144] Change how "$anon" mangling works to use an atomic value instead --- src/llvm_backend.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 34a401c33..01d18d868 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -787,7 +787,8 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr // parent$count isize name_len = prefix_name.len + 1 + 8 + 1; char *name_text = gb_alloc_array(permanent_allocator(), char, name_len); - i32 name_id = cast(i32)m->gen->anonymous_proc_lits.count; + static std::atomic name_id; + name_id.fetch_add(1); name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), name_id); String name = make_string((u8 *)name_text, name_len-1); From ede98814587319815dc7050b8ff96132bf683592 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 12 Jun 2023 12:54:36 +0100 Subject: [PATCH 024/144] Keep compilers happy --- src/llvm_backend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 01d18d868..2897d3ca7 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -790,7 +790,7 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr static std::atomic name_id; name_id.fetch_add(1); - name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), name_id); + name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), name_id.load()); String name = make_string((u8 *)name_text, name_len-1); Type *type = type_of_expr(expr); From e16c6c1b6b6bb5fd870507f6b4051ce27dba0dd9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 12 Jun 2023 12:58:47 +0100 Subject: [PATCH 025/144] Replace procedure `strings.concatenate` --- core/os/os2/path_windows.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/os/os2/path_windows.odin b/core/os/os2/path_windows.odin index 2dc667822..a2306784e 100644 --- a/core/os/os2/path_windows.odin +++ b/core/os/os2/path_windows.odin @@ -23,7 +23,7 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { fix_root_directory :: proc(p: string) -> (s: string, allocated: bool, err: runtime.Allocator_Error) { if len(p) == len(`\\?\c:`) { if is_path_separator(p[0]) && is_path_separator(p[1]) && p[2] == '?' && is_path_separator(p[3]) && p[5] == ':' { - s = strings.concatenate_safe({p, `\`}, _file_allocator()) or_return + s = strings.concatenate({p, `\`}, _file_allocator()) or_return allocated = true return } From dca0fae781c9a21cacda44eaa9fd08d1929e0854 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 12 Jun 2023 13:42:51 +0100 Subject: [PATCH 026/144] Improve lb_generate_anonymous_proc_lit (again) --- src/llvm_backend.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 2897d3ca7..1acd15ad6 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -785,12 +785,10 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr // NOTE(bill): Generate a new name // parent$count - isize name_len = prefix_name.len + 1 + 8 + 1; + isize name_len = prefix_name.len + 6 + 11; char *name_text = gb_alloc_array(permanent_allocator(), char, name_len); static std::atomic name_id; - name_id.fetch_add(1); - - name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), name_id.load()); + name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), name_id.fetch_add(1)); String name = make_string((u8 *)name_text, name_len-1); Type *type = type_of_expr(expr); From 2022a7615aeb476521ad4eac168b2cdc231ba931 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 12 Jun 2023 14:10:18 +0100 Subject: [PATCH 027/144] Make all id suffixes use atomics where possible --- src/llvm_backend.cpp | 13 +++++++------ src/llvm_backend.hpp | 2 +- src/llvm_backend_general.cpp | 7 +++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 1acd15ad6..4fe8d4a00 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -157,10 +157,10 @@ gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) { return {compare_proc->value, compare_proc->type}; } - static u32 proc_index = 0; + static std::atomic proc_index; char buf[32] = {}; - isize n = gb_snprintf(buf, 32, "__$equal%u", ++proc_index); + isize n = gb_snprintf(buf, 32, "__$equal%u", 1+proc_index.fetch_add(1)); char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1); String proc_name = make_string_c(str); @@ -656,10 +656,10 @@ gb_internal lbValue lb_map_set_proc_for_type(lbModule *m, Type *type) { GB_ASSERT(*found != nullptr); return {(*found)->value, (*found)->type}; } - static u32 proc_index = 0; + static std::atomic proc_index; char buf[32] = {}; - isize n = gb_snprintf(buf, 32, "__$map_set-%u", ++proc_index); + isize n = gb_snprintf(buf, 32, "__$map_set-%u", 1+proc_index.fetch_add(1)); char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1); String proc_name = make_string_c(str); @@ -774,7 +774,8 @@ gb_internal lbValue lb_map_set_proc_for_type(lbModule *m, Type *type) { gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) { - MUTEX_GUARD(&m->gen->anonymous_proc_lits_mutex); + mutex_lock(&m->gen->anonymous_proc_lits_mutex); + defer (mutex_unlock(&m->gen->anonymous_proc_lits_mutex)); lbProcedure **found = map_get(&m->gen->anonymous_proc_lits, expr); if (found) { @@ -788,7 +789,7 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr isize name_len = prefix_name.len + 6 + 11; char *name_text = gb_alloc_array(permanent_allocator(), char, name_len); static std::atomic name_id; - name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), name_id.fetch_add(1)); + name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), 1+name_id.fetch_add(1)); String name = make_string((u8 *)name_text, name_len-1); Type *type = type_of_expr(expr); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 4c4d9703d..6e785d492 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -164,7 +164,7 @@ struct lbModule { PtrMap map_get_procs; PtrMap map_set_procs; - u32 nested_type_name_guid; + std::atomic nested_type_name_guid; Array procedures_to_generate; Array global_procedures_and_types_to_create; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index e5f3e3081..176e53042 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1426,7 +1426,7 @@ gb_internal String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedur if (p != nullptr) { isize name_len = p->name.len + 1 + ts_name.len + 1 + 10 + 1; char *name_text = gb_alloc_array(permanent_allocator(), char, name_len); - u32 guid = ++p->module->nested_type_name_guid; + u32 guid = 1+p->module->nested_type_name_guid.fetch_add(1); name_len = gb_snprintf(name_text, name_len, "%.*s.%.*s-%u", LIT(p->name), LIT(ts_name), guid); String name = make_string(cast(u8 *)name_text, name_len-1); @@ -1436,9 +1436,8 @@ gb_internal String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedur // NOTE(bill): a nested type be required before its parameter procedure exists. Just give it a temp name for now isize name_len = 9 + 1 + ts_name.len + 1 + 10 + 1; char *name_text = gb_alloc_array(permanent_allocator(), char, name_len); - static u32 guid = 0; - guid += 1; - name_len = gb_snprintf(name_text, name_len, "_internal.%.*s-%u", LIT(ts_name), guid); + static std::atomic guid; + name_len = gb_snprintf(name_text, name_len, "_internal.%.*s-%u", LIT(ts_name), 1+guid.fetch_add(1)); String name = make_string(cast(u8 *)name_text, name_len-1); e->TypeName.ir_mangled_name = name; From d38ea63c781c1f0673ad09525582e5bb21ea3e65 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Mon, 12 Jun 2023 15:37:39 +0200 Subject: [PATCH 028/144] fix fcntl binding on darwin --- core/os/os_darwin.odin | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin index adb438f60..ab05756ae 100644 --- a/core/os/os_darwin.odin +++ b/core/os/os_darwin.odin @@ -440,7 +440,7 @@ foreign libc { @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int --- @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) --- - @(link_name="__fcntl") _unix__fcntl :: proc(fd: Handle, cmd: c.int, #c_vararg args: ..any) -> c.int --- + @(link_name="__fcntl") _unix__fcntl :: proc(fd: Handle, cmd: c.int, arg: uintptr) -> c.int --- @(link_name="rename") _unix_rename :: proc(old: cstring, new: cstring) -> c.int --- @(link_name="remove") _unix_remove :: proc(path: cstring) -> c.int --- @@ -794,14 +794,14 @@ _readlink :: proc(path: string) -> (string, Errno) { } absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { - buf : [256]byte - res := _unix__fcntl(fd, F_GETPATH, &buf[0]) - if res != 0 { - return "", Errno(get_last_error()) + buf: [DARWIN_MAXPATHLEN]byte + _, err := fcntl(int(fd), F_GETPATH, int(uintptr(&buf[0]))) + if err != ERROR_NONE { + return "", err } path := strings.clone_from_cstring(cstring(&buf[0])) - return path, ERROR_NONE + return path, err } absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { @@ -1068,7 +1068,7 @@ shutdown :: proc(sd: Socket, how: int) -> (Errno) { } fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Errno) { - result := _unix__fcntl(Handle(fd), c.int(cmd), c.int(arg)) + result := _unix__fcntl(Handle(fd), c.int(cmd), uintptr(arg)) if result < 0 { return 0, Errno(get_last_error()) } From 52a926dd903b85a1784726e1beb0ea830c302b5f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 12 Jun 2023 14:49:00 +0100 Subject: [PATCH 029/144] Deprecate `-disallow-rtti` in favour of `-no-rtti` --- src/main.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 162cd309e..029c9f896 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -675,7 +675,7 @@ enum BuildFlagKind { BuildFlag_StrictStyle, BuildFlag_StrictStyleInitOnly, BuildFlag_ForeignErrorProcedures, - BuildFlag_DisallowRTTI, + BuildFlag_NoRTTI, BuildFlag_DynamicMapCalls, BuildFlag_Compact, @@ -854,7 +854,8 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_StrictStyleInitOnly, str_lit("strict-style-init-only"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ForeignErrorProcedures, str_lit("foreign-error-procedures"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_DisallowRTTI, str_lit("disallow-rtti"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_NoRTTI, str_lit("no-rtti"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_NoRTTI, str_lit("disallow-rtti"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_DynamicMapCalls, str_lit("dynamic-map-calls"), BuildFlagParam_None, Command__does_check); @@ -1448,7 +1449,11 @@ gb_internal bool parse_build_flags(Array args) { case BuildFlag_DisallowDo: build_context.disallow_do = true; break; - case BuildFlag_DisallowRTTI: + case BuildFlag_NoRTTI: + if (name == "disallow-rtti") { + gb_printf_err("'-disallow-rtti' has been replaced with '-no-rtti'\n"); + bad_flags = true; + } build_context.disallow_rtti = true; break; case BuildFlag_DynamicMapCalls: From 296674e18b5ce3ba90013fb5d599251c66d8bd18 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 12 Jun 2023 14:53:05 +0100 Subject: [PATCH 030/144] Rename `ODIN_DISALLOW_RTTI` to `ODIN_NO_RTTI`; Remove dead command line flags --- core/reflect/reflect.odin | 2 +- core/runtime/core.odin | 2 +- core/runtime/error_checks.odin | 2 +- core/runtime/print.odin | 4 ++-- src/build_settings.cpp | 6 +++--- src/check_builtin.cpp | 4 ++-- src/checker.cpp | 8 ++++---- src/llvm_backend.cpp | 4 ++-- src/llvm_backend_expr.cpp | 6 +++--- src/llvm_backend_type.cpp | 6 +++--- src/llvm_backend_utility.cpp | 8 ++++---- src/main.cpp | 16 +--------------- 12 files changed, 27 insertions(+), 41 deletions(-) diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index f8343ead2..2eb31c21b 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -132,7 +132,7 @@ type_info_core :: runtime.type_info_core type_info_base_without_enum :: type_info_core -when !ODIN_DISALLOW_RTTI { +when !ODIN_NO_RTTI { typeid_base :: runtime.typeid_base typeid_core :: runtime.typeid_core typeid_base_without_enum :: typeid_core diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 058ca6161..83504c9ee 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -566,7 +566,7 @@ __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check return &type_table[n] } -when !ODIN_DISALLOW_RTTI { +when !ODIN_NO_RTTI { typeid_base :: proc "contextless" (id: typeid) -> typeid { ti := type_info_of(id) ti = type_info_base(ti) diff --git a/core/runtime/error_checks.odin b/core/runtime/error_checks.odin index 8539c724d..a667b9065 100644 --- a/core/runtime/error_checks.odin +++ b/core/runtime/error_checks.odin @@ -122,7 +122,7 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32 } -when ODIN_DISALLOW_RTTI { +when ODIN_NO_RTTI { type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32) { if ok { return diff --git a/core/runtime/print.odin b/core/runtime/print.odin index 326a29667..732ed9c12 100644 --- a/core/runtime/print.odin +++ b/core/runtime/print.odin @@ -5,7 +5,7 @@ _INTEGER_DIGITS :: "0123456789abcdefghijklmnopqrstuvwxyz" @(private="file") _INTEGER_DIGITS_VAR := _INTEGER_DIGITS -when !ODIN_DISALLOW_RTTI { +when !ODIN_NO_RTTI { print_any_single :: proc "contextless" (arg: any) { x := arg if loc, ok := x.(Source_Code_Location); ok { @@ -234,7 +234,7 @@ print_caller_location :: proc "contextless" (using loc: Source_Code_Location) { } } print_typeid :: proc "contextless" (id: typeid) { - when ODIN_DISALLOW_RTTI { + when ODIN_NO_RTTI { if id == nil { print_string("nil") } else { diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 92e0df38b..866631f9a 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -309,7 +309,7 @@ struct BuildContext { bool copy_file_contents; - bool disallow_rtti; + bool no_rtti; bool dynamic_map_calls; @@ -1227,8 +1227,8 @@ gb_internal void init_build_context(TargetMetrics *cross_target) { if (bc->metrics.os == TargetOs_freestanding) { bc->no_entry_point = true; } else { - if (bc->disallow_rtti) { - gb_printf_err("-disallow-rtti is only allowed on freestanding targets\n"); + if (bc->no_rtti) { + gb_printf_err("-no-rtti is only allowed on freestanding targets\n"); gb_exit(1); } } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 46ee6b7f9..269a0ec48 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2063,7 +2063,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As if (c->scope->flags&ScopeFlag_Global) { compiler_error("'type_info_of' Cannot be declared within the runtime package due to how the internals of the compiler works"); } - if (build_context.disallow_rtti) { + if (build_context.no_rtti) { error(call, "'%.*s' has been disallowed", LIT(builtin_name)); return false; } @@ -2106,7 +2106,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As if (c->scope->flags&ScopeFlag_Global) { compiler_error("'typeid_of' Cannot be declared within the runtime package due to how the internals of the compiler works"); } - if (build_context.disallow_rtti) { + if (build_context.no_rtti) { error(call, "'%.*s' has been disallowed", LIT(builtin_name)); return false; } diff --git a/src/checker.cpp b/src/checker.cpp index a8227fc2e..354cdadd3 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -32,7 +32,7 @@ gb_internal bool is_operand_uninit(Operand o) { } gb_internal bool check_rtti_type_disallowed(Token const &token, Type *type, char const *format) { - if (build_context.disallow_rtti && type) { + if (build_context.no_rtti && type) { if (is_type_any(type)) { gbString t = type_to_string(type); error(token, format, t); @@ -1054,7 +1054,7 @@ gb_internal void init_universal(void) { add_global_bool_constant("ODIN_TEST", bc->command_kind == Command_test); add_global_bool_constant("ODIN_NO_ENTRY_POINT", bc->no_entry_point); add_global_bool_constant("ODIN_FOREIGN_ERROR_PROCEDURES", bc->ODIN_FOREIGN_ERROR_PROCEDURES); - add_global_bool_constant("ODIN_DISALLOW_RTTI", bc->disallow_rtti); + add_global_bool_constant("ODIN_NO_RTTI", bc->no_rtti); add_global_bool_constant("ODIN_VALGRIND_SUPPORT", bc->ODIN_VALGRIND_SUPPORT); @@ -1742,7 +1742,7 @@ gb_internal void add_implicit_entity(CheckerContext *c, Ast *clause, Entity *e) gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t); gb_internal void add_type_info_type(CheckerContext *c, Type *t) { - if (build_context.disallow_rtti) { + if (build_context.no_rtti) { return; } if (t == nullptr) { @@ -2343,7 +2343,7 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) { str_lit("__multi3"), ); - FORCE_ADD_RUNTIME_ENTITIES(!build_context.disallow_rtti, + FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_rtti, // Odin types str_lit("Type_Info"), diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 4fe8d4a00..525ac8a9d 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1048,7 +1048,7 @@ struct lbGlobalVariable { }; gb_internal lbProcedure *lb_create_startup_type_info(lbModule *m) { - if (build_context.disallow_rtti) { + if (build_context.no_rtti) { return nullptr; } Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl); @@ -2170,7 +2170,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { TIME_SECTION("LLVM Global Variables"); - if (!build_context.disallow_rtti) { + if (!build_context.no_rtti) { lbModule *m = default_module; { // Add type info data diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index f95e351ce..619b40fda 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2984,7 +2984,7 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { isize arg_count = 6; - if (build_context.disallow_rtti) { + if (build_context.no_rtti) { arg_count = 4; } @@ -2996,7 +2996,7 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { args[2] = lb_const_int(p->module, t_i32, pos.line); args[3] = lb_const_int(p->module, t_i32, pos.column); - if (!build_context.disallow_rtti) { + if (!build_context.no_rtti) { args[4] = lb_typeid(p->module, src_type); args[5] = lb_typeid(p->module, dst_type); } @@ -3012,7 +3012,7 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { } lbValue data_ptr = lb_emit_struct_ev(p, v, 0); if ((p->state_flags & StateFlag_no_type_assert) == 0) { - GB_ASSERT(!build_context.disallow_rtti); + GB_ASSERT(!build_context.no_rtti); lbValue any_id = lb_emit_struct_ev(p, v, 1); diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index 1e26fd6bd..a54fde748 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -15,7 +15,7 @@ gb_internal isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_ } gb_internal lbValue lb_typeid(lbModule *m, Type *type) { - GB_ASSERT(!build_context.disallow_rtti); + GB_ASSERT(!build_context.no_rtti); type = default_type(type); @@ -92,7 +92,7 @@ gb_internal lbValue lb_typeid(lbModule *m, Type *type) { } gb_internal lbValue lb_type_info(lbModule *m, Type *type) { - GB_ASSERT(!build_context.disallow_rtti); + GB_ASSERT(!build_context.no_rtti); type = default_type(type); @@ -141,7 +141,7 @@ gb_internal lbValue lb_type_info_member_tags_offset(lbProcedure *p, isize count) gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data - if (build_context.disallow_rtti) { + if (build_context.no_rtti) { return; } diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 0c26382ed..2ecad1703 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -721,7 +721,7 @@ gb_internal lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type Type *dst_type = tuple->Tuple.variables[0]->type; isize arg_count = 7; - if (build_context.disallow_rtti) { + if (build_context.no_rtti) { arg_count = 4; } @@ -733,7 +733,7 @@ gb_internal lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type args[2] = lb_const_int(m, t_i32, pos.line); args[3] = lb_const_int(m, t_i32, pos.column); - if (!build_context.disallow_rtti) { + if (!build_context.no_rtti) { args[4] = lb_typeid(m, src_type); args[5] = lb_typeid(m, dst_type); args[6] = lb_emit_conv(p, value_, t_rawptr); @@ -797,7 +797,7 @@ gb_internal lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *ty lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1)); isize arg_count = 7; - if (build_context.disallow_rtti) { + if (build_context.no_rtti) { arg_count = 4; } auto args = array_make(permanent_allocator(), arg_count); @@ -807,7 +807,7 @@ gb_internal lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *ty args[2] = lb_const_int(m, t_i32, pos.line); args[3] = lb_const_int(m, t_i32, pos.column); - if (!build_context.disallow_rtti) { + if (!build_context.no_rtti) { args[4] = any_typeid; args[5] = dst_typeid; args[6] = lb_emit_struct_ev(p, value, 0); diff --git a/src/main.cpp b/src/main.cpp index 029c9f896..2198b4536 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -655,7 +655,6 @@ enum BuildFlagKind { BuildFlag_ShowDebugMessages, BuildFlag_Vet, BuildFlag_VetExtra, - BuildFlag_UseLLVMApi, BuildFlag_IgnoreUnknownAttributes, BuildFlag_ExtraLinkerFlags, BuildFlag_ExtraAssemblerFlags, @@ -671,7 +670,6 @@ enum BuildFlagKind { BuildFlag_DisallowDo, BuildFlag_DefaultToNilAllocator, - BuildFlag_InsertSemicolon, BuildFlag_StrictStyle, BuildFlag_StrictStyleInitOnly, BuildFlag_ForeignErrorProcedures, @@ -834,7 +832,6 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetExtra, str_lit("vet-extra"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags, str_lit("extra-assembler-flags"), BuildFlagParam_String, Command__does_build); @@ -849,7 +846,6 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_InsertSemicolon, str_lit("insert-semicolon"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_StrictStyle, str_lit("strict-style"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_StrictStyleInitOnly, str_lit("strict-style-init-only"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ForeignErrorProcedures, str_lit("foreign-error-procedures"), BuildFlagParam_None, Command__does_check); @@ -1373,11 +1369,6 @@ gb_internal bool parse_build_flags(Array args) { build_context.vet_extra = true; break; } - case BuildFlag_UseLLVMApi: { - gb_printf_err("-llvm-api flag is not required any more\n"); - bad_flags = true; - break; - } case BuildFlag_IgnoreUnknownAttributes: build_context.ignore_unknown_attributes = true; break; @@ -1454,7 +1445,7 @@ gb_internal bool parse_build_flags(Array args) { gb_printf_err("'-disallow-rtti' has been replaced with '-no-rtti'\n"); bad_flags = true; } - build_context.disallow_rtti = true; + build_context.no_rtti = true; break; case BuildFlag_DynamicMapCalls: build_context.dynamic_map_calls = true; @@ -1465,11 +1456,6 @@ gb_internal bool parse_build_flags(Array args) { case BuildFlag_ForeignErrorProcedures: build_context.ODIN_FOREIGN_ERROR_PROCEDURES = true; break; - case BuildFlag_InsertSemicolon: { - gb_printf_err("-insert-semicolon flag is not required any more\n"); - bad_flags = true; - break; - } case BuildFlag_StrictStyle: { if (build_context.strict_style_init_only) { gb_printf_err("-strict-style and -strict-style-init-only cannot be used together\n"); From 10f2136675f3437c0f74243bd45ed09d2d9a1cea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Rey=20M=C3=A9ndez?= Date: Mon, 12 Jun 2023 19:28:41 -0300 Subject: [PATCH 031/144] fix: read file from windows was not returning platform error correctly --- core/os/os2/file_windows.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index e4ae4856a..bc1530dc7 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -287,7 +287,7 @@ _read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { } } - return int(total_read), nil + return int(total_read), err } _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { From 581eebb197cc9905248d59a084a68bd2e64c9830 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 13 Jun 2023 13:14:59 +0100 Subject: [PATCH 032/144] Fix a race condition when produced anonymous procedure literals with `-use-separate-modules` --- src/checker.hpp | 3 ++ src/llvm_backend.cpp | 53 +------------------- src/llvm_backend.hpp | 4 +- src/llvm_backend_const.cpp | 1 + src/llvm_backend_general.cpp | 93 +++++++++++++++++++++++++++++++++++- 5 files changed, 99 insertions(+), 55 deletions(-) diff --git a/src/checker.hpp b/src/checker.hpp index 1a95e2772..b06d0a8f9 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -199,6 +199,9 @@ struct DeclInfo { BlockingMutex type_and_value_mutex; Array labels; + + // NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time + struct lbModule *code_gen_module; }; // ProcInfo stores the information needed for checking a procedure diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 525ac8a9d..730610ad9 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -772,56 +772,6 @@ gb_internal lbValue lb_map_set_proc_for_type(lbModule *m, Type *type) { return {p->value, p->type}; } - -gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) { - mutex_lock(&m->gen->anonymous_proc_lits_mutex); - defer (mutex_unlock(&m->gen->anonymous_proc_lits_mutex)); - - lbProcedure **found = map_get(&m->gen->anonymous_proc_lits, expr); - if (found) { - return lb_find_procedure_value_from_entity(m, (*found)->entity); - } - - ast_node(pl, ProcLit, expr); - - // NOTE(bill): Generate a new name - // parent$count - isize name_len = prefix_name.len + 6 + 11; - char *name_text = gb_alloc_array(permanent_allocator(), char, name_len); - static std::atomic name_id; - name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), 1+name_id.fetch_add(1)); - String name = make_string((u8 *)name_text, name_len-1); - - Type *type = type_of_expr(expr); - - Token token = {}; - token.pos = ast_token(expr).pos; - token.kind = Token_Ident; - token.string = name; - Entity *e = alloc_entity_procedure(nullptr, token, type, pl->tags); - e->file = expr->file(); - e->decl_info = pl->decl; - e->code_gen_module = m; - e->flags |= EntityFlag_ProcBodyChecked; - lbProcedure *p = lb_create_procedure(m, e); - - lbValue value = {}; - value.value = p->value; - value.type = p->type; - - array_add(&m->procedures_to_generate, p); - if (parent != nullptr) { - array_add(&parent->children, p); - } else { - string_map_set(&m->members, name, value); - } - - map_set(&m->gen->anonymous_proc_lits, expr, p); - - return value; -} - - gb_internal lbValue lb_gen_map_cell_info_ptr(lbModule *m, Type *type) { lbAddr *found = map_get(&m->map_cell_info_map, type); if (found) { @@ -1513,7 +1463,7 @@ gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc lbModule *m = cast(lbModule *)data; for (isize i = 0; i < m->missing_procedures_to_check.count; i++) { lbProcedure *p = m->missing_procedures_to_check[i]; - debugf("Generate missing procedure: %.*s\n", LIT(p->name)); + debugf("Generate missing procedure: %.*s module %p\n", LIT(p->name), m); lb_generate_procedure(m, p); } return 0; @@ -1577,7 +1527,6 @@ gb_internal void lb_llvm_module_passes(lbGenerator *gen, bool do_threading) { thread_pool_wait(); } - gb_internal String lb_filepath_ll_for_module(lbModule *m) { String path = concatenate3_strings(permanent_allocator(), build_context.build_paths[BuildPath_Output].basename, diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 6e785d492..3aa13b488 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -201,7 +201,7 @@ struct lbGenerator { PtrMap modules_through_ctx; lbModule default_module; - BlockingMutex anonymous_proc_lits_mutex; + RecursiveMutex anonymous_proc_lits_mutex; PtrMap anonymous_proc_lits; BlockingMutex foreign_mutex; @@ -545,6 +545,8 @@ gb_internal gb_inline i64 lb_max_zero_init_size(void) { gb_internal LLVMTypeRef OdinLLVMGetArrayElementType(LLVMTypeRef type); gb_internal LLVMTypeRef OdinLLVMGetVectorElementType(LLVMTypeRef type); +gb_internal String lb_filepath_ll_for_module(lbModule *m); + #define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime" #define LB_CLEANUP_RUNTIME_PROC_NAME "__$cleanup_runtime" #define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info" diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index c9d2f5b26..efe1e4d45 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -473,6 +473,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo if (value.kind == ExactValue_Procedure) { lbValue res = {}; Ast *expr = unparen_expr(value.value_procedure); + GB_ASSERT(expr != nullptr); if (expr->kind == Ast_ProcLit) { res = lb_generate_anonymous_proc_lit(m, str_lit("_proclit"), expr); } else { diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 176e53042..9333f13a4 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -334,10 +334,35 @@ gb_internal bool lb_is_instr_terminating(LLVMValueRef instr) { return false; } +gb_internal lbModule *lb_module_of_expr(lbGenerator *gen, Ast *expr) { + GB_ASSERT(expr != nullptr); + lbModule **found = nullptr; + AstFile *file = expr->file(); + if (file) { + found = map_get(&gen->modules, cast(void *)file); + if (found) { + return *found; + } + + if (file->pkg) { + found = map_get(&gen->modules, cast(void *)file->pkg); + if (found) { + return *found; + } + } + } + + return &gen->default_module; +} gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e) { GB_ASSERT(e != nullptr); lbModule **found = nullptr; + if (e->kind == Entity_Procedure && + e->decl_info && + e->decl_info->code_gen_module) { + return e->decl_info->code_gen_module; + } if (e->file) { found = map_get(&gen->modules, cast(void *)e->file); if (found) { @@ -2661,9 +2686,12 @@ gb_internal lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *e gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) { + lbGenerator *gen = m->gen; + GB_ASSERT(is_type_proc(e->type)); e = strip_entity_wrapping(e); GB_ASSERT(e != nullptr); + GB_ASSERT(e->kind == Entity_Procedure); lbValue *found = nullptr; rw_mutex_shared_lock(&m->values_mutex); @@ -2677,20 +2705,24 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) lbModule *other_module = m; if (USE_SEPARATE_MODULES) { - other_module = lb_module_of_entity(m->gen, e); + other_module = lb_module_of_entity(gen, e); } if (other_module == m) { - debugf("Missing Procedure (lb_find_procedure_value_from_entity): %.*s\n", LIT(e->token.string)); + debugf("Missing Procedure (lb_find_procedure_value_from_entity): %.*s module %p\n", LIT(e->token.string), m); } ignore_body = other_module != m; lbProcedure *missing_proc = lb_create_procedure(m, e, ignore_body); if (ignore_body) { + mutex_lock(&gen->anonymous_proc_lits_mutex); + defer (mutex_unlock(&gen->anonymous_proc_lits_mutex)); + GB_ASSERT(other_module != nullptr); rw_mutex_shared_lock(&other_module->values_mutex); auto *found = map_get(&other_module->values, e); rw_mutex_shared_unlock(&other_module->values_mutex); if (found == nullptr) { + // THIS IS THE RACE CONDITION lbProcedure *missing_proc_in_other_module = lb_create_procedure(other_module, e, false); array_add(&other_module->missing_procedures_to_check, missing_proc_in_other_module); } @@ -2707,6 +2739,63 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) } + +gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) { + lbGenerator *gen = m->gen; + + mutex_lock(&gen->anonymous_proc_lits_mutex); + defer (mutex_unlock(&gen->anonymous_proc_lits_mutex)); + + TokenPos pos = ast_token(expr).pos; + lbProcedure **found = map_get(&gen->anonymous_proc_lits, expr); + if (found) { + return lb_find_procedure_value_from_entity(m, (*found)->entity); + } + + ast_node(pl, ProcLit, expr); + + // NOTE(bill): Generate a new name + // parent$count + isize name_len = prefix_name.len + 6 + 11; + char *name_text = gb_alloc_array(permanent_allocator(), char, name_len); + static std::atomic name_id; + name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), 1+name_id.fetch_add(1)); + String name = make_string((u8 *)name_text, name_len-1); + + Type *type = type_of_expr(expr); + + GB_ASSERT(pl->decl->entity == nullptr); + Token token = {}; + token.pos = ast_token(expr).pos; + token.kind = Token_Ident; + token.string = name; + Entity *e = alloc_entity_procedure(nullptr, token, type, pl->tags); + e->file = expr->file(); + + // NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time + pl->decl->code_gen_module = m; + e->decl_info = pl->decl; + pl->decl->entity = e; + e->flags |= EntityFlag_ProcBodyChecked; + + lbProcedure *p = lb_create_procedure(m, e); + GB_ASSERT(e->code_gen_module == m); + + lbValue value = {}; + value.value = p->value; + value.type = p->type; + + map_set(&gen->anonymous_proc_lits, expr, p); + array_add(&m->procedures_to_generate, p); + if (parent != nullptr) { + array_add(&parent->children, p); + } else { + string_map_set(&m->members, name, value); + } + return value; +} + + gb_internal lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value, Entity **entity_) { GB_ASSERT(type != nullptr); type = default_type(type); From fc4a5e61c27467b30a421bceb7d21ac29ef8468e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 13 Jun 2023 13:21:15 +0100 Subject: [PATCH 033/144] Add `ODIN_IGNORE_MSVC_CHECK` check to build.bat --- build.bat | 20 +++++++++++--------- misc/shell.bat | 2 ++ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/build.bat b/build.bat index 99c3ad2ee..b7537fba6 100644 --- a/build.bat +++ b/build.bat @@ -3,18 +3,20 @@ setlocal EnableDelayedExpansion where /Q cl.exe || ( - set __VSCMD_ARG_NO_LOGO=1 - for /f "tokens=*" %%i in ('"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath') do set VS=%%i - if "!VS!" equ "" ( - echo ERROR: Visual Studio installation not found - exit /b 1 - ) - call "!VS!\VC\Auxiliary\Build\vcvarsall.bat" amd64 || exit /b 1 + set __VSCMD_ARG_NO_LOGO=1 + for /f "tokens=*" %%i in ('"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath') do set VS=%%i + if "!VS!" equ "" ( + echo ERROR: Visual Studio installation not found + exit /b 1 + ) + call "!VS!\VC\Auxiliary\Build\vcvarsall.bat" amd64 || exit /b 1 ) if "%VSCMD_ARG_TGT_ARCH%" neq "x64" ( - echo ERROR: please run this from MSVC x64 native tools command prompt, 32-bit target is not supported! - exit /b 1 + if "%ODIN_IGNORE_MSVC_CHECK%" == "" ( + echo ERROR: please run this from MSVC x64 native tools command prompt, 32-bit target is not supported! + exit /b 1 + ) ) for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime ( diff --git a/misc/shell.bat b/misc/shell.bat index 60f603bc1..bfb444396 100644 --- a/misc/shell.bat +++ b/misc/shell.bat @@ -7,5 +7,7 @@ rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxil rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 1> NUL set _NO_DEBUG_HEAP=1 +set ODIN_IGNORE_MSVC_CHECK=1 + rem set path=w:\Odin\misc;%path% cls From ec32967daab5136fc2518f4068e7007e265c4e92 Mon Sep 17 00:00:00 2001 From: finn Date: Tue, 13 Jun 2023 22:07:01 +0200 Subject: [PATCH 034/144] [check-type] fix faulty #no_nil variants error - when checking the variants of a union type we will skip adding the variants if we have an unspecialized polymorphic, hence our union_type variants will be empty and have a count of 0 - so when checking if we violate the #no_nil error, if we are in the unspecialized polymorphic case and there exists at least one variant in the original variants then we should not raise this error - test checks that we do not raise the error anymore, and that we still detect the #no_nil error in the described circumstances --- src/check_type.cpp | 6 ++++++ tests/issues/run.sh | 7 +++++++ tests/issues/test_issue_2395.odin | 29 +++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 tests/issues/test_issue_2395.odin diff --git a/src/check_type.cpp b/src/check_type.cpp index bbfc25a12..a69dcdadc 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -729,6 +729,12 @@ gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *no union_type->Union.kind = ut->kind; switch (ut->kind) { case UnionType_no_nil: + if (union_type->Union.is_polymorphic && poly_operands == nullptr) { + GB_ASSERT(variants.count == 0); + if (ut->variants.count != 1) { + break; + } + } if (variants.count < 2) { error(ut->align, "A union with #no_nil must have at least 2 variants"); } diff --git a/tests/issues/run.sh b/tests/issues/run.sh index bbcd6fb28..b0c43572f 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -6,6 +6,8 @@ pushd build ODIN=../../../odin COMMON="-collection:tests=../.." +NO_NIL_ERR="Error: " + set -x $ODIN test ../test_issue_829.odin $COMMON -file @@ -14,6 +16,11 @@ $ODIN test ../test_issue_2056.odin $COMMON -file $ODIN test ../test_issue_2087.odin $COMMON -file $ODIN build ../test_issue_2113.odin $COMMON -file -debug $ODIN test ../test_issue_2466.odin $COMMON -file +if [[ $($ODIN build ../test_issue_2395.odin $COMMON -file 2>&1 >/dev/null | grep -c "$NO_NIL_ERR") -eq 2 ]] ; then + echo "SUCCESSFUL 1/1" +else + echo "SUCCESSFUL 0/1" +fi set +x diff --git a/tests/issues/test_issue_2395.odin b/tests/issues/test_issue_2395.odin new file mode 100644 index 000000000..48e1ee516 --- /dev/null +++ b/tests/issues/test_issue_2395.odin @@ -0,0 +1,29 @@ +// Tests issue #2395 https://github.com/odin-lang/Odin/issues/2395 + +// Ensures that we no longer raise the faulty error for #no_nil unions when +// then are 2 variants with the polymorphic type. Also ensure that we raise +// exactly 2 errors from the invalid unions +package test_issues + +import "core:testing" + +ValidUnion :: union($T: typeid) #no_nil { + T, + f32, +} + +OtherValidUnion :: union($T: typeid, $S: typeid) #no_nil { + T, + S, +} + +InvalidUnion :: union($T: typeid) #no_nil { + T, +} + +OtherInvalidUnion :: union($T: typeid) #no_nil { + u8, +} + +main :: proc() { +} From a78d6fe0b39d57f002a397d54bb506df7a3d2f1b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 14 Jun 2023 12:31:57 +0100 Subject: [PATCH 035/144] Use `i64` instead of `int` for internal procedures --- core/os/os2/errors_windows.odin | 8 +++++-- core/os/os2/file.odin | 40 ++++++++++++++++++++++++++------- core/os/os2/file_linux.odin | 32 ++++++++++++-------------- core/os/os2/file_windows.odin | 36 +++++++++++++---------------- 4 files changed, 68 insertions(+), 48 deletions(-) diff --git a/core/os/os2/errors_windows.odin b/core/os/os2/errors_windows.odin index 27c16e72e..6500e7ccc 100644 --- a/core/os/os2/errors_windows.odin +++ b/core/os/os2/errors_windows.odin @@ -37,14 +37,18 @@ _get_platform_error :: proc() -> Error { case win32.ERROR_NOT_SUPPORTED: return .Unsupported + case win32.ERROR_HANDLE_EOF: + return .EOF + + case win32.ERROR_INVALID_HANDLE: + return .Invalid_File + case win32.ERROR_BAD_ARGUMENTS, win32.ERROR_INVALID_PARAMETER, win32.ERROR_NOT_ENOUGH_MEMORY, - win32.ERROR_INVALID_HANDLE, win32.ERROR_NO_MORE_FILES, win32.ERROR_LOCK_VIOLATION, - win32.ERROR_HANDLE_EOF, win32.ERROR_BROKEN_PIPE, win32.ERROR_CALL_NOT_IMPLEMENTED, win32.ERROR_INSUFFICIENT_BUFFER, diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin index 4113675ca..da822374a 100644 --- a/core/os/os2/file.odin +++ b/core/os/os2/file.odin @@ -71,35 +71,59 @@ name :: proc(f: ^File) -> string { } close :: proc(f: ^File) -> Error { - return io.close(f.impl.stream) + if f != nil { + return io.close(f.impl.stream) + } + return nil } seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) { - return io.seek(f.impl.stream, offset, whence) + if f != nil { + return io.seek(f.impl.stream, offset, whence) + } + return 0, .Invalid_File } read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { - return io.read(f.impl.stream, p) + if f != nil { + return io.read(f.impl.stream, p) + } + return 0, .Invalid_File } read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { - return io.read_at(f.impl.stream, p, offset) + if f != nil { + return io.read_at(f.impl.stream, p, offset) + } + return 0, .Invalid_File } write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { - return io.write(f.impl.stream, p) + if f != nil { + return io.write(f.impl.stream, p) + } + return 0, .Invalid_File } write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { - return io.write_at(f.impl.stream, p, offset) + if f != nil { + return io.write_at(f.impl.stream, p, offset) + } + return 0, .Invalid_File } file_size :: proc(f: ^File) -> (n: i64, err: Error) { - return io.size(f.impl.stream) + if f != nil { + return io.size(f.impl.stream) + } + return 0, .Invalid_File } flush :: proc(f: ^File) -> Error { - return io.flush(f.impl.stream) + if f != nil { + return io.flush(f.impl.stream) + } + return nil } sync :: proc(f: ^File) -> Error { diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index 8df19614f..ddd827bce 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -116,18 +116,18 @@ _seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Er return res, nil } -_read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { +_read :: proc(f: ^File, p: []byte) -> (i64, Error) { if len(p) == 0 { return 0, nil } - n = unix.sys_read(f.impl.fd, &p[0], len(p)) + n := unix.sys_read(f.impl.fd, &p[0], len(p)) if n < 0 { return -1, _get_platform_error(n) } - return n, nil + return i64(n), nil } -_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { +_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) { if offset < 0 { return 0, .Invalid_Offset } @@ -138,25 +138,25 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { if m < 0 { return -1, _get_platform_error(m) } - n += m + n += i64(m) b = b[m:] offset += i64(m) } return } -_write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { +_write :: proc(f: ^File, p: []byte) -> (i64, Error) { if len(p) == 0 { return 0, nil } - n = unix.sys_write(f.impl.fd, &p[0], uint(len(p))) + n := unix.sys_write(f.impl.fd, &p[0], uint(len(p))) if n < 0 { return -1, _get_platform_error(n) } - return int(n), nil + return i64(n), nil } -_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { +_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) { if offset < 0 { return 0, .Invalid_Offset } @@ -167,7 +167,7 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { if m < 0 { return -1, _get_platform_error(m) } - n += m + n += i64(m) b = b[m:] offset += i64(m) } @@ -371,23 +371,19 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, i: int switch mode { case .Read: - i, ferr = _read(f, p) - n = i64(i) + n, ferr = _read(f, p) err = error_to_io_error(ferr) return case .Read_At: - i, ferr = _read_at(f, p, offset) - n = i64(i) + n, ferr = _read_at(f, p, offset) err = error_to_io_error(ferr) return case .Write: - i, ferr = _write(f, p) - n = i64(i) + n, ferr = _write(f, p) err = error_to_io_error(ferr) return case .Write_At: - i, ferr = _write_at(f, p, offset) - n = i64(i) + n, ferr = _write_at(f, p, offset) err = error_to_io_error(ferr) return case .Seek: diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index bb9d983b9..600ecde21 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -215,7 +215,7 @@ _seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Er return i64(hi)<<32 + i64(dw_ptr), nil } -_read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { +_read :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) { read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) { if len(b) == 0 { return 0, nil @@ -281,7 +281,7 @@ _read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { n, err := read_console(handle, p[total_read:][:to_read]) total_read += n if err != nil { - return int(total_read), err + return i64(total_read), err } } else { ok = win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil) @@ -294,11 +294,11 @@ _read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { } } - return int(total_read), err + return i64(total_read), err } -_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { - pread :: proc(f: ^File, data: []byte, offset: i64) -> (n: int, err: Error) { +_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) { + pread :: proc(f: ^File, data: []byte, offset: i64) -> (n: i64, err: Error) { buf := data if len(buf) > MAX_RW { buf = buf[:MAX_RW] @@ -320,7 +320,7 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { err = _get_platform_error() done = 0 } - n = int(done) + n = i64(done) return } @@ -336,7 +336,7 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { return } -_write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { +_write :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) { if len(p) == 0 { return } @@ -354,17 +354,17 @@ _write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { e := win32.WriteFile(handle, &p[total_write], to_write, &single_write_length, nil) if single_write_length <= 0 || !e { - n = int(total_write) + n = i64(total_write) err = _get_platform_error() return } total_write += i64(single_write_length) } - return int(total_write), nil + return i64(total_write), nil } -_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { - pwrite :: proc(f: ^File, data: []byte, offset: i64) -> (n: int, err: Error) { +_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) { + pwrite :: proc(f: ^File, data: []byte, offset: i64) -> (n: i64, err: Error) { buf := data if len(buf) > MAX_RW { buf = buf[:MAX_RW] @@ -384,7 +384,7 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { err = _get_platform_error() done = 0 } - n = int(done) + n = i64(done) return } @@ -733,23 +733,19 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, i: int switch mode { case .Read: - i, ferr = _read(f, p) - n = i64(i) + n, ferr = _read(f, p) err = error_to_io_error(ferr) return case .Read_At: - i, ferr = _read_at(f, p, offset) - n = i64(i) + n, ferr = _read_at(f, p, offset) err = error_to_io_error(ferr) return case .Write: - i, ferr = _write(f, p) - n = i64(i) + n, ferr = _write(f, p) err = error_to_io_error(ferr) return case .Write_At: - i, ferr = _write_at(f, p, offset) - n = i64(i) + n, ferr = _write_at(f, p, offset) err = error_to_io_error(ferr) return case .Seek: From 3a761395bee4f14c81682d4c21a5aa1e9565959a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 14 Jun 2023 12:49:33 +0100 Subject: [PATCH 036/144] Add basic optimization for comparisons against the empty string `""` --- src/llvm_backend_expr.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 619b40fda..68ad9e6e0 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1325,6 +1325,15 @@ handle_op:; return {}; } +gb_internal bool lb_is_empty_string_constant(Ast *expr) { + if (expr->tav.value.kind == ExactValue_String && + is_type_string(expr->tav.type)) { + String s = expr->tav.value.value_string; + return s.len == 0; + } + return false; +} + gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { ast_node(be, BinaryExpr, expr); @@ -1373,15 +1382,29 @@ gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { case Token_CmpEq: case Token_NotEq: if (is_type_untyped_nil(be->right->tav.type)) { + // `x == nil` or `x != nil` lbValue left = lb_build_expr(p, be->left); lbValue cmp = lb_emit_comp_against_nil(p, be->op.kind, left); Type *type = default_type(tv.type); return lb_emit_conv(p, cmp, type); } else if (is_type_untyped_nil(be->left->tav.type)) { + // `nil == x` or `nil != x` lbValue right = lb_build_expr(p, be->right); lbValue cmp = lb_emit_comp_against_nil(p, be->op.kind, right); Type *type = default_type(tv.type); return lb_emit_conv(p, cmp, type); + } else if (lb_is_empty_string_constant(be->right)) { + // `x == ""` or `x != ""` + lbValue len = lb_string_len(p, lb_build_expr(p, be->left)); + lbValue cmp = lb_emit_comp(p, be->op.kind, len, lb_const_int(p->module, t_int, 0)); + Type *type = default_type(tv.type); + return lb_emit_conv(p, cmp, type); + } else if (lb_is_empty_string_constant(be->left)) { + // `"" == x` or `"" != x` + lbValue len = lb_string_len(p, lb_build_expr(p, be->right)); + lbValue cmp = lb_emit_comp(p, be->op.kind, len, lb_const_int(p->module, t_int, 0)); + Type *type = default_type(tv.type); + return lb_emit_conv(p, cmp, type); } /*fallthrough*/ case Token_Lt: @@ -2246,7 +2269,6 @@ gb_internal lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValu - gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right) { Type *a = core_type(left.type); Type *b = core_type(right.type); From feacc5cd1176f93fc4b52c081d8ad93ed391df00 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 14 Jun 2023 14:03:08 +0100 Subject: [PATCH 037/144] Basic enforcement of ordered named arguments/parameters for procedures --- examples/demo/demo.odin | 4 +- src/check_expr.cpp | 131 ++++++++++++++++++++++++++++++++++++---- src/parser_pos.cpp | 6 +- 3 files changed, 127 insertions(+), 14 deletions(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 4b02bcd53..6863a7768 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -1175,13 +1175,13 @@ threading_example :: proc() { N :: 3 pool: thread.Pool - thread.pool_init(pool=&pool, thread_count=N, allocator=context.allocator) + thread.pool_init(pool=&pool, allocator=context.allocator, thread_count=N) defer thread.pool_destroy(&pool) for i in 0..<30 { // be mindful of the allocator used for tasks. The allocator needs to be thread safe, or be owned by the task for exclusive use - thread.pool_add_task(pool=&pool, procedure=task_proc, data=nil, user_index=i, allocator=context.allocator) + thread.pool_add_task(pool=&pool, allocator=context.allocator, procedure=task_proc, data=nil, user_index=i) } thread.pool_start(&pool) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 830b5315d..c6b332cde 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -14,6 +14,7 @@ enum CallArgumentError { CallArgumentError_ParameterMissing, CallArgumentError_DuplicateParameter, CallArgumentError_NoneConstantParameter, + CallArgumentError_OutOfOrderParameters, CallArgumentError_MAX, }; @@ -121,6 +122,7 @@ gb_internal void check_or_return_split_types(CheckerContext *c, Operand *x, Stri gb_internal bool is_diverging_expr(Ast *expr); +gb_internal CallArgumentError check_order_of_call_arguments(CheckerContext *c, Type *proc_type, Ast *call, Slice const &args, bool show_error); enum LoadDirectiveResult { LoadDirective_Success = 0, @@ -5604,6 +5606,10 @@ gb_internal CALL_ARGUMENT_CHECKER(check_named_call_arguments) { } }); + if (check_order_of_call_arguments(c, proc_type, call, ce->args, show_error)) { + return CallArgumentError_OutOfOrderParameters; + } + for_array(i, ce->args) { Ast *arg = ce->args[i]; ast_node(fv, FieldValue, arg); @@ -5867,6 +5873,99 @@ gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Sco return true; } +gb_internal CallArgumentError check_order_of_call_arguments(CheckerContext *c, Type *proc_type, Ast *call, Slice const &args, bool show_error) { + CallArgumentError err = CallArgumentError_None; + + if (proc_type == nullptr || !is_type_proc(proc_type)) { + // ignore for the time being + return err; + } + + ast_node(ce, CallExpr, call); + if (!is_call_expr_field_value(ce)) { + return err; + } + + TypeProc *pt = &base_type(proc_type)->Proc; + isize param_count = pt->param_count; + + TEMPORARY_ALLOCATOR_GUARD(); + + auto arg_order_indices = slice_make(temporary_allocator(), args.count); + auto visited = slice_make(temporary_allocator(), param_count); + + for_array(i, args) { + Ast *arg = args[i]; + ast_node(fv, FieldValue, arg); + + Ast *field = fv->field; + String name = {}; + if (field != nullptr && field->kind == Ast_Ident) { + name = field->Ident.token.string; + } else { + err = CallArgumentError_InvalidFieldValue; + if (show_error) { + gbString s = expr_to_string(arg); + error(arg, "Invalid parameter name '%s' in procedure call", s); + gb_string_free(s); + } + } + + isize index = lookup_procedure_parameter(pt, name); + if (index < 0) { + err = CallArgumentError_InvalidFieldValue; + if (show_error) { + error(arg, "No parameter named '%.*s' for this procedure type", LIT(name)); + } + } + if (visited[index]) { + err = CallArgumentError_DuplicateParameter; + if (show_error) { + error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name)); + } + } + arg_order_indices[i] = index; + } + + if (err) { + return err; + } + + for (isize i = 0; i < arg_order_indices.count-1; i++) { + isize a = arg_order_indices[i+0]; + isize b = arg_order_indices[i+1]; + GB_ASSERT(a != b); + if (a > b) { + err = CallArgumentError_OutOfOrderParameters; + if (show_error) { + Ast *arg_a = args[i+0]; + Ast *arg_b = args[i+1]; + + isize curr_a_order = a; + for (isize j = i; j >= 0; j--) { + isize j_order = arg_order_indices[j]; + if (b < j_order && j_order < curr_a_order) { + curr_a_order = j_order; + arg_a = args[j]; + } + } + + + ast_node(fv_a, FieldValue, arg_a); + ast_node(fv_b, FieldValue, arg_b); + + gbString s_a = expr_to_string(fv_a->field); + gbString s_b = expr_to_string(fv_b->field); + error(arg_b, "Parameter names out of order, expected '%s' to be called before '%s'", s_b, s_a); + gb_string_free(s_b); + gb_string_free(s_a); + } + } + } + + return err; +} + gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call, Slice const &args) { ast_node(ce, CallExpr, call); @@ -5878,17 +5977,6 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op Type *result_type = t_invalid; if (is_call_expr_field_value(ce)) { - call_checker = check_named_call_arguments; - - operands = array_make(heap_allocator(), args.count); - - // NOTE(bill): This is give type hints for the named parameters - // in order to improve the type inference system - - StringMap type_hint_map = {}; // Key: String - string_map_init(&type_hint_map, 2*args.count); - defer (string_map_destroy(&type_hint_map)); - Type *ptype = nullptr; bool single_case = true; @@ -5903,6 +5991,27 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op ptype = proc_type; } + if (check_order_of_call_arguments(c, ptype, call, args, true)) { + CallArgumentData data = {}; + data.result_type = t_invalid; + if (ptype && is_type_proc(ptype) && !is_type_polymorphic(ptype)) { + data.result_type = reduce_tuple_to_single_type(ptype->Proc.results); + } + return data; + } + + call_checker = check_named_call_arguments; + + operands = array_make(heap_allocator(), args.count); + + // NOTE(bill): This is give type hints for the named parameters + // in order to improve the type inference system + + StringMap type_hint_map = {}; // Key: String + string_map_init(&type_hint_map, 2*args.count); + defer (string_map_destroy(&type_hint_map)); + + if (single_case) { Type *bptype = base_type(ptype); if (is_type_proc(bptype)) { diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp index 52d49e897..2f22a85d3 100644 --- a/src/parser_pos.cpp +++ b/src/parser_pos.cpp @@ -41,7 +41,11 @@ gb_internal Token ast_token(Ast *node) { case Ast_MatrixIndexExpr: return node->MatrixIndexExpr.open; case Ast_SliceExpr: return node->SliceExpr.open; case Ast_Ellipsis: return node->Ellipsis.token; - case Ast_FieldValue: return node->FieldValue.eq; + case Ast_FieldValue: + if (node->FieldValue.field) { + return ast_token(node->FieldValue.field); + } + return node->FieldValue.eq; case Ast_EnumFieldValue: return ast_token(node->EnumFieldValue.name); case Ast_DerefExpr: return node->DerefExpr.op; case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x); From c66ac9725ee89df82b70a1109ee1988360d5ab6d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 14 Jun 2023 14:56:33 +0100 Subject: [PATCH 038/144] Separate out logic for checking mixture of named and unnamed parameters --- src/check_expr.cpp | 67 ++++++++++++++++++++++++++-------------------- src/parser.cpp | 2 -- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index c6b332cde..d3e287ba4 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6849,6 +6849,34 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O +// returns true on failure +gb_internal bool check_call_parameter_mixture(Operand *operand, Slice const &args, char const *context) { + bool failure = false; + if (args.count > 0) { + bool first_is_field_value = (args[0]->kind == Ast_FieldValue); + for (Ast *arg : args) { + bool mix = false; + if (first_is_field_value) { + mix = arg->kind != Ast_FieldValue; + } else { + mix = arg->kind == Ast_FieldValue; + } + if (mix) { + error(arg, "Mixture of 'field = value' and value elements in a %s is not allowed", context); + failure = true; + } + } + + } + return failure; +} + +#define CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN(context_) if (check_call_parameter_mixture(operand, args, "procedure call")) { \ + operand->mode = Addressing_Invalid; \ + operand->expr = call; \ + return Expr_Stmt; \ +} + gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice const &args, ProcInlining inlining, Type *type_hint) { if (proc != nullptr && @@ -6888,30 +6916,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } - if (args.count > 0) { - bool fail = false; - bool first_is_field_value = (args[0]->kind == Ast_FieldValue); - for (Ast *arg : args) { - bool mix = false; - if (first_is_field_value) { - mix = arg->kind != Ast_FieldValue; - } else { - mix = arg->kind == Ast_FieldValue; - } - if (mix) { - error(arg, "Mixture of 'field = value' and value elements in a procedure call is not allowed"); - fail = true; - } - } - - if (fail) { - operand->mode = Addressing_Invalid; - operand->expr = call; - return Expr_Stmt; - } - } - if (operand->mode == Addressing_Invalid) { + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure call"); for (Ast *arg : args) { if (arg->kind == Ast_FieldValue) { arg = arg->FieldValue.value; @@ -6926,6 +6932,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c if (operand->mode == Addressing_Type) { Type *t = operand->type; if (is_type_polymorphic_record(t)) { + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("polymorphic type construction"); + if (!is_type_named(t)) { gbString s = expr_to_string(operand->expr); error(call, "Illegal use of an unnamed polymorphic record, %s", s); @@ -6951,6 +6959,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c operand->type = t_invalid; } } else { + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("type conversion"); + operand->mode = Addressing_Invalid; isize arg_count = args.count; switch (arg_count) { @@ -6996,6 +7006,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } if (operand->mode == Addressing_Builtin) { + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("builtin call"); + i32 id = operand->builtin_id; Entity *e = entity_of_node(operand->expr); if (e != nullptr && e->token.string == "expand_to_tuple") { @@ -7009,6 +7021,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c return builtin_procs[id].kind; } + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure call"); + Entity *initial_entity = entity_of_node(operand->expr); if (initial_entity != nullptr && initial_entity->kind == Entity_Procedure) { @@ -7095,7 +7109,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c if (decl->proc_lit) { ast_node(pl, ProcLit, decl->proc_lit); if (pl->inlining == ProcInlining_no_inline) { - error(call, "'inline' cannot be applied to a procedure that has be marked as 'no_inline'"); + error(call, "'#force_inline' cannot be applied to a procedure that has be marked as '#force_no_inline'"); } } } @@ -7108,9 +7122,6 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c operand->expr = call; { - if (proc_type == t_invalid) { - // gb_printf_err("%s\n", expr_to_string(operand->expr)); - } Type *type = nullptr; if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) { type = type_of_expr(operand->expr->CallExpr.proc); @@ -7128,8 +7139,6 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } - // add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value); - return Expr_Expr; } diff --git a/src/parser.cpp b/src/parser.cpp index 883342b21..96b7c96ea 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2777,8 +2777,6 @@ gb_internal Ast *parse_call_expr(AstFile *f, Ast *operand) { Ast *value = parse_value(f); arg = ast_field_value(f, arg, value, eq); - - } array_add(&args, arg); From 242d3b3c4d7e269b04c96a81dbf6e5f5fbeb5e8b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 14 Jun 2023 15:40:52 +0100 Subject: [PATCH 039/144] Begin work allowing mixture of named and unnamed --- src/check_expr.cpp | 75 ++++++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d3e287ba4..52ab5092e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -101,8 +101,7 @@ gb_internal void check_struct_type (CheckerContext *c, Type *st gb_internal void check_union_type (CheckerContext *c, Type *union_type, Ast *node, Array *poly_operands, Type *named_type = nullptr, Type *original_type_for_poly = nullptr); -gb_internal CallArgumentData check_call_arguments (CheckerContext *c, Operand *operand, Type *proc_type, Ast *call); -gb_internal Type * check_init_variable (CheckerContext *c, Entity *e, Operand *operand, String context_name); +gb_internal Type * check_init_variable (CheckerContext *c, Entity *e, Operand *operand, String context_name); gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type); @@ -122,7 +121,7 @@ gb_internal void check_or_return_split_types(CheckerContext *c, Operand *x, Stri gb_internal bool is_diverging_expr(Ast *expr); -gb_internal CallArgumentError check_order_of_call_arguments(CheckerContext *c, Type *proc_type, Ast *call, Slice const &args, bool show_error); +gb_internal CallArgumentError check_order_of_call_arguments(CheckerContext *c, Type *proc_type, Ast *call, bool show_error); enum LoadDirectiveResult { LoadDirective_Success = 0, @@ -5606,7 +5605,7 @@ gb_internal CALL_ARGUMENT_CHECKER(check_named_call_arguments) { } }); - if (check_order_of_call_arguments(c, proc_type, call, ce->args, show_error)) { + if (check_order_of_call_arguments(c, proc_type, call, show_error)) { return CallArgumentError_OutOfOrderParameters; } @@ -5873,7 +5872,7 @@ gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Sco return true; } -gb_internal CallArgumentError check_order_of_call_arguments(CheckerContext *c, Type *proc_type, Ast *call, Slice const &args, bool show_error) { +gb_internal CallArgumentError check_order_of_call_arguments(CheckerContext *c, Type *proc_type, Ast *call, bool show_error) { CallArgumentError err = CallArgumentError_None; if (proc_type == nullptr || !is_type_proc(proc_type)) { @@ -5891,11 +5890,11 @@ gb_internal CallArgumentError check_order_of_call_arguments(CheckerContext *c, T TEMPORARY_ALLOCATOR_GUARD(); - auto arg_order_indices = slice_make(temporary_allocator(), args.count); + auto arg_order_indices = slice_make(temporary_allocator(), ce->args.count); auto visited = slice_make(temporary_allocator(), param_count); - for_array(i, args) { - Ast *arg = args[i]; + for_array(i, ce->args) { + Ast *arg = ce->args[i]; ast_node(fv, FieldValue, arg); Ast *field = fv->field; @@ -5938,15 +5937,15 @@ gb_internal CallArgumentError check_order_of_call_arguments(CheckerContext *c, T if (a > b) { err = CallArgumentError_OutOfOrderParameters; if (show_error) { - Ast *arg_a = args[i+0]; - Ast *arg_b = args[i+1]; + Ast *arg_a = ce->args[i+0]; + Ast *arg_b = ce->args[i+1]; isize curr_a_order = a; for (isize j = i; j >= 0; j--) { isize j_order = arg_order_indices[j]; if (b < j_order && j_order < curr_a_order) { curr_a_order = j_order; - arg_a = args[j]; + arg_a = ce->args[j]; } } @@ -5967,15 +5966,18 @@ gb_internal CallArgumentError check_order_of_call_arguments(CheckerContext *c, T } -gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call, Slice const &args) { +gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Ast *call) { ast_node(ce, CallExpr, call); CallArgumentCheckerType *call_checker = check_call_arguments_internal; Array operands = {}; defer (array_free(&operands)); - Type *result_type = t_invalid; + Type *result_type = t_invalid; + Type *proc_type = base_type(operand->type); + + Slice const &args = ce->args; if (is_call_expr_field_value(ce)) { Type *ptype = nullptr; bool single_case = true; @@ -5991,7 +5993,7 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op ptype = proc_type; } - if (check_order_of_call_arguments(c, ptype, call, args, true)) { + if (check_order_of_call_arguments(c, ptype, call, true)) { CallArgumentData data = {}; data.result_type = t_invalid; if (ptype && is_type_proc(ptype) && !is_type_polymorphic(ptype)) { @@ -6850,20 +6852,31 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O // returns true on failure -gb_internal bool check_call_parameter_mixture(Operand *operand, Slice const &args, char const *context) { +gb_internal bool check_call_parameter_mixture(Operand *operand, Slice const &args, char const *context, bool allow_mixed=false) { bool failure = false; if (args.count > 0) { - bool first_is_field_value = (args[0]->kind == Ast_FieldValue); - for (Ast *arg : args) { - bool mix = false; - if (first_is_field_value) { - mix = arg->kind != Ast_FieldValue; - } else { - mix = arg->kind == Ast_FieldValue; + if (allow_mixed) { + bool was_named = false; + for (Ast *arg : args) { + if (was_named && arg->kind != Ast_FieldValue) { + error(arg, "Non-named parameter is not allowed to follow named parameter i.e. 'field = value' in a %s", context); + failure = true; + } + was_named = was_named || arg->kind == Ast_FieldValue; } - if (mix) { - error(arg, "Mixture of 'field = value' and value elements in a %s is not allowed", context); - failure = true; + } else { + bool first_is_field_value = (args[0]->kind == Ast_FieldValue); + for (Ast *arg : args) { + bool mix = false; + if (first_is_field_value) { + mix = arg->kind != Ast_FieldValue; + } else { + mix = arg->kind == Ast_FieldValue; + } + if (mix) { + error(arg, "Mixture of 'field = value' and value elements in a %s is not allowed", context); + failure = true; + } } } @@ -6871,7 +6884,7 @@ gb_internal bool check_call_parameter_mixture(Operand *operand, Slice con return failure; } -#define CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN(context_) if (check_call_parameter_mixture(operand, args, "procedure call")) { \ +#define CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN(context_, ...) if (check_call_parameter_mixture(operand, args, context_, ##__VA_ARGS__)) { \ operand->mode = Addressing_Invalid; \ operand->expr = call; \ return Expr_Stmt; \ @@ -7021,6 +7034,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c return builtin_procs[id].kind; } + // CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure call", true); CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure call"); Entity *initial_entity = entity_of_node(operand->expr); @@ -7034,8 +7048,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } - Type *proc_type = base_type(operand->type); if (operand->mode != Addressing_ProcGroup) { + Type *proc_type = base_type(operand->type); bool valid_type = (proc_type != nullptr) && is_type_proc(proc_type); bool valid_mode = is_operand_value(*operand); if (!valid_type || !valid_mode) { @@ -7053,7 +7067,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } - CallArgumentData data = check_call_arguments(c, operand, proc_type, call, args); + CallArgumentData data = check_call_arguments(c, operand, call); Type *result_type = data.result_type; gb_zero_item(operand); operand->expr = call; @@ -7064,7 +7078,10 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c return Expr_Stmt; } - Type *pt = base_type(proc_type); + Type *pt = base_type(operand->type); + if (pt == nullptr) { + pt = t_invalid; + } if (pt == t_invalid) { if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) { pt = type_of_expr(operand->expr->CallExpr.proc); From 4f2b9835f5f6966c5f11fe20f9f56855c54e4ce5 Mon Sep 17 00:00:00 2001 From: Rickard Andersson Date: Wed, 14 Jun 2023 22:13:36 +0300 Subject: [PATCH 040/144] feat(unix): add `poll` --- core/os/os_linux.odin | 36 +++++++++++++++++++++++++++++++ core/sys/unix/syscalls_linux.odin | 29 +++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index 7a0a4b1dd..beb4acec5 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -432,6 +432,18 @@ AT_FDCWD :: ~uintptr(99) /* -100 */ AT_REMOVEDIR :: uintptr(0x200) AT_SYMLINK_NOFOLLOW :: uintptr(0x100) +pollfd :: struct { + fd: c.int, + events: c.short, + revents: c.short, +} + +nfds_t :: distinct c.uint + +sigset_t :: struct { + __val: [16]c.ulong, +} + foreign libc { @(link_name="__errno_location") __errno_location :: proc() -> ^int --- @@ -1087,3 +1099,27 @@ fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Errno) { } return result, ERROR_NONE } + +// select :: proc(nfds: int, readfds: ^fd_set, writefds: ^fd_set, exceptfds: ^fd_set, timeout: ^timeval) -> (int, Errno) { +// result := unix.sys_select(nfds, readfds, writefds, exceptfds, timeout) +// if result < 0 { +// return 0, _get_errno(result) +// } +// return result, ERROR_NONE +// } + +poll :: proc(fds: [^]pollfd, nfds: nfds_t, timeout: int) -> (int, Errno) { + result := unix.sys_poll(fds, uint(nfds), timeout) + if result < 0 { + return 0, _get_errno(result) + } + return result, ERROR_NONE +} + +ppoll :: proc(fds: [^]pollfd, nfds: nfds_t, timeout: ^unix.timespec, sigmask: ^sigset_t) -> (int, Errno) { + result := unix.sys_ppoll(fds, uint(nfds), timeout, sigmask, size_of(sigset_t)) + if result < 0 { + return 0, _get_errno(result) + } + return result, ERROR_NONE +} diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index abdcf0b92..54f8bcc25 100644 --- a/core/sys/unix/syscalls_linux.odin +++ b/core/sys/unix/syscalls_linux.odin @@ -1567,6 +1567,23 @@ MADV_HWPOISON :: 100 // pipe2 flags O_CLOEXEC :: 0o2000000 +// poll events +POLLIN :: 0x0001 +POLLPRI :: 0x0002 +POLLOUT :: 0x0004 +POLLERR :: 0x0008 +POLLHUP :: 0x0010 +POLLNVAL :: 0x0020 +POLLRDNORM :: 0x0040 +POLLRDBAND :: 0x0080 +POLLWRNORM :: 0x0100 +POLLWRBAND :: 0x0200 +POLLMSG :: 0x0400 +POLLREMOVE :: 0x1000 +POLLRDHUP :: 0x2000 +POLLFREE :: 0x4000 +POLL_BUSY_LOOP :: 0x8000 + // perf event data Perf_Sample :: struct #raw_union { period: u64, @@ -2057,6 +2074,18 @@ sys_fcntl :: proc "contextless" (fd: int, cmd: int, arg: int) -> int { return int(intrinsics.syscall(SYS_fcntl, uintptr(fd), uintptr(cmd), uintptr(arg))) } +sys_select :: proc "contextless" (nfds: int, readfds, writefds, exceptfds: rawptr, timeout: rawptr) -> int { + return int(intrinsics.syscall(SYS_select, uintptr(nfds), uintptr(readfds), uintptr(writefds), uintptr(exceptfds), uintptr(timeout))) +} + +sys_poll :: proc "contextless" (fds: rawptr, nfds: uint, timeout: int) -> int { + return int(intrinsics.syscall(SYS_poll, uintptr(fds), uintptr(nfds), uintptr(timeout))) +} + +sys_ppoll :: proc "contextless" (fds: rawptr, nfds: uint, timeout: rawptr, sigmask: rawptr, sigsetsize: uint) -> int { + return int(intrinsics.syscall(SYS_ppoll, uintptr(fds), uintptr(nfds), uintptr(timeout), uintptr(sigmask), uintptr(sigsetsize))) +} + get_errno :: proc "contextless" (res: int) -> i32 { if res < 0 && res > -4096 { return i32(-res) From 091c515fea784b639b4a907a2fff4a6c6eef53de Mon Sep 17 00:00:00 2001 From: Rickard Andersson Date: Wed, 14 Jun 2023 22:34:57 +0300 Subject: [PATCH 041/144] cleanup(os_linux): remove `select` --- core/os/os_linux.odin | 8 -------- 1 file changed, 8 deletions(-) diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index beb4acec5..70e842aa4 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -1100,14 +1100,6 @@ fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Errno) { return result, ERROR_NONE } -// select :: proc(nfds: int, readfds: ^fd_set, writefds: ^fd_set, exceptfds: ^fd_set, timeout: ^timeval) -> (int, Errno) { -// result := unix.sys_select(nfds, readfds, writefds, exceptfds, timeout) -// if result < 0 { -// return 0, _get_errno(result) -// } -// return result, ERROR_NONE -// } - poll :: proc(fds: [^]pollfd, nfds: nfds_t, timeout: int) -> (int, Errno) { result := unix.sys_poll(fds, uint(nfds), timeout) if result < 0 { From d6540d9077cbfe18f0d6804ec2a301c04758c820 Mon Sep 17 00:00:00 2001 From: Rickard Andersson Date: Wed, 14 Jun 2023 23:26:43 +0300 Subject: [PATCH 042/144] fix(os_linux): call `ppoll` instead on `arm64` --- core/os/os_linux.odin | 4 +--- core/sys/unix/syscalls_linux.odin | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index 70e842aa4..3e5258af3 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -440,9 +440,7 @@ pollfd :: struct { nfds_t :: distinct c.uint -sigset_t :: struct { - __val: [16]c.ulong, -} +sigset_t :: distinct u64 foreign libc { @(link_name="__errno_location") __errno_location :: proc() -> ^int --- diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index 54f8bcc25..2e5bad153 100644 --- a/core/sys/unix/syscalls_linux.odin +++ b/core/sys/unix/syscalls_linux.odin @@ -2079,7 +2079,22 @@ sys_select :: proc "contextless" (nfds: int, readfds, writefds, exceptfds: rawpt } sys_poll :: proc "contextless" (fds: rawptr, nfds: uint, timeout: int) -> int { - return int(intrinsics.syscall(SYS_poll, uintptr(fds), uintptr(nfds), uintptr(timeout))) + // NOTE: specialcased here because `arm64` does not have `poll` + when ODIN_ARCH != .arm64 { + // redefined because we can't depend on the `unix` module here + timespec :: struct { + tv_sec: i64, + tv_nsec: i64, + } + seconds := i64(timeout / 1_000) + nanoseconds := i64((timeout % 1000) * 1_000_000) + timeout_spec := timespec{seconds, nanoseconds} + + return int(intrinsics.syscall(SYS_ppoll, uintptr(fds), uintptr(nfds), uintptr(&timeout_spec), uintptr(0), uintptr(8))) + } else { + return int(intrinsics.syscall(SYS_poll, uintptr(fds), uintptr(nfds), uintptr(timeout))) + } + } sys_ppoll :: proc "contextless" (fds: rawptr, nfds: uint, timeout: rawptr, sigmask: rawptr, sigsetsize: uint) -> int { From 003c470a4df12fe2e82b13fde532f95b2ac85637 Mon Sep 17 00:00:00 2001 From: Rickard Andersson Date: Wed, 14 Jun 2023 23:38:00 +0300 Subject: [PATCH 043/144] fix(os_linux): make `when` for `arm64` check correct order Also remove `sys_select` since we aren't using it anyway --- core/sys/unix/syscalls_linux.odin | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index 2e5bad153..0accc9d53 100644 --- a/core/sys/unix/syscalls_linux.odin +++ b/core/sys/unix/syscalls_linux.odin @@ -2074,13 +2074,9 @@ sys_fcntl :: proc "contextless" (fd: int, cmd: int, arg: int) -> int { return int(intrinsics.syscall(SYS_fcntl, uintptr(fd), uintptr(cmd), uintptr(arg))) } -sys_select :: proc "contextless" (nfds: int, readfds, writefds, exceptfds: rawptr, timeout: rawptr) -> int { - return int(intrinsics.syscall(SYS_select, uintptr(nfds), uintptr(readfds), uintptr(writefds), uintptr(exceptfds), uintptr(timeout))) -} - sys_poll :: proc "contextless" (fds: rawptr, nfds: uint, timeout: int) -> int { // NOTE: specialcased here because `arm64` does not have `poll` - when ODIN_ARCH != .arm64 { + when ODIN_ARCH == .arm64 { // redefined because we can't depend on the `unix` module here timespec :: struct { tv_sec: i64, From 0036509f74592e6921e0dbb994e0df02e6d673ba Mon Sep 17 00:00:00 2001 From: Rickard Andersson Date: Wed, 14 Jun 2023 23:51:33 +0300 Subject: [PATCH 044/144] cleanup(os_linux/poll): use tabs everywhere :( --- core/sys/unix/syscalls_linux.odin | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index 0accc9d53..fdfc27187 100644 --- a/core/sys/unix/syscalls_linux.odin +++ b/core/sys/unix/syscalls_linux.odin @@ -2075,22 +2075,21 @@ sys_fcntl :: proc "contextless" (fd: int, cmd: int, arg: int) -> int { } sys_poll :: proc "contextless" (fds: rawptr, nfds: uint, timeout: int) -> int { - // NOTE: specialcased here because `arm64` does not have `poll` + // NOTE: specialcased here because `arm64` does not have `poll` when ODIN_ARCH == .arm64 { - // redefined because we can't depend on the `unix` module here - timespec :: struct { - tv_sec: i64, - tv_nsec: i64, - } - seconds := i64(timeout / 1_000) - nanoseconds := i64((timeout % 1000) * 1_000_000) - timeout_spec := timespec{seconds, nanoseconds} - + // redefined because we can't depend on the `unix` module here + timespec :: struct { + tv_sec: i64, + tv_nsec: i64, + } + seconds := i64(timeout / 1_000) + nanoseconds := i64((timeout % 1000) * 1_000_000) + timeout_spec := timespec{seconds, nanoseconds} + return int(intrinsics.syscall(SYS_ppoll, uintptr(fds), uintptr(nfds), uintptr(&timeout_spec), uintptr(0), uintptr(8))) } else { - return int(intrinsics.syscall(SYS_poll, uintptr(fds), uintptr(nfds), uintptr(timeout))) + return int(intrinsics.syscall(SYS_poll, uintptr(fds), uintptr(nfds), uintptr(timeout))) } - } sys_ppoll :: proc "contextless" (fds: rawptr, nfds: uint, timeout: rawptr, sigmask: rawptr, sigsetsize: uint) -> int { From a5ed5883c71282b2454e3e320fc7c8703b9fa952 Mon Sep 17 00:00:00 2001 From: Rickard Andersson Date: Wed, 14 Jun 2023 23:59:09 +0300 Subject: [PATCH 045/144] cleanup: more tabs --- core/os/os_linux.odin | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index 3e5258af3..ff3ff700c 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -1099,17 +1099,17 @@ fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Errno) { } poll :: proc(fds: [^]pollfd, nfds: nfds_t, timeout: int) -> (int, Errno) { - result := unix.sys_poll(fds, uint(nfds), timeout) - if result < 0 { - return 0, _get_errno(result) - } - return result, ERROR_NONE + result := unix.sys_poll(fds, uint(nfds), timeout) + if result < 0 { + return 0, _get_errno(result) + } + return result, ERROR_NONE } ppoll :: proc(fds: [^]pollfd, nfds: nfds_t, timeout: ^unix.timespec, sigmask: ^sigset_t) -> (int, Errno) { - result := unix.sys_ppoll(fds, uint(nfds), timeout, sigmask, size_of(sigset_t)) - if result < 0 { - return 0, _get_errno(result) - } - return result, ERROR_NONE + result := unix.sys_ppoll(fds, uint(nfds), timeout, sigmask, size_of(sigset_t)) + if result < 0 { + return 0, _get_errno(result) + } + return result, ERROR_NONE } From 2992ca5df122e2f20113d5b357413c7fff606879 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 15 Jun 2023 01:37:55 +0100 Subject: [PATCH 046/144] Basic support for new procedure code (non-polymorphic, non-proc-group) --- src/check_expr.cpp | 388 ++++++++++++++++++++++++++++++++++++-- src/llvm_backend_proc.cpp | 119 ++++++++++++ src/parser.hpp | 6 + src/parser_pos.cpp | 6 +- 4 files changed, 501 insertions(+), 18 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 52ab5092e..cc3f268c3 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -123,6 +123,8 @@ gb_internal bool is_diverging_expr(Ast *expr); gb_internal CallArgumentError check_order_of_call_arguments(CheckerContext *c, Type *proc_type, Ast *call, bool show_error); +gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_); + enum LoadDirectiveResult { LoadDirective_Success = 0, LoadDirective_Error = 1, @@ -5151,14 +5153,19 @@ enum UnpackFlag : u32 { }; -gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array *operands, Slice const &rhs, UnpackFlags flags) { +gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array *operands, Slice const &rhs_arguments, UnpackFlags flags) { bool allow_ok = (flags & UnpackFlag_AllowOk) != 0; bool is_variadic = (flags & UnpackFlag_IsVariadic) != 0; bool allow_undef = (flags & UnpackFlag_AllowUndef) != 0; bool optional_ok = false; isize tuple_index = 0; - for_array(i, rhs) { + for (Ast *rhs : rhs_arguments) { + if (rhs->kind == Ast_FieldValue) { + error(rhs, "Invalid use of 'field = value'"); + rhs = rhs->FieldValue.value; + } + CheckerContext c_ = *ctx; CheckerContext *c = &c_; @@ -5166,12 +5173,11 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize Type *type_hint = nullptr; + if (lhs != nullptr && tuple_index < lhs_count) { // NOTE(bill): override DeclInfo for dependency Entity *e = lhs[tuple_index]; if (e != nullptr) { - // DeclInfo *decl = decl_info_of_entity(e); - // if (decl) c->decl = decl; type_hint = e->type; if (e->flags & EntityFlag_Ellipsis) { GB_ASSERT(is_type_slice(e->type)); @@ -5183,8 +5189,6 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize // NOTE(bill): override DeclInfo for dependency Entity *e = lhs[lhs_count-1]; if (e != nullptr) { - // DeclInfo *decl = decl_info_of_entity(e); - // if (decl) c->decl = decl; type_hint = e->type; if (e->flags & EntityFlag_Ellipsis) { GB_ASSERT(is_type_slice(e->type)); @@ -5194,15 +5198,15 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize } } - Ast *rhs_expr = unparen_expr(rhs[i]); + Ast *rhs_expr = unparen_expr(rhs); if (allow_undef && rhs_expr != nullptr && rhs_expr->kind == Ast_Uninit) { // NOTE(bill): Just handle this very specific logic here o.type = t_untyped_uninit; o.mode = Addressing_Value; - o.expr = rhs[i]; - add_type_and_value(c, rhs[i], o.mode, o.type, o.value); + o.expr = rhs; + add_type_and_value(c, rhs, o.mode, o.type, o.value); } else { - check_expr_base(c, &o, rhs[i], type_hint); + check_expr_base(c, &o, rhs, type_hint); } if (o.mode == Addressing_NoValue) { error_operand_no_value(&o); @@ -5210,7 +5214,7 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize } if (o.type == nullptr || o.type->kind != Type_Tuple) { - if (allow_ok && lhs_count == 2 && rhs.count == 1 && + if (allow_ok && lhs_count == 2 && rhs_arguments.count == 1 && (o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk || o.mode == Addressing_OptionalOkPtr)) { Ast *expr = unparen_expr(o.expr); @@ -5966,14 +5970,364 @@ gb_internal CallArgumentError check_order_of_call_arguments(CheckerContext *c, T } +gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContext *c, Operand *operand, Ast *call) { + Type *proc_type = nullptr; + + CallArgumentData data = {}; + data.result_type = t_invalid; + + + GB_ASSERT(operand->mode != Addressing_ProcGroup); + + proc_type = base_type(operand->type); + + if (proc_type == nullptr || proc_type->kind != Type_Proc) { + gbString s = type_to_string(proc_type); + error(call, "Expected a procedure to call, got %s", s); + gb_string_free(s); + + return data; + } + + if (is_type_polymorphic(proc_type)) { + error(call, "Polymorphic procedures not yet supported"); + return data; + } + + TypeProc *pt = &proc_type->Proc; + + TEMPORARY_ALLOCATOR_GUARD(); + ast_node(ce, CallExpr, call); + + bool any_failure = false; + + bool vari_expand = (ce->ellipsis.pos.line != 0); + + auto positional_args = ce->args; + for (isize i = 0; i < ce->args.count; i++) { + Ast *arg = ce->args.data[i]; + if (arg->kind == Ast_FieldValue) { + positional_args.count = i; + break; + } + } + auto named_args = slice(ce->args, positional_args.count, ce->args.count); + + auto positional_operands = array_make(heap_allocator(), 0, positional_args.count); + auto variadic_operands = array_make(heap_allocator(), 0, 0); + + defer (array_free(&positional_operands)); + defer (array_free(&variadic_operands)); + + isize positional_operand_count = 0; + + auto visited = slice_make(temporary_allocator(), pt->param_count); + + if (positional_args.count > 0) { + isize lhs_count = -1; + bool is_variadic = false; + Entity **lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic); + check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); + + if (!is_variadic) { + if (pt->param_count < positional_operands.count) { + char const *err_fmt = "Too many arguments for '%s', expected a maximum of %td arguments, got %td"; + gbString c = expr_to_string(ce->proc); + error(call, err_fmt, c, pt->param_count, positional_operands.count); + gb_string_free(c); + return data; + } + } + + for (Operand const &o : positional_operands) { + check_no_copy_assignment(o, str_lit("procedure call expression")); + } + + positional_operand_count = gb_min(pt->param_count, positional_operands.count); + if (is_variadic) { + positional_operand_count = gb_min(pt->variadic_index, positional_operand_count); + } + + for (isize i = 0; i < positional_operand_count; i++) { + visited[i] = true; + } + if (is_variadic) { + visited[pt->variadic_index] = true; + } + + for (isize i = positional_operand_count; i < positional_operands.count; i++) { + array_add(&variadic_operands, positional_operands[i]); + } + if (vari_expand) { + if (variadic_operands.count > 1) { + error(variadic_operands[1].expr, "Unexpected position arguments after variadic expansion with '..'"); + return data; + } + } + + array_resize(&positional_operands, positional_operand_count); + } + + isize param_count = 0; + isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(proc_type, ¶m_count); + + if (named_args.count == 0 && positional_operand_count < param_count_excluding_defaults) { + char const *err_fmt = "Too few arguments for '%s', expected a minimum of %td arguments, got %td"; + gbString c = expr_to_string(ce->proc); + error(call, err_fmt, c, param_count_excluding_defaults, positional_operands.count); + gb_string_free(c); + return data; + } + + for_array(i, positional_operands) { + Operand &o = positional_operands[i]; + isize param_index = i; + + Entity *e = pt->params->Tuple.variables[param_index]; + GB_ASSERT(e->kind == Entity_Variable); + + check_assignment(c, &o, e->type, str_lit("procedure call expression")); + } + + if (pt->variadic) { + isize param_index = pt->variadic_index; + + Entity *e = pt->params->Tuple.variables[param_index]; + GB_ASSERT(e->kind == Entity_Variable); + GB_ASSERT(e->flags & EntityFlag_Ellipsis); + GB_ASSERT(is_type_slice(e->type)); + + if (vari_expand) { + if (variadic_operands.count != 0) { + GB_ASSERT(variadic_operands.count == 1); + check_assignment(c, &variadic_operands[0], e->type, str_lit("procedure call expression")); + } + } else { + Type *elem = e->type->Slice.elem; + for (Operand &o : variadic_operands) { + check_assignment(c, &o, elem, str_lit("procedure call expression")); + } + } + } + + array_resize(&positional_operands, pt->param_count); + + for_array(i, named_args) { + Ast *arg = named_args[i]; + if (arg->kind != Ast_FieldValue) { + error(arg, "Expected a 'field = value'"); + return data; + } + ast_node(fv, FieldValue, arg); + if (fv->field->kind != Ast_Ident) { + gbString expr_str = expr_to_string(fv->field); + error(arg, "Invalid parameter name '%s' in procedure call", expr_str); + any_failure = true; + gb_string_free(expr_str); + continue; + } + String name = fv->field->Ident.token.string; + isize param_index = lookup_procedure_parameter(pt, name); + if (param_index < 0) { + error(arg, "No parameter named '%.*s' for this procedure type", LIT(name)); + any_failure = true; + continue; + } + + bool prev_visited = visited[param_index]; + visited[param_index] = true; + + if (prev_visited) { + error(arg, "Duplicate parameter named '%.*s' in procedure call", LIT(name)); + } + + Entity *e = pt->params->Tuple.variables[param_index]; + GB_ASSERT(e->kind == Entity_Variable); + + Operand o = {}; + check_expr_with_type_hint(c, &o, fv->value, e->type); + if (o.mode == Addressing_Invalid) { + any_failure = true; + } + + check_assignment(c, &o, e->type, str_lit("procedure call expression")); + check_no_copy_assignment(o, str_lit("procedure call expression")); + + if (!prev_visited) { + positional_operands[param_index] = o; + } + } + + for_array(i, visited) { + if (!visited[i]) { + if (pt->variadic && i == pt->variadic_index) { + visited[i] = true; + continue; + } + + Entity *e = pt->params->Tuple.variables[i]; + if (e->kind == Entity_Variable) { + if (has_parameter_value(e->Variable.param_value)) { + visited[i] = true; + continue; + } + } + gbString t = type_to_string(e->type); + error(call, "Missing parameter of type '%s' at index %td", t, i); + gb_string_free(t); + } + } + + if (any_failure) { + return data; + } + + + auto ordered_args = gb_alloc_item(permanent_allocator(), AstOrderedArgs); + ordered_args->positional = positional_args; + ordered_args->named = named_args; + + ce->ordered_args = ordered_args; + + data.result_type = pt->results; + return data; +#if 0 + + + + /* + auto visited = slice_make(temporary_allocator(), pt->param_count); + auto ordered_values = slice_make(temporary_allocator(), pt->param_count); + + + for_array(i, ce->args) { + Ast *arg = ce->args[i]; + + isize param_index = i; + String param_name = {}; + + if (arg->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, arg); + if (fv->field->kind != Ast_Ident) { + gbString expr_str = expr_to_string(fv->field); + error(arg, "Invalid parameter name '%s' in procedure call", expr_str); + any_failure = true; + gb_string_free(expr_str); + continue; + } + param_name = fv->field->Ident.token.string; + param_index = lookup_procedure_parameter(pt, param_name); + if (param_index < 0) { + error(arg, "No parameter named '%.*s' for this procedure type", LIT(param_name)); + any_failure = true; + continue; + } + arg = fv->value; + } else { + if (param_index >= pt->param_count) { + error(arg, "Too many parameters in procedure call, expected a maximum of %td, got %td", pt->param_count, ce->args.count); + any_failure = true; + break; + } + + param_name = pt->params->Tuple.variables[param_index]->token.string; + } + if (visited[param_index]) { + error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(param_name)); + any_failure = true; + } else { + ParameterValue value = {}; + value.kind = ParameterValue_Value; + value.original_ast_expr = arg; + value.ast_value = arg; + ordered_values[param_index] = value; + } + visited[param_index] = true; + } + + for (isize i = 0; i < pt->param_count; i++) { + if (!visited[i]) { + Entity *e = pt->params->Tuple.variables[i]; + if (e->kind == Entity_Variable && + has_parameter_value(e->Variable.param_value)) { + ordered_values[i] = e->Variable.param_value; + visited[i] = true; + } + } + } + + for (isize i = 0; i < visited.count; i++) { + if (!visited[i]) { + Entity *e = pt->params->Tuple.variables[i]; + gbString t = type_to_string(e->type); + defer (gb_string_free(t)); + if (is_blank_ident(e->token)) { + error(call, "Missing parameter of type '%s' at index %td", t, i); + } else { + error(call, "Missing parameter '%.*s' of type '%s'", LIT(e->token.string), t, i); + } + return data; + } + } + */ + if (any_failure) { + return data; + } + + error(call, "TODO: Implement check_call_arguments_single_procedure for %s", type_to_string(proc_type)); + + Array operands = {}; + defer (array_free(&operands)); + + Ast *ident = unparen_expr(operand->expr); + while (ident->kind == Ast_SelectorExpr) { + Ast *s = ident->SelectorExpr.selector; + ident = unparen_expr(s); + } + + Entity *e = entity_of_node(ident); + + CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data); + gb_unused(err); + Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; + add_entity_use(c, ident, entity_to_use); + if (entity_to_use != nullptr) { + update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); + } + if (data.gen_entity != nullptr) { + Entity *e = data.gen_entity; + DeclInfo *decl = data.gen_entity->decl_info; + CheckerContext ctx = *c; + ctx.scope = decl->scope; + ctx.decl = decl; + ctx.proc_name = e->token.string; + ctx.curr_proc_decl = decl; + ctx.curr_proc_sig = e->type; + + GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); + bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); + decl->where_clauses_evaluated = true; + + if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { + check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); + } + } + return data; +#endif +} + + gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Ast *call) { + if (operand->mode != Addressing_ProcGroup && is_type_proc(operand->type) && !is_type_polymorphic(operand->type)) { + return check_call_arguments_single_procedure(c, operand, call); + } + ast_node(ce, CallExpr, call); CallArgumentCheckerType *call_checker = check_call_arguments_internal; Array operands = {}; defer (array_free(&operands)); - Type *result_type = t_invalid; Type *proc_type = base_type(operand->type); @@ -6102,7 +6456,7 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op } for (Operand const &o : operands) { - check_no_copy_assignment(o, str_lit("call expression")); + check_no_copy_assignment(o, str_lit("procedure call expression")); } if (operand->mode == Addressing_ProcGroup) { @@ -6861,6 +7215,7 @@ gb_internal bool check_call_parameter_mixture(Operand *operand, Slice con if (was_named && arg->kind != Ast_FieldValue) { error(arg, "Non-named parameter is not allowed to follow named parameter i.e. 'field = value' in a %s", context); failure = true; + break; } was_named = was_named || arg->kind == Ast_FieldValue; } @@ -7034,8 +7389,11 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c return builtin_procs[id].kind; } - // CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure call", true); - CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure call"); + if (operand->mode == Addressing_ProcGroup) { + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure group call"); + } else { + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure call", true); + } Entity *initial_entity = entity_of_node(operand->expr); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 8b9f8b249..3c1cfe47c 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3145,6 +3145,118 @@ gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) { } return res; } + +gb_internal void lb_add_values_to_array(lbProcedure *p, Array *args, lbValue value) { + if (is_type_tuple(value.type)) { + for_array(i, value.type->Tuple.variables) { + lbValue sub_value = lb_emit_struct_ev(p, value, cast(i32)i); + array_add(args, sub_value); + } + } else { + array_add(args, value); + } +} +gb_internal lbValue lb_build_call_expr_internal_with_arg_ordered_args(lbProcedure *p, lbValue value, TypeProc *pt, Ast *call, AstOrderedArgs *ordered_args) { + ast_node(ce, CallExpr, call); + + auto args = array_make(permanent_allocator(), 0, pt->param_count); + + bool vari_expand = (ce->ellipsis.pos.line != 0); + + for_array(i, ordered_args->positional) { + Entity *e = pt->params->Tuple.variables[i]; + GB_ASSERT(e->kind == Entity_Variable); + + if (pt->variadic && pt->variadic_index == i) { + lbValue variadic_args = lb_const_nil(p->module, e->type); + auto variadic = slice(ordered_args->positional, pt->variadic_index, ordered_args->positional.count); + if (variadic.count != 0) { + // variadic call argument generation + Type *slice_type = e->type; + GB_ASSERT(slice_type->kind == Type_Slice); + if (vari_expand) { + GB_ASSERT(variadic.count == 1); + variadic_args = lb_build_expr(p, variadic[0]); + variadic_args = lb_emit_conv(p, variadic_args, slice_type); + } else { + Type *elem_type = slice_type->Slice.elem; + + auto var_args = array_make(heap_allocator(), 0, variadic.count); + defer (array_free(&var_args)); + for (Ast *var_arg : variadic) { + lbValue v = lb_build_expr(p, var_arg); + lb_add_values_to_array(p, &var_args, v); + } + isize slice_len = var_args.count; + if (slice_len > 0) { + lbAddr slice = lb_add_local_generated(p, slice_type, true); + lbAddr base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true); + + for (isize i = 0; i < var_args.count; i++) { + lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)i); + lbValue var_arg = var_args[i]; + var_arg = lb_emit_conv(p, var_arg, elem_type); + lb_emit_store(p, addr, var_arg); + } + + lbValue base_elem = lb_emit_array_epi(p, base_array.addr, 0); + lbValue len = lb_const_int(p->module, t_int, slice_len); + lb_fill_slice(p, slice, base_elem, len); + + variadic_args = lb_addr_load(p, slice); + } + } + } + array_add(&args, variadic_args); + + break; + } else { + lbValue value = lb_build_expr(p, ordered_args->positional[i]); + lb_add_values_to_array(p, &args, value); + } + } + + array_resize(&args, pt->param_count); + + for (Ast *arg : ordered_args->named) { + ast_node(fv, FieldValue, arg); + GB_ASSERT(fv->field->kind == Ast_Ident); + String name = fv->field->Ident.token.string; + gb_unused(name); + isize param_index = lookup_procedure_parameter(pt, name); + GB_ASSERT(param_index >= 0); + + lbValue value = lb_build_expr(p, fv->value); + GB_ASSERT(!is_type_tuple(value.type)); + args[param_index] = value; + } + + TokenPos pos = ast_token(ce->proc).pos; + + for_array(i, args) { + Entity *e = pt->params->Tuple.variables[i]; + lbValue arg = args[i]; + if (arg.value == nullptr) { + switch (e->kind) { + case Entity_TypeName: + args[i] = lb_const_nil(p->module, e->type); + break; + case Entity_Variable: + args[i] = lb_handle_param_value(p, e->type, e->Variable.param_value, pos); + break; + case Entity_Constant: + GB_PANIC("TODO constant parameter"); + break; + } + } else { + args[i] = lb_emit_conv(p, arg, e->type); + } + } + + + return lb_emit_call(p, value, args, ce->inlining); +} + gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { lbModule *m = p->module; @@ -3219,6 +3331,10 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { GB_ASSERT(proc_type_->kind == Type_Proc); TypeProc *pt = &proc_type_->Proc; + if (ce->ordered_args) { + return lb_build_call_expr_internal_with_arg_ordered_args(p, value, pt, expr, ce->ordered_args); + } + if (is_call_expr_field_value(ce)) { auto args = array_make(permanent_allocator(), pt->param_count); @@ -3274,6 +3390,9 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { isize arg_count = 0; for_array(i, ce->args) { Ast *arg = ce->args[i]; + if (arg->kind == Ast_FieldValue) { + arg = arg->FieldValue.value; + } TypeAndValue tav = type_and_value_of_expr(arg); GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s %d", expr_to_string(arg), expr_to_string(expr), tav.mode); GB_ASSERT_MSG(tav.mode != Addressing_ProcGroup, "%s", expr_to_string(arg)); diff --git a/src/parser.hpp b/src/parser.hpp index 6ba4ef6d6..e0e78fa1d 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -367,6 +367,11 @@ gb_global char const *union_type_kind_strings[UnionType_COUNT] = { "#shared_nil", }; +struct AstOrderedArgs { + Slice positional; + Slice named; +}; + #define AST_KINDS \ AST_KIND(Ident, "identifier", struct { \ Token token; \ @@ -442,6 +447,7 @@ AST_KIND(_ExprBegin, "", bool) \ ProcInlining inlining; \ bool optional_ok_one; \ bool was_selector; \ + AstOrderedArgs *ordered_args; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(EnumFieldValue, "enum field value", struct { \ diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp index 2f22a85d3..3d2e8f27d 100644 --- a/src/parser_pos.cpp +++ b/src/parser_pos.cpp @@ -37,9 +37,9 @@ gb_internal Token ast_token(Ast *node) { return ast_token(node->ImplicitSelectorExpr.selector); } return node->ImplicitSelectorExpr.token; - case Ast_IndexExpr: return node->IndexExpr.open; - case Ast_MatrixIndexExpr: return node->MatrixIndexExpr.open; - case Ast_SliceExpr: return node->SliceExpr.open; + case Ast_IndexExpr: return ast_token(node->IndexExpr.expr); + case Ast_MatrixIndexExpr: return ast_token(node->MatrixIndexExpr.expr); + case Ast_SliceExpr: return ast_token(node->SliceExpr.expr); case Ast_Ellipsis: return node->Ellipsis.token; case Ast_FieldValue: if (node->FieldValue.field) { From dce57627c972276be8398ec0e2efcb7782b650d8 Mon Sep 17 00:00:00 2001 From: Rickard Andersson Date: Thu, 15 Jun 2023 08:54:42 +0300 Subject: [PATCH 047/144] fix: remove redefinition of `timespec` They are in the same package, so it's accessible anyway. --- core/sys/unix/syscalls_linux.odin | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index fdfc27187..92b551041 100644 --- a/core/sys/unix/syscalls_linux.odin +++ b/core/sys/unix/syscalls_linux.odin @@ -2077,11 +2077,6 @@ sys_fcntl :: proc "contextless" (fd: int, cmd: int, arg: int) -> int { sys_poll :: proc "contextless" (fds: rawptr, nfds: uint, timeout: int) -> int { // NOTE: specialcased here because `arm64` does not have `poll` when ODIN_ARCH == .arm64 { - // redefined because we can't depend on the `unix` module here - timespec :: struct { - tv_sec: i64, - tv_nsec: i64, - } seconds := i64(timeout / 1_000) nanoseconds := i64((timeout % 1000) * 1_000_000) timeout_spec := timespec{seconds, nanoseconds} From 37469dc9c2eb2df30995599b80b234526c05f4d1 Mon Sep 17 00:00:00 2001 From: Rickard Andersson Date: Thu, 15 Jun 2023 16:10:00 +0300 Subject: [PATCH 048/144] fix(poll): make interface more odinary We take `fds` as a normal slice and get the length from it instead of bothering with a second parameter. --- core/os/os_linux.odin | 16 +++++++--------- core/sys/unix/syscalls_linux.odin | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index ff3ff700c..e0b60fd36 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -433,13 +433,11 @@ AT_REMOVEDIR :: uintptr(0x200) AT_SYMLINK_NOFOLLOW :: uintptr(0x100) pollfd :: struct { - fd: c.int, - events: c.short, - revents: c.short, + fd: c.int, + events: c.short, + revents: c.short, } -nfds_t :: distinct c.uint - sigset_t :: distinct u64 foreign libc { @@ -1098,16 +1096,16 @@ fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Errno) { return result, ERROR_NONE } -poll :: proc(fds: [^]pollfd, nfds: nfds_t, timeout: int) -> (int, Errno) { - result := unix.sys_poll(fds, uint(nfds), timeout) +poll :: proc(fds: []pollfd, timeout: int) -> (int, Errno) { + result := unix.sys_poll(raw_data(fds), uint(len(fds)), timeout) if result < 0 { return 0, _get_errno(result) } return result, ERROR_NONE } -ppoll :: proc(fds: [^]pollfd, nfds: nfds_t, timeout: ^unix.timespec, sigmask: ^sigset_t) -> (int, Errno) { - result := unix.sys_ppoll(fds, uint(nfds), timeout, sigmask, size_of(sigset_t)) +ppoll :: proc(fds: []pollfd, timeout: ^unix.timespec, sigmask: ^sigset_t) -> (int, Errno) { + result := unix.sys_ppoll(raw_data(fds), uint(len(fds)), timeout, sigmask, size_of(sigset_t)) if result < 0 { return 0, _get_errno(result) } diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index 92b551041..3083c084b 100644 --- a/core/sys/unix/syscalls_linux.odin +++ b/core/sys/unix/syscalls_linux.odin @@ -2088,7 +2088,7 @@ sys_poll :: proc "contextless" (fds: rawptr, nfds: uint, timeout: int) -> int { } sys_ppoll :: proc "contextless" (fds: rawptr, nfds: uint, timeout: rawptr, sigmask: rawptr, sigsetsize: uint) -> int { - return int(intrinsics.syscall(SYS_ppoll, uintptr(fds), uintptr(nfds), uintptr(timeout), uintptr(sigmask), uintptr(sigsetsize))) + return int(intrinsics.syscall(SYS_ppoll, uintptr(fds), uintptr(nfds), uintptr(timeout), uintptr(sigmask), uintptr(sigsetsize))) } get_errno :: proc "contextless" (res: int) -> i32 { From 427f212170114b006fcb6ff1366231f99acff13e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 19 Jun 2023 13:56:00 +0100 Subject: [PATCH 049/144] Begin work in procedure calls --- src/check_expr.cpp | 357 ++++++++++++++++++++++++++++++--------------- 1 file changed, 239 insertions(+), 118 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index cc3f268c3..c3f6002c7 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5976,11 +5976,17 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex CallArgumentData data = {}; data.result_type = t_invalid; - - GB_ASSERT(operand->mode != Addressing_ProcGroup); - proc_type = base_type(operand->type); + if (operand->mode == Addressing_ProcGroup) { + Array procs = proc_group_entities(c, *operand); + if (procs.count == 1) { + proc_type = base_type(procs[0]->type); + } else { + error(call, "Procedure groups >1 not yet supported"); + return data; + } + } if (proc_type == nullptr || proc_type->kind != Type_Proc) { gbString s = type_to_string(proc_type); error(call, "Expected a procedure to call, got %s", s); @@ -5989,12 +5995,15 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex return data; } - if (is_type_polymorphic(proc_type)) { + if (proc_type != nullptr && is_type_polymorphic(proc_type)) { error(call, "Polymorphic procedures not yet supported"); return data; } - TypeProc *pt = &proc_type->Proc; + TypeProc *pt = nullptr; + if (proc_type) { + pt = &proc_type->Proc; + } TEMPORARY_ALLOCATOR_GUARD(); ast_node(ce, CallExpr, call); @@ -6015,21 +6024,31 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex auto positional_operands = array_make(heap_allocator(), 0, positional_args.count); auto variadic_operands = array_make(heap_allocator(), 0, 0); + auto named_operands = array_make(heap_allocator(), 0, 0); defer (array_free(&positional_operands)); defer (array_free(&variadic_operands)); + defer (array_free(&named_operands)); isize positional_operand_count = 0; - auto visited = slice_make(temporary_allocator(), pt->param_count); + Slice visited = {}; if (positional_args.count > 0) { isize lhs_count = -1; bool is_variadic = false; - Entity **lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic); + Entity **lhs = nullptr; + if (proc_type != nullptr && is_type_proc(proc_type)) { + lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic); + } check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); + } + positional_operand_count = positional_operands.count; - if (!is_variadic) { + if (proc_type != nullptr) { + visited = slice_make(temporary_allocator(), pt->param_count); + + if (!pt->variadic) { if (pt->param_count < positional_operands.count) { char const *err_fmt = "Too many arguments for '%s', expected a maximum of %td arguments, got %td"; gbString c = expr_to_string(ce->proc); @@ -6037,27 +6056,24 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex gb_string_free(c); return data; } - } + positional_operand_count = positional_operands.count; + } else { + // only state it is visited if it was set later on + visited[pt->variadic_index] = positional_operand_count > pt->variadic_index; - for (Operand const &o : positional_operands) { - check_no_copy_assignment(o, str_lit("procedure call expression")); - } - - positional_operand_count = gb_min(pt->param_count, positional_operands.count); - if (is_variadic) { positional_operand_count = gb_min(pt->variadic_index, positional_operand_count); } + for (isize i = 0; i < positional_operand_count; i++) { visited[i] = true; } - if (is_variadic) { - visited[pt->variadic_index] = true; - } for (isize i = positional_operand_count; i < positional_operands.count; i++) { array_add(&variadic_operands, positional_operands[i]); } + array_resize(&positional_operands, positional_operand_count); + if (vari_expand) { if (variadic_operands.count > 1) { error(variadic_operands[1].expr, "Unexpected position arguments after variadic expansion with '..'"); @@ -6065,119 +6081,214 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex } } - array_resize(&positional_operands, positional_operand_count); - } + isize param_count = 0; + isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(proc_type, ¶m_count); - isize param_count = 0; - isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(proc_type, ¶m_count); - - if (named_args.count == 0 && positional_operand_count < param_count_excluding_defaults) { - char const *err_fmt = "Too few arguments for '%s', expected a minimum of %td arguments, got %td"; - gbString c = expr_to_string(ce->proc); - error(call, err_fmt, c, param_count_excluding_defaults, positional_operands.count); - gb_string_free(c); - return data; - } - - for_array(i, positional_operands) { - Operand &o = positional_operands[i]; - isize param_index = i; - - Entity *e = pt->params->Tuple.variables[param_index]; - GB_ASSERT(e->kind == Entity_Variable); - - check_assignment(c, &o, e->type, str_lit("procedure call expression")); - } - - if (pt->variadic) { - isize param_index = pt->variadic_index; - - Entity *e = pt->params->Tuple.variables[param_index]; - GB_ASSERT(e->kind == Entity_Variable); - GB_ASSERT(e->flags & EntityFlag_Ellipsis); - GB_ASSERT(is_type_slice(e->type)); - - if (vari_expand) { - if (variadic_operands.count != 0) { - GB_ASSERT(variadic_operands.count == 1); - check_assignment(c, &variadic_operands[0], e->type, str_lit("procedure call expression")); - } - } else { - Type *elem = e->type->Slice.elem; - for (Operand &o : variadic_operands) { - check_assignment(c, &o, elem, str_lit("procedure call expression")); - } - } - } - - array_resize(&positional_operands, pt->param_count); - - for_array(i, named_args) { - Ast *arg = named_args[i]; - if (arg->kind != Ast_FieldValue) { - error(arg, "Expected a 'field = value'"); + if (named_args.count == 0 && positional_operands.count < param_count_excluding_defaults) { + char const *err_fmt = "Too few arguments for '%s', expected a minimum of %td arguments, got %td"; + gbString c = expr_to_string(ce->proc); + error(call, err_fmt, c, param_count_excluding_defaults, positional_operands.count); + gb_string_free(c); return data; } - ast_node(fv, FieldValue, arg); - if (fv->field->kind != Ast_Ident) { - gbString expr_str = expr_to_string(fv->field); - error(arg, "Invalid parameter name '%s' in procedure call", expr_str); - any_failure = true; - gb_string_free(expr_str); - continue; - } - String name = fv->field->Ident.token.string; - isize param_index = lookup_procedure_parameter(pt, name); - if (param_index < 0) { - error(arg, "No parameter named '%.*s' for this procedure type", LIT(name)); - any_failure = true; - continue; + + + for_array(i, positional_operands) { + Operand &o = positional_operands[i]; + isize param_index = i; + + Entity *e = pt->params->Tuple.variables[param_index]; + GB_ASSERT(e->kind == Entity_Variable); + + check_assignment(c, &o, e->type, str_lit("procedure call expression")); } - bool prev_visited = visited[param_index]; - visited[param_index] = true; + if (pt->variadic) { + isize param_index = pt->variadic_index; - if (prev_visited) { - error(arg, "Duplicate parameter named '%.*s' in procedure call", LIT(name)); + Entity *e = pt->params->Tuple.variables[param_index]; + GB_ASSERT(e->kind == Entity_Variable); + GB_ASSERT(e->flags & EntityFlag_Ellipsis); + GB_ASSERT(is_type_slice(e->type)); + + if (vari_expand) { + if (variadic_operands.count != 0) { + GB_ASSERT(variadic_operands.count == 1); + check_assignment(c, &variadic_operands[0], e->type, str_lit("procedure call expression")); + } + } else { + Type *elem = e->type->Slice.elem; + for (Operand &o : variadic_operands) { + check_assignment(c, &o, elem, str_lit("procedure call expression")); + } + } } - Entity *e = pt->params->Tuple.variables[param_index]; - GB_ASSERT(e->kind == Entity_Variable); + array_resize(&positional_operands, pt->param_count); - Operand o = {}; - check_expr_with_type_hint(c, &o, fv->value, e->type); - if (o.mode == Addressing_Invalid) { - any_failure = true; - } - - check_assignment(c, &o, e->type, str_lit("procedure call expression")); - check_no_copy_assignment(o, str_lit("procedure call expression")); - - if (!prev_visited) { - positional_operands[param_index] = o; - } } - for_array(i, visited) { - if (!visited[i]) { - if (pt->variadic && i == pt->variadic_index) { - visited[i] = true; + + if (named_args.count > 0) { + struct NamedParameter { + String key; + Ast * value; + }; + + auto named_parameters = array_make(heap_allocator(), 0, named_args.count); + defer (array_free(&named_parameters)); + + for_array(i, named_args) { + Ast *arg = named_args[i]; + if (arg->kind != Ast_FieldValue) { + error(arg, "Expected a 'field = value'"); + return data; + } + ast_node(fv, FieldValue, arg); + if (fv->field->kind != Ast_Ident) { + gbString expr_str = expr_to_string(fv->field); + error(arg, "Invalid parameter name '%s' in procedure call", expr_str); + any_failure = true; + gb_string_free(expr_str); + continue; + } + String name = fv->field->Ident.token.string; + + array_add(&named_parameters, NamedParameter{name, fv->value}); + } + + StringMap type_hint_map = {}; // Key: String + string_map_init(&type_hint_map, 2*named_args.count); + defer (string_map_destroy(&type_hint_map)); + + if (proc_type != nullptr) { + TypeTuple *param_tuple = &pt->params->Tuple; + for (Entity *e : param_tuple->variables) { + if (is_blank_ident(e->token)) { + continue; + } + string_map_set(&type_hint_map, e->token.string, e->type); + } + } else if (operand->mode == Addressing_ProcGroup) { + Array procs = proc_group_entities(c, *operand); + for (Entity *proc : procs) { + Type *proc_type = base_type(proc->type); + if (is_type_proc(proc_type)) { + TypeProc *pt = &proc_type->Proc; + TypeTuple *param_tuple = nullptr; + if (pt->params != nullptr) { + param_tuple = &pt->params->Tuple; + } + if (param_tuple == nullptr) { + continue; + } + for (Entity *e : param_tuple->variables) { + if (is_blank_ident(e->token)) { + continue; + } + StringHashKey key = string_hash_string(e->token.string); + Type **found = string_map_get(&type_hint_map, key); + if (found) { + Type *t = *found; + if (t == nullptr) { + // NOTE(bill): Ambiguous named parameter across all types + continue; + } + if (are_types_identical(t, e->type)) { + // NOTE(bill): No need to set again + } else { + // NOTE(bill): Ambiguous named parameter across all types so set it to a nullptr + string_map_set(&type_hint_map, key, cast(Type *)nullptr); + } + } else { + string_map_set(&type_hint_map, key, e->type); + } + } + } + } + } + + for (NamedParameter ¶m : named_parameters) { + Type *type_hint = nullptr; + if (Type **found = string_map_get(&type_hint_map, param.key)) { + type_hint = *found; + } + + Operand operand = {}; + check_expr_or_type(c, &operand, param.value, type_hint); + array_add(&named_operands, operand); + } + + for (NamedParameter const ¶m : named_parameters) { + isize param_index = lookup_procedure_parameter(pt, param.key); + if (param_index < 0) { + error(param.value, "No parameter named '%.*s' for this procedure type", LIT(param.key)); + any_failure = true; continue; } - Entity *e = pt->params->Tuple.variables[i]; - if (e->kind == Entity_Variable) { - if (has_parameter_value(e->Variable.param_value)) { + bool prev_visited = visited[param_index]; + visited[param_index] = true; + + if (prev_visited) { + error(param.value, "Duplicate parameter named '%.*s' in procedure call", LIT(param.key)); + } + + Entity *e = pt->params->Tuple.variables[param_index]; + GB_ASSERT(e->kind == Entity_Variable); + + Operand o = {}; + check_expr_with_type_hint(c, &o, param.value, e->type); + if (o.mode == Addressing_Invalid) { + any_failure = true; + } + + check_assignment(c, &o, e->type, str_lit("procedure call expression")); + + if (!prev_visited) { + positional_operands[param_index] = o; + } + } + + } + + if (proc_type != nullptr) { + if (pt->variadic) { + visited[pt->variadic_index] = true; + } + + for_array(i, visited) { + if (!visited[i]) { + if (pt->variadic && i == pt->variadic_index) { visited[i] = true; continue; } + + Entity *e = pt->params->Tuple.variables[i]; + if (e->kind == Entity_Variable) { + if (has_parameter_value(e->Variable.param_value)) { + visited[i] = true; + continue; + } + } + gbString t = type_to_string(e->type); + error(call, "Missing parameter of type '%s' at index %td", t, i); + gb_string_free(t); } - gbString t = type_to_string(e->type); - error(call, "Missing parameter of type '%s' at index %td", t, i); - gb_string_free(t); } } + for (Operand const &o : positional_operands) { + check_no_copy_assignment(o, str_lit("procedure call expression")); + } + for (Operand const &o : variadic_operands) { + check_no_copy_assignment(o, str_lit("procedure call expression")); + } + for (Operand const &o : named_operands) { + check_no_copy_assignment(o, str_lit("procedure call expression")); + } + + if (any_failure) { return data; } @@ -6189,7 +6300,9 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex ce->ordered_args = ordered_args; - data.result_type = pt->results; + if (proc_type != nullptr) { + data.result_type = pt->results; + } return data; #if 0 @@ -6274,8 +6387,6 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex return data; } - error(call, "TODO: Implement check_call_arguments_single_procedure for %s", type_to_string(proc_type)); - Array operands = {}; defer (array_free(&operands)); @@ -6318,6 +6429,20 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Ast *call) { + if (operand->mode == Addressing_ProcGroup) { + Array procs = proc_group_entities(c, *operand); + bool all_non_poly = true; + for (Entity *e : procs) { + if (is_type_polymorphic(e->type)) { + all_non_poly = false; + break; + } + } + if (all_non_poly && (procs.count == 1 || c->pkg->name == "bug")) { + return check_call_arguments_single_procedure(c, operand, call); + } + } + if (operand->mode != Addressing_ProcGroup && is_type_proc(operand->type) && !is_type_polymorphic(operand->type)) { return check_call_arguments_single_procedure(c, operand, call); } @@ -7389,11 +7514,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c return builtin_procs[id].kind; } - if (operand->mode == Addressing_ProcGroup) { - CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure group call"); - } else { - CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure call", true); - } + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN(operand->mode == Addressing_ProcGroup ? "procedure group call": "procedure call", true); Entity *initial_entity = entity_of_node(operand->expr); From 6568625dea679b4622024f62fc14725aa49b2106 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 19 Jun 2023 22:12:47 +0100 Subject: [PATCH 050/144] Fix line error printing for error messages --- src/array.cpp | 4 +- src/check_expr.cpp | 760 +++++++++++++++++++++++++++++++++++--- src/error.cpp | 31 +- src/gb/gb.h | 39 +- src/llvm_backend_proc.cpp | 14 +- src/parser.hpp | 4 +- src/types.cpp | 6 + 7 files changed, 770 insertions(+), 88 deletions(-) diff --git a/src/array.cpp b/src/array.cpp index f1a1f93e2..d8e25d25d 100644 --- a/src/array.cpp +++ b/src/array.cpp @@ -80,7 +80,9 @@ gb_internal Slice slice_make(gbAllocator const &allocator, isize count) { GB_ASSERT(count >= 0); Slice s = {}; s.data = gb_alloc_array(allocator, T, count); - GB_ASSERT(s.data != nullptr); + if (count > 0) { + GB_ASSERT(s.data != nullptr); + } s.count = count; return s; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index c3f6002c7..e0c6be1d1 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -66,7 +66,7 @@ gb_internal int valid_index_and_score_cmp(void const *a, void const *b) { -#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(CheckerContext *c, Ast *call, Type *proc_type, Entity *entity, Array operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data) +#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(CheckerContext *c, Ast *call, Type *proc_type, Entity *entity, Array operands, Array const &named_operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data) typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType); @@ -5314,7 +5314,24 @@ gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize * } +gb_internal isize lookup_procedure_parameter(TypeProc *pt, String parameter_name) { + isize param_count = pt->param_count; + for (isize i = 0; i < param_count; i++) { + Entity *e = pt->params->Tuple.variables[i]; + String name = e->token.string; + if (is_blank_ident(name)) { + continue; + } + if (name == parameter_name) { + return i; + } + } + return -1; +} + gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { + CallArgumentError err = CallArgumentError_None; + ast_node(ce, CallExpr, call); GB_ASSERT(is_type_proc(proc_type)); proc_type = base_type(proc_type); @@ -5327,6 +5344,52 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { i64 score = 0; bool show_error = show_error_mode == CallArgumentMode_ShowErrors; + auto ordered_operands = array_make(temporary_allocator(), pt->param_count); + bool *visited = gb_alloc_array(temporary_allocator(), bool, pt->param_count); + + if (ce->split_args) { + GB_ASSERT(ce->split_args->named.count == named_operands.count); + for_array(i, ce->split_args->named) { + Ast *arg = ce->split_args->named[i]; + Operand operand = named_operands[i]; + + ast_node(fv, FieldValue, arg); + if (fv->field->kind != Ast_Ident) { + if (show_error) { + gbString expr_str = expr_to_string(fv->field); + error(arg, "Invalid parameter name '%s' in procedure call", expr_str); + gb_string_free(expr_str); + } + err = CallArgumentError_InvalidFieldValue; + continue; + } + String name = fv->field->Ident.token.string; + isize param_index = lookup_procedure_parameter(pt, name); + if (param_index < 0) { + if (show_error) { + error(arg, "No parameter named '%.*s' for this procedure type", LIT(name)); + } + err = CallArgumentError_ParameterNotFound; + continue; + } + if (visited[param_index]) { + if (show_error) { + error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name)); + } + err = CallArgumentError_DuplicateParameter; + continue; + } + + visited[param_index] = true; + ordered_operands[param_index] = operand; + + err = CallArgumentError_DuplicateParameter; + } + if (err) { + return err; + } + } + TypeTuple *param_tuple = nullptr; if (pt->params != nullptr) { @@ -5334,7 +5397,6 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { } - CallArgumentError err = CallArgumentError_None; Type *final_proc_type = proc_type; Entity *gen_entity = nullptr; @@ -5571,21 +5633,6 @@ gb_internal bool is_call_expr_field_value(AstCallExpr *ce) { return ce->args[0]->kind == Ast_FieldValue; } -gb_internal isize lookup_procedure_parameter(TypeProc *pt, String parameter_name) { - isize param_count = pt->param_count; - for (isize i = 0; i < param_count; i++) { - Entity *e = pt->params->Tuple.variables[i]; - String name = e->token.string; - if (is_blank_ident(name)) { - continue; - } - if (name == parameter_name) { - return i; - } - } - return -1; -} - gb_internal CALL_ARGUMENT_CHECKER(check_named_call_arguments) { ast_node(ce, CallExpr, call); GB_ASSERT(is_type_proc(proc_type)); @@ -5969,8 +6016,561 @@ gb_internal CallArgumentError check_order_of_call_arguments(CheckerContext *c, T return err; } +gb_internal bool check_named_arguments(CheckerContext *c, Type *type, Slice const &named_args, Array *named_operands, bool show_error) { + bool success = true; -gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContext *c, Operand *operand, Ast *call) { + type = base_type(type); + if (named_args.count > 0) { + TypeProc *pt = nullptr; + if (is_type_proc(type)) { + pt = &type->Proc; + } + + for_array(i, named_args) { + Ast *arg = named_args[i]; + if (arg->kind != Ast_FieldValue) { + if (show_error) { + error(arg, "Expected a 'field = value'"); + } + return false; + } + ast_node(fv, FieldValue, arg); + if (fv->field->kind != Ast_Ident) { + if (show_error) { + gbString expr_str = expr_to_string(fv->field); + error(arg, "Invalid parameter name '%s' in procedure call", expr_str); + gb_string_free(expr_str); + } + success = false; + continue; + } + String key = fv->field->Ident.token.string; + Ast *value = fv->value; + + Type *type_hint = nullptr; + if (pt) { + isize param_index = lookup_procedure_parameter(pt, key); + if (param_index < 0) { + if (show_error) { + error(value, "No parameter named '%.*s' for this procedure type %s", LIT(key), type_to_string(type)); + } + success = false; + continue; + } + + // bool prev_visited = visited[param_index]; + // if (prev_visited) { + // error(value, "Duplicate parameter named '%.*s' in procedure call", LIT(key)); + // success = false; + // } + + Entity *e = pt->params->Tuple.variables[param_index]; + if (!is_type_polymorphic(e->type)) { + type_hint = e->type; + } + + } + Operand o = {}; + check_expr_with_type_hint(c, &o, value, type_hint); + if (o.mode == Addressing_Invalid) { + success = false; + } + array_add(named_operands, o); + } + + } + return success; +} + + +gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(CheckerContext *c, Operand *operand, Ast *call) { + ast_node(ce, CallExpr, call); + GB_ASSERT(ce->split_args != nullptr); + + Slice const &positional_args = ce->split_args->positional; + Slice const &named_args = ce->split_args->named; + + CallArgumentData data = {}; + data.result_type = t_invalid; + + GB_ASSERT(operand->mode == Addressing_ProcGroup); + auto procs = proc_group_entities_cloned(c, *operand); + + if (procs.count > 1) { + isize max_arg_count = positional_args.count + named_args.count; + for (Ast *arg : positional_args) { + // NOTE(bill): The only thing that may have multiple values + // will be a call expression (assuming `or_return` and `()` will be stripped) + arg = strip_or_return_expr(arg); + if (arg && arg->kind == Ast_CallExpr) { + max_arg_count = ISIZE_MAX; + break; + } + } + if (max_arg_count != ISIZE_MAX) for (Ast *arg : named_args) { + // NOTE(bill): The only thing that may have multiple values + // will be a call expression (assuming `or_return` and `()` will be stripped) + if (arg->kind == Ast_FieldValue) { + arg = strip_or_return_expr(arg->FieldValue.value); + if (arg && arg->kind == Ast_CallExpr) { + max_arg_count = ISIZE_MAX; + break; + } + } + } + + for (isize proc_index = 0; proc_index < procs.count; /**/) { + Entity *proc = procs[proc_index]; + Type *pt = base_type(proc->type); + if (!(pt != nullptr && is_type_proc(pt))) { + proc_index++; + continue; + } + + isize param_count = 0; + isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, ¶m_count); + + if (param_count_excluding_defaults > max_arg_count) { + array_unordered_remove(&procs, proc_index); + continue; + } + + // for (Ast *arg : named_args) { + // if (arg->kind != Ast_FieldValue) { + // continue; + // } + // ast_node(fv, FieldValue, arg); + // if (fv->field->kind != Ast_Ident) { + // continue; + // } + // bool ok = false; + // String key = fv->field->Ident.token.string; + // if (param_count) for (Entity *e : pt->Proc.params->Tuple.variables) { + // if (e->token.string == key) { + // ok = true; + // break; + // } + // } + // if (!ok) { + // if (proc_index < procs.count) { + // array_unordered_remove(&procs, proc_index); + // continue; + // } else { + // break; + // } + // } + // } + + proc_index++; + } + } + + Entity **lhs = nullptr; + isize lhs_count = -1; + bool is_variadic = false; + + auto positional_operands = array_make(heap_allocator(), 0, 0); + auto named_operands = array_make(heap_allocator(), 0, 0); + defer (array_free(&positional_operands)); + defer (array_free(&named_operands)); + + if (procs.count == 1) { + Ast *ident = operand->expr; + while (ident->kind == Ast_SelectorExpr) { + Ast *s = ident->SelectorExpr.selector; + ident = s; + } + + + Entity *e = procs[0]; + + lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic); + check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); + + if (!check_named_arguments(c, e->type, named_args, &named_operands, true)) { + return data; + } + + CallArgumentError err = check_call_arguments_internal(c, call, e->type, e, positional_operands, named_operands, CallArgumentMode_ShowErrors, &data); + if (err != CallArgumentError_None) { + // handle error + } + + Type *proc_type = base_type(operand->type); + + Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; + add_entity_use(c, ident, entity_to_use); + if (entity_to_use != nullptr) { + update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); + + proc_type = base_type(entity_to_use->type); + } + + if (proc_type && proc_type->kind == Type_Proc) { + data.result_type = proc_type->Proc.results; + add_type_and_value(c, operand->expr, operand->mode, proc_type, operand->value); + } else if (err == CallArgumentError_None) { + data.result_type = nullptr; + } + + if (data.gen_entity != nullptr) { + Entity *e = data.gen_entity; + DeclInfo *decl = data.gen_entity->decl_info; + CheckerContext ctx = *c; + ctx.scope = decl->scope; + ctx.decl = decl; + ctx.proc_name = e->token.string; + ctx.curr_proc_decl = decl; + ctx.curr_proc_sig = e->type; + + GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); + bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); + decl->where_clauses_evaluated = true; + + if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { + check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); + } + } + return data; + } + + { + // NOTE(bill, 2019-07-13): This code is used to improve the type inference for procedure groups + // where the same positional parameter has the same type value (and ellipsis) + bool proc_arg_count_all_equal = true; + isize proc_arg_count = -1; + for (Entity *p : procs) { + Type *pt = base_type(p->type); + if (pt != nullptr && is_type_proc(pt)) { + if (proc_arg_count < 0) { + proc_arg_count = pt->Proc.param_count; + } else { + if (proc_arg_count != pt->Proc.param_count) { + proc_arg_count_all_equal = false; + break; + } + } + } + } + + if (proc_arg_count >= 0 && proc_arg_count_all_equal) { + lhs_count = proc_arg_count; + if (lhs_count > 0) { + lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count); + for (isize param_index = 0; param_index < lhs_count; param_index++) { + Entity *e = nullptr; + for (Entity *p : procs) { + Type *pt = base_type(p->type); + if (!(pt != nullptr && is_type_proc(pt))) { + continue; + } + + if (e == nullptr) { + e = pt->Proc.params->Tuple.variables[param_index]; + } else { + Entity *f = pt->Proc.params->Tuple.variables[param_index]; + if (e == f) { + continue; + } + if (are_types_identical(e->type, f->type)) { + bool ee = (e->flags & EntityFlag_Ellipsis) != 0; + bool fe = (f->flags & EntityFlag_Ellipsis) != 0; + if (ee == fe) { + continue; + } + } + // NOTE(bill): Entities are not close enough to be used + e = nullptr; + break; + } + } + lhs[param_index] = e; + } + } + } + } + + check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); + + for_array(i, named_args) { + Ast *arg = named_args[i]; + if (arg->kind != Ast_FieldValue) { + error(arg, "Expected a 'field = value'"); + return data; + } + ast_node(fv, FieldValue, arg); + if (fv->field->kind != Ast_Ident) { + gbString expr_str = expr_to_string(fv->field); + error(arg, "Invalid parameter name '%s' in procedure call", expr_str); + gb_string_free(expr_str); + return data; + } + String key = fv->field->Ident.token.string; + Ast *value = fv->value; + + Type *type_hint = nullptr; + + for (isize lhs_idx = 0; lhs_idx < lhs_count; lhs_idx++) { + Entity *e = lhs[lhs_idx]; + if (e != nullptr && e->token.string == key && + !is_type_polymorphic(e->type)) { + type_hint = e->type; + break; + } + } + Operand o = {}; + check_expr_with_type_hint(c, &o, value, type_hint); + array_add(&named_operands, o); + } + + gb_free(heap_allocator(), lhs); + + auto valids = array_make(heap_allocator(), 0, procs.count); + defer (array_free(&valids)); + + auto proc_entities = array_make(heap_allocator(), 0, procs.count*2 + 1); + defer (array_free(&proc_entities)); + for (Entity *proc : procs) { + array_add(&proc_entities, proc); + } + + + gbString expr_name = expr_to_string(operand->expr); + defer (gb_string_free(expr_name)); + + for_array(i, procs) { + Entity *p = procs[i]; + Type *pt = base_type(p->type); + if (pt != nullptr && is_type_proc(pt)) { + CallArgumentError err = CallArgumentError_None; + CallArgumentData data = {}; + CheckerContext ctx = *c; + + ctx.no_polymorphic_errors = true; + ctx.allow_polymorphic_types = is_type_polymorphic(pt); + ctx.hide_polymorphic_errors = true; + + err = check_call_arguments_internal(&ctx, call, pt, p, positional_operands, named_operands, CallArgumentMode_NoErrors, &data); + if (err != CallArgumentError_None) { + continue; + } + isize index = i; + + if (data.gen_entity != nullptr) { + Entity *e = data.gen_entity; + DeclInfo *decl = data.gen_entity->decl_info; + ctx.scope = decl->scope; + ctx.decl = decl; + ctx.proc_name = e->token.string; + ctx.curr_proc_decl = decl; + ctx.curr_proc_sig = e->type; + + GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); + if (!evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) { + continue; + } + + array_add(&proc_entities, data.gen_entity); + index = proc_entities.count-1; + } + + ValidIndexAndScore item = {}; + item.index = index; + item.score = data.score; + array_add(&valids, item); + } + } + + if (valids.count > 1) { + gb_sort_array(valids.data, valids.count, valid_index_and_score_cmp); + i64 best_score = valids[0].score; + Entity *best_entity = proc_entities[valids[0].index]; + GB_ASSERT(best_entity != nullptr); + for (isize i = 1; i < valids.count; i++) { + if (best_score > valids[i].score) { + valids.count = i; + break; + } + if (best_entity == proc_entities[valids[i].index]) { + valids.count = i; + break; + } + } + } + + auto print_argument_types = [&]() { + error_line("\tGiven argument types: ("); + isize i = 0; + for (Operand const &o : positional_operands) { + if (i++ > 0) error_line(", "); + gbString type = type_to_string(o.type); + defer (gb_string_free(type)); + error_line("%s", type); + } + for (Operand const &o : named_operands) { + if (i++ > 0) error_line(", "); + + gbString type = type_to_string(o.type); + defer (gb_string_free(type)); + + if (i < ce->split_args->named.count) { + Ast *named_field = ce->split_args->named[i]; + ast_node(fv, FieldValue, named_field); + + gbString field = expr_to_string(fv->field); + defer (gb_string_free(field)); + + error_line("%s = %s", field, type); + } else { + error_line("%s", type); + } + } + error_line(")\n"); + }; + + if (valids.count == 0) { + begin_error_block(); + defer (end_error_block()); + + error(operand->expr, "No procedures or ambiguous call for procedure group '%s' that match with the given arguments", expr_name); + if (positional_operands.count == 0 && named_operands.count == 0) { + error_line("\tNo given arguments\n"); + } else { + print_argument_types(); + } + + if (procs.count > 0) { + error_line("Did you mean to use one of the following:\n"); + } + for (Entity *proc : procs) { + TokenPos pos = proc->token.pos; + Type *t = base_type(proc->type); + if (t == t_invalid) continue; + GB_ASSERT(t->kind == Type_Proc); + gbString pt; + defer (gb_string_free(pt)); + if (t->Proc.node != nullptr) { + pt = expr_to_string(t->Proc.node); + } else { + pt = type_to_string(t); + } + String prefix = {}; + String prefix_sep = {}; + if (proc->pkg) { + prefix = proc->pkg->name; + prefix_sep = str_lit("."); + } + String name = proc->token.string; + + char const *sep = "::"; + if (proc->kind == Entity_Variable) { + sep = ":="; + } + error_line("\t%.*s%.*s%.*s %s %s at %s\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, token_pos_to_string(pos)); + } + if (procs.count > 0) { + error_line("\n"); + } + + data.result_type = t_invalid; + } else if (valids.count > 1) { + begin_error_block(); + defer (end_error_block()); + + error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name); + print_argument_types(); + + for (isize i = 0; i < valids.count; i++) { + Entity *proc = proc_entities[valids[i].index]; + GB_ASSERT(proc != nullptr); + TokenPos pos = proc->token.pos; + Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc); + gbString pt = nullptr; + defer (gb_string_free(pt)); + if (t->Proc.node != nullptr) { + pt = expr_to_string(t->Proc.node); + } else { + pt = type_to_string(t); + } + String name = proc->token.string; + char const *sep = "::"; + if (proc->kind == Entity_Variable) { + sep = ":="; + } + error_line("\t%.*s %s %s ", LIT(name), sep, pt); + if (proc->decl_info->proc_lit != nullptr) { + GB_ASSERT(proc->decl_info->proc_lit->kind == Ast_ProcLit); + auto *pl = &proc->decl_info->proc_lit->ProcLit; + if (pl->where_token.kind != Token_Invalid) { + error_line("\n\t\twhere "); + for_array(j, pl->where_clauses) { + Ast *clause = pl->where_clauses[j]; + if (j != 0) { + error_line("\t\t "); + } + gbString str = expr_to_string(clause); + error_line("%s", str); + gb_string_free(str); + + if (j != pl->where_clauses.count-1) { + error_line(","); + } + } + error_line("\n\t"); + } + } + error_line("at %s\n", token_pos_to_string(pos)); + } + data.result_type = t_invalid; + } else { + GB_ASSERT(valids.count == 1); + Ast *ident = operand->expr; + while (ident->kind == Ast_SelectorExpr) { + Ast *s = ident->SelectorExpr.selector; + ident = s; + } + + Entity *e = proc_entities[valids[0].index]; + GB_ASSERT(e != nullptr); + + Array named_operands = {}; + + Type *proc_type = e->type; + CallArgumentData data = {}; + CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, positional_operands, named_operands, CallArgumentMode_ShowErrors, &data); + gb_unused(err); + Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; + add_entity_use(c, ident, entity_to_use); + if (entity_to_use != nullptr) { + update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); + } + + if (data.gen_entity != nullptr) { + Entity *e = data.gen_entity; + DeclInfo *decl = data.gen_entity->decl_info; + CheckerContext ctx = *c; + ctx.scope = decl->scope; + ctx.decl = decl; + ctx.proc_name = e->token.string; + ctx.curr_proc_decl = decl; + ctx.curr_proc_sig = e->type; + + GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); + bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); + decl->where_clauses_evaluated = true; + + if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { + check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); + } + } + return data; + } + + return data; +} + + +gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContext *c, Operand *operand, Ast *call) { Type *proc_type = nullptr; CallArgumentData data = {}; @@ -5978,23 +6578,6 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex proc_type = base_type(operand->type); - if (operand->mode == Addressing_ProcGroup) { - Array procs = proc_group_entities(c, *operand); - if (procs.count == 1) { - proc_type = base_type(procs[0]->type); - } else { - error(call, "Procedure groups >1 not yet supported"); - return data; - } - } - if (proc_type == nullptr || proc_type->kind != Type_Proc) { - gbString s = type_to_string(proc_type); - error(call, "Expected a procedure to call, got %s", s); - gb_string_free(s); - - return data; - } - if (proc_type != nullptr && is_type_polymorphic(proc_type)) { error(call, "Polymorphic procedures not yet supported"); return data; @@ -6012,19 +6595,38 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex bool vari_expand = (ce->ellipsis.pos.line != 0); - auto positional_args = ce->args; - for (isize i = 0; i < ce->args.count; i++) { - Ast *arg = ce->args.data[i]; - if (arg->kind == Ast_FieldValue) { - positional_args.count = i; - break; + // Split positional and named args into separate arrays/slices + Slice positional_args = {}; + Slice named_args = {}; + + if (ce->split_args == nullptr) { + positional_args = ce->args; + for (isize i = 0; i < ce->args.count; i++) { + Ast *arg = ce->args.data[i]; + if (arg->kind == Ast_FieldValue) { + positional_args.count = i; + break; + } } + named_args = slice(ce->args, positional_args.count, ce->args.count); + + auto split_args = gb_alloc_item(permanent_allocator(), AstSplitArgs); + split_args->positional = positional_args; + split_args->named = named_args; + ce->split_args = split_args; + } else { + positional_args = ce->split_args->positional; + named_args = ce->split_args->named; } - auto named_args = slice(ce->args, positional_args.count, ce->args.count); + + if (operand->mode == Addressing_ProcGroup) { + return check_call_arguments_new_and_improved_proc_group(c, operand, call); + } + auto positional_operands = array_make(heap_allocator(), 0, positional_args.count); - auto variadic_operands = array_make(heap_allocator(), 0, 0); - auto named_operands = array_make(heap_allocator(), 0, 0); + auto variadic_operands = array_make(heap_allocator(), 0, 0); + auto named_operands = array_make(heap_allocator(), 0, 0); defer (array_free(&positional_operands)); defer (array_free(&variadic_operands)); @@ -6038,14 +6640,16 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex isize lhs_count = -1; bool is_variadic = false; Entity **lhs = nullptr; - if (proc_type != nullptr && is_type_proc(proc_type)) { + if (pt != nullptr) { lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic); + } else if (operand->mode == Addressing_ProcGroup) { + // TODO } check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); } positional_operand_count = positional_operands.count; - if (proc_type != nullptr) { + if (pt != nullptr) { visited = slice_make(temporary_allocator(), pt->param_count); if (!pt->variadic) { @@ -6161,7 +6765,7 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex string_map_init(&type_hint_map, 2*named_args.count); defer (string_map_destroy(&type_hint_map)); - if (proc_type != nullptr) { + if (pt != nullptr) { TypeTuple *param_tuple = &pt->params->Tuple; for (Entity *e : param_tuple->variables) { if (is_blank_ident(e->token)) { @@ -6252,7 +6856,7 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex } - if (proc_type != nullptr) { + if (pt != nullptr) { if (pt->variadic) { visited[pt->variadic_index] = true; } @@ -6293,14 +6897,45 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex return data; } + { + Ast *ident = operand->expr; + while (ident->kind == Ast_SelectorExpr) { + Ast *s = ident->SelectorExpr.selector; + ident = s; + } - auto ordered_args = gb_alloc_item(permanent_allocator(), AstOrderedArgs); - ordered_args->positional = positional_args; - ordered_args->named = named_args; + Entity *e = entity_of_node(ident); - ce->ordered_args = ordered_args; + Array operands = array_clone(heap_allocator(), positional_operands); - if (proc_type != nullptr) { + CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, operands, named_operands, CallArgumentMode_ShowErrors, &data); + gb_unused(err); + Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; + add_entity_use(c, ident, entity_to_use); + if (entity_to_use != nullptr) { + update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); + } + if (data.gen_entity != nullptr) { + Entity *e = data.gen_entity; + DeclInfo *decl = data.gen_entity->decl_info; + CheckerContext ctx = *c; + ctx.scope = decl->scope; + ctx.decl = decl; + ctx.proc_name = e->token.string; + ctx.curr_proc_decl = decl; + ctx.curr_proc_sig = e->type; + + GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); + bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); + decl->where_clauses_evaluated = true; + + if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { + check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); + } + } + } + + if (pt != nullptr) { data.result_type = pt->results; } return data; @@ -6438,13 +7073,13 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op break; } } - if (all_non_poly && (procs.count == 1 || c->pkg->name == "bug")) { - return check_call_arguments_single_procedure(c, operand, call); + if (all_non_poly) { + return check_call_arguments_new_and_improved(c, operand, call); } } if (operand->mode != Addressing_ProcGroup && is_type_proc(operand->type) && !is_type_polymorphic(operand->type)) { - return check_call_arguments_single_procedure(c, operand, call); + return check_call_arguments_new_and_improved(c, operand, call); } ast_node(ce, CallExpr, call); @@ -6635,8 +7270,9 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic); check_unpack_arguments(c, lhs, lhs_count, &operands, args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); + Array named_operands = {}; CallArgumentData data = {}; - CallArgumentError err = call_checker(c, call, e->type, e, operands, CallArgumentMode_ShowErrors, &data); + CallArgumentError err = call_checker(c, call, e->type, e, operands, named_operands, CallArgumentMode_ShowErrors, &data); if (err != CallArgumentError_None) { // handle error } @@ -6740,7 +7376,8 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op ctx.allow_polymorphic_types = is_type_polymorphic(pt); ctx.hide_polymorphic_errors = true; - err = call_checker(&ctx, call, pt, p, operands, CallArgumentMode_NoErrors, &data); + Array named_operands = {}; + err = call_checker(&ctx, call, pt, p, operands, named_operands, CallArgumentMode_NoErrors, &data); if (err != CallArgumentError_None) { continue; } @@ -6910,9 +7547,11 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op Entity *e = proc_entities[valids[0].index]; GB_ASSERT(e != nullptr); + Array named_operands = {}; + proc_type = e->type; CallArgumentData data = {}; - CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data); + CallArgumentError err = call_checker(c, call, proc_type, e, operands, named_operands, CallArgumentMode_ShowErrors, &data); gb_unused(err); Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; add_entity_use(c, ident, entity_to_use); @@ -6949,9 +7588,10 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op Entity *e = entity_of_node(ident); + Array named_operands = {}; CallArgumentData data = {}; - CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data); + CallArgumentError err = call_checker(c, call, proc_type, e, operands, named_operands, CallArgumentMode_ShowErrors, &data); gb_unused(err); Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; add_entity_use(c, ident, entity_to_use); diff --git a/src/error.cpp b/src/error.cpp index defc2593f..eb010eb36 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -265,7 +265,8 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { defer (gb_string_free(the_line)); if (the_line != nullptr) { - String line = make_string(cast(u8 const *)the_line, gb_string_length(the_line)); + char const *line_text = the_line; + isize line_len = gb_string_length(the_line); // TODO(bill): This assumes ASCII @@ -285,21 +286,27 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { isize squiggle_extra = 0; - if (line.len > MAX_LINE_LENGTH_PADDED) { + if (line_len > MAX_LINE_LENGTH_PADDED) { i32 left = MAX_TAB_WIDTH; - line.text += offset-left; - line.len -= offset-left; - offset = left+MAX_TAB_WIDTH/2; - if (line.len > MAX_LINE_LENGTH_PADDED) { - line.len = MAX_LINE_LENGTH_PADDED; - if (error_length > line.len-left) { - error_length = cast(i32)line.len - left; + if (offset > 0) { + line_text += offset-left; + line_len -= offset-left; + offset = left+MAX_TAB_WIDTH/2; + } + if (line_len > MAX_LINE_LENGTH_PADDED) { + line_len = MAX_LINE_LENGTH_PADDED; + if (error_length > line_len-left) { + error_length = cast(i32)line_len - left; squiggle_extra = 1; } } - error_out("... %.*s ...", LIT(line)); + if (offset > 0) { + error_out("... %.*s ...", cast(i32)line_len, line_text); + } else { + error_out("%.*s ...", cast(i32)line_len, line_text); + } } else { - error_out("%.*s", LIT(line)); + error_out("%.*s", cast(i32)line_len, line_text); } error_out("\n\t"); @@ -312,7 +319,7 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { error_out("^"); if (end.file_id == pos.file_id) { if (end.line > pos.line) { - for (i32 i = offset; i < line.len; i++) { + for (i32 i = offset; i < line_len; i++) { error_out("~"); } } else if (end.line == pos.line && end.column > pos.column) { diff --git a/src/gb/gb.h b/src/gb/gb.h index bc4c1f27d..3d4bff9b4 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -3299,12 +3299,39 @@ void const *gb_memrchr(void const *data, u8 c, isize n) { -gb_inline void *gb_alloc_align (gbAllocator a, isize size, isize alignment) { return a.proc(a.data, gbAllocation_Alloc, size, alignment, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); } -gb_inline void *gb_alloc (gbAllocator a, isize size) { return gb_alloc_align(a, size, GB_DEFAULT_MEMORY_ALIGNMENT); } -gb_inline void gb_free (gbAllocator a, void *ptr) { if (ptr != NULL) a.proc(a.data, gbAllocation_Free, 0, 0, ptr, 0, GB_DEFAULT_ALLOCATOR_FLAGS); } -gb_inline void gb_free_all (gbAllocator a) { a.proc(a.data, gbAllocation_FreeAll, 0, 0, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); } -gb_inline void *gb_resize (gbAllocator a, void *ptr, isize old_size, isize new_size) { return gb_resize_align(a, ptr, old_size, new_size, GB_DEFAULT_MEMORY_ALIGNMENT); } -gb_inline void *gb_resize_align(gbAllocator a, void *ptr, isize old_size, isize new_size, isize alignment) { return a.proc(a.data, gbAllocation_Resize, new_size, alignment, ptr, old_size, GB_DEFAULT_ALLOCATOR_FLAGS); } +gb_inline void *gb_alloc_align (gbAllocator a, isize size, isize alignment) { + if (size == 0) { + return NULL; + } + return a.proc(a.data, gbAllocation_Alloc, size, alignment, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); +} +gb_inline void *gb_alloc(gbAllocator a, isize size) { + return gb_alloc_align(a, size, GB_DEFAULT_MEMORY_ALIGNMENT); +} +gb_inline void gb_free(gbAllocator a, void *ptr) { + if (ptr != NULL) { + a.proc(a.data, gbAllocation_Free, 0, 0, ptr, 0, GB_DEFAULT_ALLOCATOR_FLAGS); + } +} +gb_inline void gb_free_all(gbAllocator a) { + a.proc(a.data, gbAllocation_FreeAll, 0, 0, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); +} +gb_inline void *gb_resize(gbAllocator a, void *ptr, isize old_size, isize new_size) { + return gb_resize_align(a, ptr, old_size, new_size, GB_DEFAULT_MEMORY_ALIGNMENT); +} +gb_inline void *gb_resize_align(gbAllocator a, void *ptr, isize old_size, isize new_size, isize alignment) { + if (new_size == 0) { + if (ptr != NULL) { + return a.proc(a.data, gbAllocation_Free, 0, 0, ptr, old_size, GB_DEFAULT_ALLOCATOR_FLAGS); + } + return NULL; + } else if (ptr == NULL) { + return a.proc(a.data, gbAllocation_Alloc, new_size, alignment, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); + } else if (old_size == new_size && ((uintptr)ptr % (uintptr)alignment) == 0) { + return ptr; + } + return a.proc(a.data, gbAllocation_Resize, new_size, alignment, ptr, old_size, GB_DEFAULT_ALLOCATOR_FLAGS); +} gb_inline void *gb_alloc_copy (gbAllocator a, void const *src, isize size) { return gb_memcopy(gb_alloc(a, size), src, size); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 3c1cfe47c..046016c75 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3156,20 +3156,20 @@ gb_internal void lb_add_values_to_array(lbProcedure *p, Array *args, lb array_add(args, value); } } -gb_internal lbValue lb_build_call_expr_internal_with_arg_ordered_args(lbProcedure *p, lbValue value, TypeProc *pt, Ast *call, AstOrderedArgs *ordered_args) { +gb_internal lbValue lb_build_call_expr_internal_with_arg_split_args(lbProcedure *p, lbValue value, TypeProc *pt, Ast *call, AstSplitArgs *split_args) { ast_node(ce, CallExpr, call); auto args = array_make(permanent_allocator(), 0, pt->param_count); bool vari_expand = (ce->ellipsis.pos.line != 0); - for_array(i, ordered_args->positional) { + for_array(i, split_args->positional) { Entity *e = pt->params->Tuple.variables[i]; GB_ASSERT(e->kind == Entity_Variable); if (pt->variadic && pt->variadic_index == i) { lbValue variadic_args = lb_const_nil(p->module, e->type); - auto variadic = slice(ordered_args->positional, pt->variadic_index, ordered_args->positional.count); + auto variadic = slice(split_args->positional, pt->variadic_index, split_args->positional.count); if (variadic.count != 0) { // variadic call argument generation Type *slice_type = e->type; @@ -3211,14 +3211,14 @@ gb_internal lbValue lb_build_call_expr_internal_with_arg_ordered_args(lbProcedur break; } else { - lbValue value = lb_build_expr(p, ordered_args->positional[i]); + lbValue value = lb_build_expr(p, split_args->positional[i]); lb_add_values_to_array(p, &args, value); } } array_resize(&args, pt->param_count); - for (Ast *arg : ordered_args->named) { + for (Ast *arg : split_args->named) { ast_node(fv, FieldValue, arg); GB_ASSERT(fv->field->kind == Ast_Ident); String name = fv->field->Ident.token.string; @@ -3331,8 +3331,8 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { GB_ASSERT(proc_type_->kind == Type_Proc); TypeProc *pt = &proc_type_->Proc; - if (ce->ordered_args) { - return lb_build_call_expr_internal_with_arg_ordered_args(p, value, pt, expr, ce->ordered_args); + if (ce->split_args) { + return lb_build_call_expr_internal_with_arg_split_args(p, value, pt, expr, ce->split_args); } if (is_call_expr_field_value(ce)) { diff --git a/src/parser.hpp b/src/parser.hpp index e0e78fa1d..900fddbab 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -367,7 +367,7 @@ gb_global char const *union_type_kind_strings[UnionType_COUNT] = { "#shared_nil", }; -struct AstOrderedArgs { +struct AstSplitArgs { Slice positional; Slice named; }; @@ -447,7 +447,7 @@ AST_KIND(_ExprBegin, "", bool) \ ProcInlining inlining; \ bool optional_ok_one; \ bool was_selector; \ - AstOrderedArgs *ordered_args; \ + AstSplitArgs *split_args; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(EnumFieldValue, "enum field value", struct { \ diff --git a/src/types.cpp b/src/types.cpp index 3cc077f84..f3b7f5bab 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -3326,6 +3326,9 @@ gb_internal bool are_struct_fields_reordered(Type *type) { type = base_type(type); GB_ASSERT(type->kind == Type_Struct); type_set_offsets(type); + if (type->Struct.fields.count == 0) { + return false; + } GB_ASSERT(type->Struct.offsets != nullptr); i64 prev_offset = 0; @@ -3344,6 +3347,9 @@ gb_internal Slice struct_fields_index_by_increasing_offset(gbAllocator allo type = base_type(type); GB_ASSERT(type->kind == Type_Struct); type_set_offsets(type); + if (type->Struct.fields.count == 0) { + return {}; + } GB_ASSERT(type->Struct.offsets != nullptr); auto indices = slice_make(allocator, type->Struct.fields.count); From f26e3c65098e5c95ac7d446f2a6db5f136c509f2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 19 Jun 2023 22:26:43 +0100 Subject: [PATCH 051/144] Improve proc group selection with named arguments --- src/check_expr.cpp | 67 ++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index e0c6be1d1..eb3713d82 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5314,7 +5314,7 @@ gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize * } -gb_internal isize lookup_procedure_parameter(TypeProc *pt, String parameter_name) { +gb_internal isize lookup_procedure_parameter(TypeProc *pt, String const ¶meter_name) { isize param_count = pt->param_count; for (isize i = 0; i < param_count; i++) { Entity *e = pt->params->Tuple.variables[i]; @@ -5329,6 +5329,13 @@ gb_internal isize lookup_procedure_parameter(TypeProc *pt, String parameter_name return -1; } +gb_internal isize lookup_procedure_parameter(Type *type, String const ¶meter_name) { + type = base_type(type); + GB_ASSERT(type->kind == Type_Proc); + return lookup_procedure_parameter(&type->Proc, parameter_name); +} + + gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { CallArgumentError err = CallArgumentError_None; @@ -6052,7 +6059,7 @@ gb_internal bool check_named_arguments(CheckerContext *c, Type *type, Slicekind != Ast_FieldValue) { + continue; + } + ast_node(fv, FieldValue, arg); + if (fv->field->kind != Ast_Ident) { + continue; + } + String key = fv->field->Ident.token.string; + for (isize proc_index = procs.count-1; proc_index >= 0; proc_index--) { + Type *t = procs[proc_index]->type; + if (is_type_proc(t)) { + isize param_index = lookup_procedure_parameter(t, key); + if (param_index < 0) { + array_unordered_remove(&procs, proc_index); + } + } + } + } + + if (procs.count == 0) { + // if any of the named arguments are wrong, the `procs` will be empty + // just start from scratch + array_free(&procs); + procs = proc_group_entities_cloned(c, *operand); + } + + // filter by positional argument length for (isize proc_index = 0; proc_index < procs.count; /**/) { Entity *proc = procs[proc_index]; Type *pt = base_type(proc->type); @@ -6134,33 +6170,6 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch array_unordered_remove(&procs, proc_index); continue; } - - // for (Ast *arg : named_args) { - // if (arg->kind != Ast_FieldValue) { - // continue; - // } - // ast_node(fv, FieldValue, arg); - // if (fv->field->kind != Ast_Ident) { - // continue; - // } - // bool ok = false; - // String key = fv->field->Ident.token.string; - // if (param_count) for (Entity *e : pt->Proc.params->Tuple.variables) { - // if (e->token.string == key) { - // ok = true; - // break; - // } - // } - // if (!ok) { - // if (proc_index < procs.count) { - // array_unordered_remove(&procs, proc_index); - // continue; - // } else { - // break; - // } - // } - // } - proc_index++; } } From 15a0d9f9008362882aed152ba3fecd09f4ce7b28 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 19 Jun 2023 23:46:06 +0100 Subject: [PATCH 052/144] Begin to handle split arguments correctly --- src/check_expr.cpp | 560 ++++++++++++++++++++++----------------------- 1 file changed, 279 insertions(+), 281 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index eb3713d82..92b572352 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -66,7 +66,7 @@ gb_internal int valid_index_and_score_cmp(void const *a, void const *b) { -#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(CheckerContext *c, Ast *call, Type *proc_type, Entity *entity, Array operands, Array const &named_operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data) +#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(CheckerContext *c, Ast *call, Type *proc_type, Entity *entity, Array positional_operands, Array const &named_operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data) typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType); @@ -5351,59 +5351,6 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { i64 score = 0; bool show_error = show_error_mode == CallArgumentMode_ShowErrors; - auto ordered_operands = array_make(temporary_allocator(), pt->param_count); - bool *visited = gb_alloc_array(temporary_allocator(), bool, pt->param_count); - - if (ce->split_args) { - GB_ASSERT(ce->split_args->named.count == named_operands.count); - for_array(i, ce->split_args->named) { - Ast *arg = ce->split_args->named[i]; - Operand operand = named_operands[i]; - - ast_node(fv, FieldValue, arg); - if (fv->field->kind != Ast_Ident) { - if (show_error) { - gbString expr_str = expr_to_string(fv->field); - error(arg, "Invalid parameter name '%s' in procedure call", expr_str); - gb_string_free(expr_str); - } - err = CallArgumentError_InvalidFieldValue; - continue; - } - String name = fv->field->Ident.token.string; - isize param_index = lookup_procedure_parameter(pt, name); - if (param_index < 0) { - if (show_error) { - error(arg, "No parameter named '%.*s' for this procedure type", LIT(name)); - } - err = CallArgumentError_ParameterNotFound; - continue; - } - if (visited[param_index]) { - if (show_error) { - error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name)); - } - err = CallArgumentError_DuplicateParameter; - continue; - } - - visited[param_index] = true; - ordered_operands[param_index] = operand; - - err = CallArgumentError_DuplicateParameter; - } - if (err) { - return err; - } - } - - - TypeTuple *param_tuple = nullptr; - if (pt->params != nullptr) { - param_tuple = &pt->params->Tuple; - } - - Type *final_proc_type = proc_type; Entity *gen_entity = nullptr; @@ -5421,7 +5368,263 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { LIT(ce->proc->Ident.token.string)); } err = CallArgumentError_NonVariadicExpand; - } else if (operands.count == 0 && param_count_excluding_defaults == 0) { + } + + if (ce->split_args) { + auto ordered_operands = array_make(temporary_allocator(), pt->param_count); + auto visited = slice_make(temporary_allocator(), pt->param_count); + + isize positional_operand_count = positional_operands.count; + if (variadic) { + positional_operand_count = gb_min(positional_operands.count, pt->variadic_index); + } else if (positional_operand_count > pt->param_count) { + err = CallArgumentError_TooManyArguments; + char const *err_fmt = "Too many arguments for '%s', expected %td arguments, got %td"; + if (show_error) { + gbString proc_str = expr_to_string(ce->proc); + defer (gb_string_free(proc_str)); + error(call, err_fmt, proc_str, param_count_excluding_defaults, positional_operands.count); + } + return err; + } + positional_operand_count = gb_min(positional_operand_count, pt->param_count); + + for (isize i = 0; i < positional_operand_count; i++) { + ordered_operands[i] = positional_operands[i]; + visited[i] = true; + } + + auto variadic_operands = slice(slice_from_array(positional_operands), positional_operand_count, positional_operands.count); + + if (named_operands.count != 0) { + GB_ASSERT(ce->split_args->named.count == named_operands.count); + for_array(i, ce->split_args->named) { + Ast *arg = ce->split_args->named[i]; + Operand operand = named_operands[i]; + + ast_node(fv, FieldValue, arg); + if (fv->field->kind != Ast_Ident) { + if (show_error) { + gbString expr_str = expr_to_string(fv->field); + error(arg, "Invalid parameter name '%s' in procedure call", expr_str); + gb_string_free(expr_str); + } + err = CallArgumentError_InvalidFieldValue; + continue; + } + String name = fv->field->Ident.token.string; + isize param_index = lookup_procedure_parameter(pt, name); + if (param_index < 0) { + if (show_error) { + error(arg, "No parameter named '%.*s' for this procedure type", LIT(name)); + } + err = CallArgumentError_ParameterNotFound; + continue; + } + if (visited[param_index]) { + if (show_error) { + error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name)); + } + err = CallArgumentError_DuplicateParameter; + continue; + } + Entity *e = pt->params->Tuple.variables[param_index]; + + check_assignment(c, &operand, e->type, str_lit("named argument")); + + visited[param_index] = true; + ordered_operands[param_index] = operand; + + err = CallArgumentError_DuplicateParameter; + } + } + + if (variadic) { + if (visited[pt->variadic_index] && + positional_operand_count < positional_operands.count) { + if (show_error) { + String name = pt->params->Tuple.variables[pt->variadic_index]->token.string; + error(call, "Variadic parameters already handled with a named argument '%.*s' in procedure call", LIT(name)); + } + err = CallArgumentError_DuplicateParameter; + } else { + visited[pt->variadic_index] = true; + } + + } + + + for (Operand const &o : ordered_operands) { + if (o.mode != Addressing_Invalid) { + check_no_copy_assignment(o, str_lit("procedure call expression")); + } + } + + for (isize i = 0; i < pt->param_count; i++) { + if (!visited[i]) { + Entity *e = pt->params->Tuple.variables[i]; + if (is_blank_ident(e->token)) { + continue; + } + if (e->kind == Entity_Variable) { + if (e->Variable.param_value.kind != ParameterValue_Invalid) { + score += assign_score_function(1); + continue; + } + } + + if (show_error) { + if (e->kind == Entity_TypeName) { + error(call, "Type parameter '%.*s' is missing in procedure call", + LIT(e->token.string)); + } else if (e->kind == Entity_Constant && e->Constant.value.kind != ExactValue_Invalid) { + // Ignore + } else { + gbString str = type_to_string(e->type); + error(call, "Parameter '%.*s' of type '%s' is missing in procedure call", + LIT(e->token.string), str); + gb_string_free(str); + } + } + err = CallArgumentError_ParameterMissing; + } + } + + if (ordered_operands.count == 0 && param_count_excluding_defaults == 0) { + err = CallArgumentError_None; + + if (variadic) { + GB_ASSERT(pt->params != nullptr && pt->params->Tuple.variables.count > 0); + Type *t = pt->params->Tuple.variables[0]->type; + if (is_type_polymorphic(t)) { + error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input"); + err = CallArgumentError_AmbiguousPolymorphicVariadic; + } + } + } else { + if (pt->is_polymorphic && !pt->is_poly_specialized && err == CallArgumentError_None) { + PolyProcData poly_proc_data = {}; + if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &ordered_operands, call, &poly_proc_data)) { + gen_entity = poly_proc_data.gen_entity; + Type *gept = base_type(gen_entity->type); + GB_ASSERT(is_type_proc(gept)); + proc_type = gept; + pt = &gept->Proc; + } else { + err = CallArgumentError_WrongTypes; + } + } + + for (isize i = 0; i < pt->param_count; i++) { + Entity *e = pt->params->Tuple.variables[i]; + Operand *o = &ordered_operands[i]; + bool param_is_variadic = pt->variadic && pt->variadic_index == i; + + if (o->mode == Addressing_Invalid) { + if (param_is_variadic) { + Type *slice = e->type; + GB_ASSERT(is_type_slice(slice)); + Type *elem = base_type(slice)->Slice.elem; + + if (variadic_operands.count == 0) { + if (is_type_polymorphic(elem)) { + error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input"); + err = CallArgumentError_AmbiguousPolymorphicVariadic; + return err; + } + } else { + if (vari_expand) { + GB_ASSERT(variadic_operands.count == 1); + check_assignment(c, &variadic_operands[0], slice, str_lit("variadic expanded argument")); + } else { + for (Operand &vo : variadic_operands) { + check_assignment(c, &vo, elem, str_lit("variadic argument")); + } + } + } + } + continue; + } + + if (e->kind == Entity_TypeName) { + GB_ASSERT(pt->is_polymorphic); + if (o->mode != Addressing_Type) { + if (show_error) { + error(o->expr, "Expected a type for the argument '%.*s'", LIT(e->token.string)); + } + err = CallArgumentError_WrongTypes; + } + if (are_types_identical(e->type, o->type)) { + score += assign_score_function(1); + } else { + score += assign_score_function(MAXIMUM_TYPE_DISTANCE); + } + } else { + i64 s = 0; + if (!check_is_assignable_to_with_score(c, o, e->type, &s, param_is_variadic)) { + bool ok = false; + if (ok) { + s = assign_score_function(MAXIMUM_TYPE_DISTANCE); + } else { + if (show_error) { + check_assignment(c, o, e->type, str_lit("procedure argument")); + } + err = CallArgumentError_WrongTypes; + } + + if (e->flags & EntityFlag_ConstInput) { + if (o->mode != Addressing_Constant) { + if (show_error) { + error(o->expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string)); + } + err = CallArgumentError_NoneConstantParameter; + } + } + } else if (show_error) { + check_assignment(c, o, e->type, str_lit("procedure argument")); + } + score += s; + } + + if (o->mode == Addressing_Type && is_type_typeid(e->type)) { + add_type_info_type(c, o->type); + add_type_and_value(c, o->expr, Addressing_Value, e->type, exact_value_typeid(o->type)); + } + } + } + + if (data) { + data->score = score; + data->result_type = final_proc_type->Proc.results; + data->gen_entity = gen_entity; + + + Ast *proc_lit = nullptr; + if (ce->proc->tav.value.kind == ExactValue_Procedure) { + Ast *vp = unparen_expr(ce->proc->tav.value.value_procedure); + if (vp && vp->kind == Ast_ProcLit) { + proc_lit = vp; + } + } + if (proc_lit == nullptr) { + add_type_and_value(c, ce->proc, Addressing_Value, final_proc_type, {}); + } + } + + return err; + } + + if (err) { + return err; + } + + auto operands = positional_operands; + TypeTuple *param_tuple = nullptr; + if (pt->params != nullptr) { + param_tuple = &pt->params->Tuple; + } + + if (operands.count == 0 && param_count_excluding_defaults == 0) { err = CallArgumentError_None; if (variadic) { @@ -5641,6 +5844,7 @@ gb_internal bool is_call_expr_field_value(AstCallExpr *ce) { } gb_internal CALL_ARGUMENT_CHECKER(check_named_call_arguments) { + auto operands = positional_operands; ast_node(ce, CallExpr, call); GB_ASSERT(is_type_proc(proc_type)); proc_type = base_type(proc_type); @@ -6705,37 +6909,26 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex return data; } + // if (pt->variadic) { + // isize param_index = pt->variadic_index; - for_array(i, positional_operands) { - Operand &o = positional_operands[i]; - isize param_index = i; + // Entity *e = pt->params->Tuple.variables[param_index]; + // GB_ASSERT(e->kind == Entity_Variable); + // GB_ASSERT(e->flags & EntityFlag_Ellipsis); + // GB_ASSERT(is_type_slice(e->type)); - Entity *e = pt->params->Tuple.variables[param_index]; - GB_ASSERT(e->kind == Entity_Variable); - - check_assignment(c, &o, e->type, str_lit("procedure call expression")); - } - - if (pt->variadic) { - isize param_index = pt->variadic_index; - - Entity *e = pt->params->Tuple.variables[param_index]; - GB_ASSERT(e->kind == Entity_Variable); - GB_ASSERT(e->flags & EntityFlag_Ellipsis); - GB_ASSERT(is_type_slice(e->type)); - - if (vari_expand) { - if (variadic_operands.count != 0) { - GB_ASSERT(variadic_operands.count == 1); - check_assignment(c, &variadic_operands[0], e->type, str_lit("procedure call expression")); - } - } else { - Type *elem = e->type->Slice.elem; - for (Operand &o : variadic_operands) { - check_assignment(c, &o, elem, str_lit("procedure call expression")); - } - } - } + // if (vari_expand) { + // if (variadic_operands.count != 0) { + // GB_ASSERT(variadic_operands.count == 1); + // check_assignment(c, &variadic_operands[0], e->type, str_lit("procedure call expression")); + // } + // } else { + // Type *elem = e->type->Slice.elem; + // for (Operand &o : variadic_operands) { + // check_assignment(c, &o, elem, str_lit("procedure call expression")); + // } + // } + // } array_resize(&positional_operands, pt->param_count); @@ -6770,68 +6963,6 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex array_add(&named_parameters, NamedParameter{name, fv->value}); } - StringMap type_hint_map = {}; // Key: String - string_map_init(&type_hint_map, 2*named_args.count); - defer (string_map_destroy(&type_hint_map)); - - if (pt != nullptr) { - TypeTuple *param_tuple = &pt->params->Tuple; - for (Entity *e : param_tuple->variables) { - if (is_blank_ident(e->token)) { - continue; - } - string_map_set(&type_hint_map, e->token.string, e->type); - } - } else if (operand->mode == Addressing_ProcGroup) { - Array procs = proc_group_entities(c, *operand); - for (Entity *proc : procs) { - Type *proc_type = base_type(proc->type); - if (is_type_proc(proc_type)) { - TypeProc *pt = &proc_type->Proc; - TypeTuple *param_tuple = nullptr; - if (pt->params != nullptr) { - param_tuple = &pt->params->Tuple; - } - if (param_tuple == nullptr) { - continue; - } - for (Entity *e : param_tuple->variables) { - if (is_blank_ident(e->token)) { - continue; - } - StringHashKey key = string_hash_string(e->token.string); - Type **found = string_map_get(&type_hint_map, key); - if (found) { - Type *t = *found; - if (t == nullptr) { - // NOTE(bill): Ambiguous named parameter across all types - continue; - } - if (are_types_identical(t, e->type)) { - // NOTE(bill): No need to set again - } else { - // NOTE(bill): Ambiguous named parameter across all types so set it to a nullptr - string_map_set(&type_hint_map, key, cast(Type *)nullptr); - } - } else { - string_map_set(&type_hint_map, key, e->type); - } - } - } - } - } - - for (NamedParameter ¶m : named_parameters) { - Type *type_hint = nullptr; - if (Type **found = string_map_get(&type_hint_map, param.key)) { - type_hint = *found; - } - - Operand operand = {}; - check_expr_or_type(c, &operand, param.value, type_hint); - array_add(&named_operands, operand); - } - for (NamedParameter const ¶m : named_parameters) { isize param_index = lookup_procedure_parameter(pt, param.key); if (param_index < 0) { @@ -6856,11 +6987,10 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex any_failure = true; } - check_assignment(c, &o, e->type, str_lit("procedure call expression")); - if (!prev_visited) { positional_operands[param_index] = o; } + array_add(&named_operands, o); } } @@ -6891,17 +7021,6 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex } } - for (Operand const &o : positional_operands) { - check_no_copy_assignment(o, str_lit("procedure call expression")); - } - for (Operand const &o : variadic_operands) { - check_no_copy_assignment(o, str_lit("procedure call expression")); - } - for (Operand const &o : named_operands) { - check_no_copy_assignment(o, str_lit("procedure call expression")); - } - - if (any_failure) { return data; } @@ -6917,7 +7036,7 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex Array operands = array_clone(heap_allocator(), positional_operands); - CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, operands, named_operands, CallArgumentMode_ShowErrors, &data); + CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, operands, {}, CallArgumentMode_ShowErrors, &data); gb_unused(err); Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; add_entity_use(c, ident, entity_to_use); @@ -6948,127 +7067,6 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex data.result_type = pt->results; } return data; -#if 0 - - - - /* - auto visited = slice_make(temporary_allocator(), pt->param_count); - auto ordered_values = slice_make(temporary_allocator(), pt->param_count); - - - for_array(i, ce->args) { - Ast *arg = ce->args[i]; - - isize param_index = i; - String param_name = {}; - - if (arg->kind == Ast_FieldValue) { - ast_node(fv, FieldValue, arg); - if (fv->field->kind != Ast_Ident) { - gbString expr_str = expr_to_string(fv->field); - error(arg, "Invalid parameter name '%s' in procedure call", expr_str); - any_failure = true; - gb_string_free(expr_str); - continue; - } - param_name = fv->field->Ident.token.string; - param_index = lookup_procedure_parameter(pt, param_name); - if (param_index < 0) { - error(arg, "No parameter named '%.*s' for this procedure type", LIT(param_name)); - any_failure = true; - continue; - } - arg = fv->value; - } else { - if (param_index >= pt->param_count) { - error(arg, "Too many parameters in procedure call, expected a maximum of %td, got %td", pt->param_count, ce->args.count); - any_failure = true; - break; - } - - param_name = pt->params->Tuple.variables[param_index]->token.string; - } - if (visited[param_index]) { - error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(param_name)); - any_failure = true; - } else { - ParameterValue value = {}; - value.kind = ParameterValue_Value; - value.original_ast_expr = arg; - value.ast_value = arg; - ordered_values[param_index] = value; - } - visited[param_index] = true; - } - - for (isize i = 0; i < pt->param_count; i++) { - if (!visited[i]) { - Entity *e = pt->params->Tuple.variables[i]; - if (e->kind == Entity_Variable && - has_parameter_value(e->Variable.param_value)) { - ordered_values[i] = e->Variable.param_value; - visited[i] = true; - } - } - } - - for (isize i = 0; i < visited.count; i++) { - if (!visited[i]) { - Entity *e = pt->params->Tuple.variables[i]; - gbString t = type_to_string(e->type); - defer (gb_string_free(t)); - if (is_blank_ident(e->token)) { - error(call, "Missing parameter of type '%s' at index %td", t, i); - } else { - error(call, "Missing parameter '%.*s' of type '%s'", LIT(e->token.string), t, i); - } - return data; - } - } - */ - if (any_failure) { - return data; - } - - Array operands = {}; - defer (array_free(&operands)); - - Ast *ident = unparen_expr(operand->expr); - while (ident->kind == Ast_SelectorExpr) { - Ast *s = ident->SelectorExpr.selector; - ident = unparen_expr(s); - } - - Entity *e = entity_of_node(ident); - - CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data); - gb_unused(err); - Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; - add_entity_use(c, ident, entity_to_use); - if (entity_to_use != nullptr) { - update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); - } - if (data.gen_entity != nullptr) { - Entity *e = data.gen_entity; - DeclInfo *decl = data.gen_entity->decl_info; - CheckerContext ctx = *c; - ctx.scope = decl->scope; - ctx.decl = decl; - ctx.proc_name = e->token.string; - ctx.curr_proc_decl = decl; - ctx.curr_proc_sig = e->type; - - GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); - bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); - decl->where_clauses_evaluated = true; - - if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { - check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); - } - } - return data; -#endif } From 9ec927b9e952cb1e82ec543c95547321273a812b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 20 Jun 2023 00:30:29 +0100 Subject: [PATCH 053/144] Try to get make everything work with parapoly --- src/check_expr.cpp | 186 +++++++------------------------------- src/llvm_backend_proc.cpp | 7 +- src/types.cpp | 16 ++-- 3 files changed, 48 insertions(+), 161 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 92b572352..8242d38e9 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6791,11 +6791,6 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex proc_type = base_type(operand->type); - if (proc_type != nullptr && is_type_polymorphic(proc_type)) { - error(call, "Polymorphic procedures not yet supported"); - return data; - } - TypeProc *pt = nullptr; if (proc_type) { pt = &proc_type->Proc; @@ -6806,7 +6801,7 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex bool any_failure = false; - bool vari_expand = (ce->ellipsis.pos.line != 0); + // bool vari_expand = (ce->ellipsis.pos.line != 0); // Split positional and named args into separate arrays/slices Slice positional_args = {}; @@ -6838,14 +6833,11 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex auto positional_operands = array_make(heap_allocator(), 0, positional_args.count); - auto variadic_operands = array_make(heap_allocator(), 0, 0); auto named_operands = array_make(heap_allocator(), 0, 0); defer (array_free(&positional_operands)); - defer (array_free(&variadic_operands)); defer (array_free(&named_operands)); - isize positional_operand_count = 0; Slice visited = {}; @@ -6860,90 +6852,8 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex } check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); } - positional_operand_count = positional_operands.count; - - if (pt != nullptr) { - visited = slice_make(temporary_allocator(), pt->param_count); - - if (!pt->variadic) { - if (pt->param_count < positional_operands.count) { - char const *err_fmt = "Too many arguments for '%s', expected a maximum of %td arguments, got %td"; - gbString c = expr_to_string(ce->proc); - error(call, err_fmt, c, pt->param_count, positional_operands.count); - gb_string_free(c); - return data; - } - positional_operand_count = positional_operands.count; - } else { - // only state it is visited if it was set later on - visited[pt->variadic_index] = positional_operand_count > pt->variadic_index; - - positional_operand_count = gb_min(pt->variadic_index, positional_operand_count); - } - - - for (isize i = 0; i < positional_operand_count; i++) { - visited[i] = true; - } - - for (isize i = positional_operand_count; i < positional_operands.count; i++) { - array_add(&variadic_operands, positional_operands[i]); - } - array_resize(&positional_operands, positional_operand_count); - - if (vari_expand) { - if (variadic_operands.count > 1) { - error(variadic_operands[1].expr, "Unexpected position arguments after variadic expansion with '..'"); - return data; - } - } - - isize param_count = 0; - isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(proc_type, ¶m_count); - - if (named_args.count == 0 && positional_operands.count < param_count_excluding_defaults) { - char const *err_fmt = "Too few arguments for '%s', expected a minimum of %td arguments, got %td"; - gbString c = expr_to_string(ce->proc); - error(call, err_fmt, c, param_count_excluding_defaults, positional_operands.count); - gb_string_free(c); - return data; - } - - // if (pt->variadic) { - // isize param_index = pt->variadic_index; - - // Entity *e = pt->params->Tuple.variables[param_index]; - // GB_ASSERT(e->kind == Entity_Variable); - // GB_ASSERT(e->flags & EntityFlag_Ellipsis); - // GB_ASSERT(is_type_slice(e->type)); - - // if (vari_expand) { - // if (variadic_operands.count != 0) { - // GB_ASSERT(variadic_operands.count == 1); - // check_assignment(c, &variadic_operands[0], e->type, str_lit("procedure call expression")); - // } - // } else { - // Type *elem = e->type->Slice.elem; - // for (Operand &o : variadic_operands) { - // check_assignment(c, &o, elem, str_lit("procedure call expression")); - // } - // } - // } - - array_resize(&positional_operands, pt->param_count); - - } - if (named_args.count > 0) { - struct NamedParameter { - String key; - Ast * value; - }; - - auto named_parameters = array_make(heap_allocator(), 0, named_args.count); - defer (array_free(&named_parameters)); - for_array(i, named_args) { Ast *arg = named_args[i]; if (arg->kind != Ast_FieldValue) { @@ -6958,74 +6868,27 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex gb_string_free(expr_str); continue; } - String name = fv->field->Ident.token.string; + String key = fv->field->Ident.token.string; + Ast *value = fv->value; - array_add(&named_parameters, NamedParameter{name, fv->value}); - } - - for (NamedParameter const ¶m : named_parameters) { - isize param_index = lookup_procedure_parameter(pt, param.key); - if (param_index < 0) { - error(param.value, "No parameter named '%.*s' for this procedure type", LIT(param.key)); - any_failure = true; - continue; + isize param_index = lookup_procedure_parameter(pt, key); + Type *type_hint = nullptr; + if (param_index >= 0) { + Entity *e = pt->params->Tuple.variables[param_index]; + type_hint = e->type; } - bool prev_visited = visited[param_index]; - visited[param_index] = true; - - if (prev_visited) { - error(param.value, "Duplicate parameter named '%.*s' in procedure call", LIT(param.key)); - } - - Entity *e = pt->params->Tuple.variables[param_index]; - GB_ASSERT(e->kind == Entity_Variable); - Operand o = {}; - check_expr_with_type_hint(c, &o, param.value, e->type); + check_expr_with_type_hint(c, &o, value, type_hint); if (o.mode == Addressing_Invalid) { any_failure = true; } - - if (!prev_visited) { - positional_operands[param_index] = o; - } array_add(&named_operands, o); } } - if (pt != nullptr) { - if (pt->variadic) { - visited[pt->variadic_index] = true; - } - - for_array(i, visited) { - if (!visited[i]) { - if (pt->variadic && i == pt->variadic_index) { - visited[i] = true; - continue; - } - - Entity *e = pt->params->Tuple.variables[i]; - if (e->kind == Entity_Variable) { - if (has_parameter_value(e->Variable.param_value)) { - visited[i] = true; - continue; - } - } - gbString t = type_to_string(e->type); - error(call, "Missing parameter of type '%s' at index %td", t, i); - gb_string_free(t); - } - } - } - - if (any_failure) { - return data; - } - - { + if (!any_failure) { Ast *ident = operand->expr; while (ident->kind == Ast_SelectorExpr) { Ast *s = ident->SelectorExpr.selector; @@ -7034,9 +6897,12 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex Entity *e = entity_of_node(ident); - Array operands = array_clone(heap_allocator(), positional_operands); + TokenPos pos = ast_token(call).pos; + if (pos.line == 282 && pos.column == 9 && is_type_polymorphic(proc_type)) { + // GB_PANIC("HERE! %s\n", expr_to_string(call)); + } - CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, operands, {}, CallArgumentMode_ShowErrors, &data); + CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, positional_operands, named_operands, CallArgumentMode_ShowErrors, &data); gb_unused(err); Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; add_entity_use(c, ident, entity_to_use); @@ -7060,12 +6926,17 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); } - } - } - if (pt != nullptr) { + if (is_type_proc(data.gen_entity->type)) { + Type *t = base_type(entity_to_use->type); + // GB_ASSERT_MSG(!is_type_polymorphic(t), "%s", expr_to_string(call)); + data.result_type = t->Proc.results; + } + } + } else if (pt) { data.result_type = pt->results; } + return data; } @@ -7085,8 +6956,15 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op } } - if (operand->mode != Addressing_ProcGroup && is_type_proc(operand->type) && !is_type_polymorphic(operand->type)) { - return check_call_arguments_new_and_improved(c, operand, call); + if (operand->mode != Addressing_ProcGroup) { + bool is_poly = is_type_polymorphic(operand->type); + bool ok = !is_poly; + if (!ok && c->pkg->name == "bug") { + ok = true; + } + if (ok) { + return check_call_arguments_new_and_improved(c, operand, call); + } } ast_node(ce, CallExpr, call); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 046016c75..c6a9438ea 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3165,7 +3165,12 @@ gb_internal lbValue lb_build_call_expr_internal_with_arg_split_args(lbProcedure for_array(i, split_args->positional) { Entity *e = pt->params->Tuple.variables[i]; - GB_ASSERT(e->kind == Entity_Variable); + if (e->kind == Entity_TypeName) { + array_add(&args, lb_const_nil(p->module, e->type)); + continue; + } else if (e->kind != Entity_Variable) { + continue; + } if (pt->variadic && pt->variadic_index == i) { lbValue variadic_args = lb_const_nil(p->module, e->type); diff --git a/src/types.cpp b/src/types.cpp index f3b7f5bab..1223132e3 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2108,8 +2108,12 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) { return is_type_polymorphic(t->Matrix.elem, or_specialized); case Type_Tuple: - for_array(i, t->Tuple.variables) { - if (is_type_polymorphic(t->Tuple.variables[i]->type, or_specialized)) { + for (Entity *e : t->Tuple.variables) { + if (e->kind == Entity_Constant) { + if (e->Constant.value.kind != ExactValue_Invalid) { + return or_specialized; + } + } else if (is_type_polymorphic(e->type, or_specialized)) { return true; } } @@ -4279,6 +4283,10 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha if (var == nullptr) { continue; } + if (comma_index++ > 0) { + str = gb_string_appendc(str, ", "); + } + String name = var->token.string; if (var->kind == Entity_Constant) { str = gb_string_appendc(str, "$"); @@ -4295,10 +4303,6 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha continue; } - if (comma_index++ > 0) { - str = gb_string_appendc(str, ", "); - } - if (var->kind == Entity_Variable) { if (var->flags&EntityFlag_CVarArg) { str = gb_string_appendc(str, "#c_vararg "); From 7c57dde2556b95a0ef206efd93fc23f8fd0bdf89 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 20 Jun 2023 13:47:10 +0100 Subject: [PATCH 054/144] Start work on parapoly args for new and improved --- src/check_expr.cpp | 18 ++++-------------- src/llvm_backend_proc.cpp | 2 +- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8242d38e9..5574827d5 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6898,9 +6898,6 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex Entity *e = entity_of_node(ident); TokenPos pos = ast_token(call).pos; - if (pos.line == 282 && pos.column == 9 && is_type_polymorphic(proc_type)) { - // GB_PANIC("HERE! %s\n", expr_to_string(call)); - } CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, positional_operands, named_operands, CallArgumentMode_ShowErrors, &data); gb_unused(err); @@ -6908,6 +6905,7 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex add_entity_use(c, ident, entity_to_use); if (entity_to_use != nullptr) { update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); + add_type_and_value(c, operand->expr, operand->mode, entity_to_use->type, operand->value); } if (data.gen_entity != nullptr) { Entity *e = data.gen_entity; @@ -6931,6 +6929,7 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex Type *t = base_type(entity_to_use->type); // GB_ASSERT_MSG(!is_type_polymorphic(t), "%s", expr_to_string(call)); data.result_type = t->Proc.results; + } } } else if (pt) { @@ -6954,17 +6953,8 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op if (all_non_poly) { return check_call_arguments_new_and_improved(c, operand, call); } - } - - if (operand->mode != Addressing_ProcGroup) { - bool is_poly = is_type_polymorphic(operand->type); - bool ok = !is_poly; - if (!ok && c->pkg->name == "bug") { - ok = true; - } - if (ok) { - return check_call_arguments_new_and_improved(c, operand, call); - } + } else { + return check_call_arguments_new_and_improved(c, operand, call); } ast_node(ce, CallExpr, call); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index c6a9438ea..b7ee88804 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3250,7 +3250,7 @@ gb_internal lbValue lb_build_call_expr_internal_with_arg_split_args(lbProcedure args[i] = lb_handle_param_value(p, e->type, e->Variable.param_value, pos); break; case Entity_Constant: - GB_PANIC("TODO constant parameter"); + args[i] = lb_handle_param_value(p, e->type, e->Constant.param_value, pos); break; } } else { From 26e06ba6a68bf9812b92d59629aaa426f2659a5f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 20 Jun 2023 16:08:26 +0100 Subject: [PATCH 055/144] Correct `check_call_arguments_new_and_improved` logic --- src/check_expr.cpp | 139 ++++++++++++++++++++++++++++++++++----------- src/check_stmt.cpp | 8 ++- src/check_type.cpp | 133 ++++++++++++++++++++++--------------------- src/types.cpp | 2 - 4 files changed, 181 insertions(+), 101 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 5574827d5..ac3886268 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -34,6 +34,7 @@ gb_global char const *CallArgumentError_strings[CallArgumentError_MAX] = { "ParameterMissing", "DuplicateParameter", "NoneConstantParameter", + "OutOfOrderParameters", }; @@ -95,7 +96,7 @@ gb_internal void check_stmt (CheckerContext *c, Ast *nod gb_internal void check_stmt_list (CheckerContext *c, Slice const &stmts, u32 flags); gb_internal void check_init_constant (CheckerContext *c, Entity *e, Operand *operand); gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Type *type, ExactValue *out_value); -gb_internal bool check_procedure_type (CheckerContext *c, Type *type, Ast *proc_type_node, Array *operands = nullptr); +gb_internal bool check_procedure_type (CheckerContext *c, Type *type, Ast *proc_type_node, Array const *operands = nullptr); gb_internal void check_struct_type (CheckerContext *c, Type *struct_type, Ast *node, Array *poly_operands, Type *named_type = nullptr, Type *original_type_for_poly = nullptr); gb_internal void check_union_type (CheckerContext *c, Type *union_type, Ast *node, Array *poly_operands, @@ -338,7 +339,7 @@ gb_internal void check_scope_decls(CheckerContext *c, Slice const &nodes, } gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_entity, Type *type, - Array *param_operands, Ast *poly_def_node, PolyProcData *poly_proc_data) { + Array const *param_operands, Ast *poly_def_node, PolyProcData *poly_proc_data) { /////////////////////////////////////////////////////////////////////////////// // // // TODO CLEANUP(bill): This procedure is very messy and hacky. Clean this!!! // @@ -605,7 +606,7 @@ gb_internal bool check_polymorphic_procedure_assignment(CheckerContext *c, Opera return find_or_generate_polymorphic_procedure(c, base_entity, type, nullptr, poly_def_node, poly_proc_data); } -gb_internal bool find_or_generate_polymorphic_procedure_from_parameters(CheckerContext *c, Entity *base_entity, Array *operands, Ast *poly_def_node, PolyProcData *poly_proc_data) { +gb_internal bool find_or_generate_polymorphic_procedure_from_parameters(CheckerContext *c, Entity *base_entity, Array const *operands, Ast *poly_def_node, PolyProcData *poly_proc_data) { return find_or_generate_polymorphic_procedure(c, base_entity, nullptr, operands, poly_def_node, poly_proc_data); } @@ -5337,6 +5338,8 @@ gb_internal isize lookup_procedure_parameter(Type *type, String const ¶meter gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { + TEMPORARY_ALLOCATOR_GUARD(); + CallArgumentError err = CallArgumentError_None; ast_node(ce, CallExpr, call); @@ -5371,8 +5374,19 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { } if (ce->split_args) { - auto ordered_operands = array_make(temporary_allocator(), pt->param_count); auto visited = slice_make(temporary_allocator(), pt->param_count); + auto ordered_operands = array_make(temporary_allocator(), pt->param_count); + defer ({ + for (Operand const &o : ordered_operands) { + if (o.expr != nullptr) { + call->viral_state_flags |= o.expr->viral_state_flags; + } + } + }); + + if (check_order_of_call_arguments(c, proc_type, call, show_error)) { + return CallArgumentError_OutOfOrderParameters; + } isize positional_operand_count = positional_operands.count; if (variadic) { @@ -5449,11 +5463,36 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { err = CallArgumentError_DuplicateParameter; } else { visited[pt->variadic_index] = true; + + if (vari_expand) { + GB_ASSERT(variadic_operands.count != 0); + ordered_operands[pt->variadic_index] = variadic_operands[0]; + } else { + AstFile *f = call->file(); + + // HACK(bill): this is an awful hack + Operand o = {}; + o.mode = Addressing_Value; + o.expr = ast_ident(f, make_token_ident("nil")); + o.expr->Ident.token.pos = ast_token(call).pos; + if (variadic_operands.count != 0) { + o.expr->Ident.token.pos = ast_token(variadic_operands[0].expr).pos; + + Entity *vt = pt->params->Tuple.variables[pt->variadic_index]; + if (is_type_polymorphic(vt->type)) { + o.type = alloc_type_slice(default_type(variadic_operands[0].type)); + } else { + o.type = vt->type; + } + } else { + o.type = t_untyped_nil; + } + ordered_operands[pt->variadic_index] = o; + } } } - for (Operand const &o : ordered_operands) { if (o.mode != Addressing_Invalid) { check_no_copy_assignment(o, str_lit("procedure call expression")); @@ -5508,44 +5547,23 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { gen_entity = poly_proc_data.gen_entity; Type *gept = base_type(gen_entity->type); GB_ASSERT(is_type_proc(gept)); - proc_type = gept; + final_proc_type = gen_entity->type; pt = &gept->Proc; + } else { err = CallArgumentError_WrongTypes; } } for (isize i = 0; i < pt->param_count; i++) { - Entity *e = pt->params->Tuple.variables[i]; Operand *o = &ordered_operands[i]; - bool param_is_variadic = pt->variadic && pt->variadic_index == i; - if (o->mode == Addressing_Invalid) { - if (param_is_variadic) { - Type *slice = e->type; - GB_ASSERT(is_type_slice(slice)); - Type *elem = base_type(slice)->Slice.elem; - - if (variadic_operands.count == 0) { - if (is_type_polymorphic(elem)) { - error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input"); - err = CallArgumentError_AmbiguousPolymorphicVariadic; - return err; - } - } else { - if (vari_expand) { - GB_ASSERT(variadic_operands.count == 1); - check_assignment(c, &variadic_operands[0], slice, str_lit("variadic expanded argument")); - } else { - for (Operand &vo : variadic_operands) { - check_assignment(c, &vo, elem, str_lit("variadic argument")); - } - } - } - } continue; } + Entity *e = pt->params->Tuple.variables[i]; + bool param_is_variadic = pt->variadic && pt->variadic_index == i; + if (e->kind == Entity_TypeName) { GB_ASSERT(pt->is_polymorphic); if (o->mode != Addressing_Type) { @@ -5563,6 +5581,11 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { i64 s = 0; if (!check_is_assignable_to_with_score(c, o, e->type, &s, param_is_variadic)) { bool ok = false; + if (e->flags & EntityFlag_AnyInt) { + if (is_type_integer(e->type)) { + ok = check_is_castable_to(c, o, e->type); + } + } if (ok) { s = assign_score_function(MAXIMUM_TYPE_DISTANCE); } else { @@ -5589,6 +5612,57 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { if (o->mode == Addressing_Type && is_type_typeid(e->type)) { add_type_info_type(c, o->type); add_type_and_value(c, o->expr, Addressing_Value, e->type, exact_value_typeid(o->type)); + } else if (show_error && is_type_untyped(o->type)) { + update_untyped_expr_type(c, o->expr, e->type, true); + } + } + } + + if (variadic) { + Type *slice = pt->params->Tuple.variables[pt->variadic_index]->type; + GB_ASSERT(is_type_slice(slice)); + Type *elem = base_type(slice)->Slice.elem; + Type *t = elem; + + if (is_type_polymorphic(t)) { + error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input %s", type_to_string(final_proc_type)); + err = CallArgumentError_AmbiguousPolymorphicVariadic; + } + + for_array(operand_index, variadic_operands) { + Operand &o = variadic_operands[operand_index]; + if (vari_expand) { + t = slice; + if (operand_index > 0) { + if (show_error) { + error(o.expr, "'..' in a variadic procedure can only have one variadic argument at the end"); + } + if (data) { + data->score = score; + data->result_type = final_proc_type->Proc.results; + data->gen_entity = gen_entity; + } + return CallArgumentError_MultipleVariadicExpand; + } + } + i64 s = 0; + if (!check_is_assignable_to_with_score(c, &o, t, &s, true)) { + if (show_error) { + check_assignment(c, &o, t, str_lit("variadic argument")); + } + err = CallArgumentError_WrongTypes; + } else if (show_error) { + check_assignment(c, &o, t, str_lit("variadic argument")); + } + score += s; + if (is_type_any(elem)) { + add_type_info_type(c, o.type); + } + if (o.mode == Addressing_Type && is_type_typeid(t)) { + add_type_info_type(c, o.type); + add_type_and_value(c, o.expr, Addressing_Value, t, exact_value_typeid(o.type)); + } else if (show_error && is_type_untyped(o.type)) { + update_untyped_expr_type(c, o.expr, t, true); } } } @@ -6756,6 +6830,7 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch add_entity_use(c, ident, entity_to_use); if (entity_to_use != nullptr) { update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); + add_type_and_value(c, operand->expr, operand->mode, entity_to_use->type, operand->value); } if (data.gen_entity != nullptr) { @@ -6950,7 +7025,7 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op break; } } - if (all_non_poly) { + if (true) { return check_call_arguments_new_and_improved(c, operand, call); } } else { diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 09af496ab..cf6f998e5 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2207,7 +2207,13 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { } else if (operands.count != result_count) { // Ignore error message as it has most likely already been reported if (all_operands_valid(operands)) { - error(node, "Expected %td return values, got %td", result_count, operands.count); + if (operands.count == 1) { + gbString t = type_to_string(operands[0].type); + error(node, "Expected %td return values, got %td (%s)", result_count, operands.count, t); + gb_string_free(t); + } else { + error(node, "Expected %td return values, got %td", result_count, operands.count); + } } } else { for (isize i = 0; i < result_count; i++) { diff --git a/src/check_type.cpp b/src/check_type.cpp index a69dcdadc..f241f35ee 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1416,7 +1416,7 @@ gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_ } -gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is_variadic_, isize *variadic_index_, bool *success_, isize *specialization_count_, Array *operands) { +gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is_variadic_, isize *variadic_index_, bool *success_, isize *specialization_count_, Array const *operands) { if (_params == nullptr) { return nullptr; } @@ -1664,80 +1664,81 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para ExactValue poly_const = {}; if (operands != nullptr && variables.count < operands->count) { - Operand op = (*operands)[variables.count]; - if (op.expr == nullptr) { - // NOTE(bill): 2019-03-30 - // This is just to add the error message to determine_type_from_polymorphic which - // depends on valid position information - op.expr = _params; - op.mode = Addressing_Invalid; - op.type = t_invalid; - } - if (is_type_polymorphic_type) { - type = determine_type_from_polymorphic(ctx, type, op); - if (type == t_invalid) { - success = false; - } else if (!ctx->no_polymorphic_errors) { - // NOTE(bill): The type should be determined now and thus, no need to determine the type any more - is_type_polymorphic_type = false; - Entity *proc_entity = entity_from_expr(op.expr); - if ((proc_entity != nullptr) && (op.value.kind == ExactValue_Procedure)) { - if (is_type_polymorphic(proc_entity->type, false)) { - error(op.expr, "Cannot determine complete type of partial polymorphic procedure"); + { + if (op.expr == nullptr) { + // NOTE(bill): 2019-03-30 + // This is just to add the error message to determine_type_from_polymorphic which + // depends on valid position information + op.expr = _params; + op.mode = Addressing_Invalid; + op.type = t_invalid; + } + if (is_type_polymorphic_type) { + type = determine_type_from_polymorphic(ctx, type, op); + if (type == t_invalid) { + success = false; + } else if (!ctx->no_polymorphic_errors) { + // NOTE(bill): The type should be determined now and thus, no need to determine the type any more + is_type_polymorphic_type = false; + Entity *proc_entity = entity_from_expr(op.expr); + if ((proc_entity != nullptr) && (op.value.kind == ExactValue_Procedure)) { + if (is_type_polymorphic(proc_entity->type, false)) { + error(op.expr, "Cannot determine complete type of partial polymorphic procedure"); + } } } } - } - if (is_poly_name) { - bool valid = false; - if (is_type_proc(op.type)) { - Ast *expr = unparen_expr(op.expr); - Entity *proc_entity = entity_from_expr(expr); - if (proc_entity) { - poly_const = exact_value_procedure(proc_entity->identifier.load() ? proc_entity->identifier.load() : op.expr); - valid = true; - } else if (expr->kind == Ast_ProcLit) { - poly_const = exact_value_procedure(expr); - valid = true; + if (is_poly_name) { + bool valid = false; + if (is_type_proc(op.type)) { + Ast *expr = unparen_expr(op.expr); + Entity *proc_entity = entity_from_expr(expr); + if (proc_entity) { + poly_const = exact_value_procedure(proc_entity->identifier.load() ? proc_entity->identifier.load() : op.expr); + valid = true; + } else if (expr->kind == Ast_ProcLit) { + poly_const = exact_value_procedure(expr); + valid = true; + } + } + if (!valid) { + if (op.mode == Addressing_Constant) { + poly_const = op.value; + } else { + error(op.expr, "Expected a constant value for this polymorphic name parameter, got %s", expr_to_string(op.expr)); + success = false; + } } } - if (!valid) { - if (op.mode == Addressing_Constant) { - poly_const = op.value; - } else { - error(op.expr, "Expected a constant value for this polymorphic name parameter, got %s", expr_to_string(op.expr)); + if (type != t_invalid && !check_is_assignable_to(ctx, &op, type)) { + bool ok = true; + if (p->flags&FieldFlag_any_int) { + if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) { + ok = false; + } else if (!check_is_castable_to(ctx, &op, type)) { + ok = false; + } + } + if (!ok) { success = false; + #if 0 + gbString got = type_to_string(op.type); + gbString expected = type_to_string(type); + error(op.expr, "Cannot assigned type to parameter, got type '%s', expected '%s'", got, expected); + gb_string_free(expected); + gb_string_free(got); + #endif } } - } - if (type != t_invalid && !check_is_assignable_to(ctx, &op, type)) { - bool ok = true; - if (p->flags&FieldFlag_any_int) { - if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) { - ok = false; - } else if (!check_is_castable_to(ctx, &op, type)) { - ok = false; - } - } - if (!ok) { - success = false; - #if 0 - gbString got = type_to_string(op.type); - gbString expected = type_to_string(type); - error(op.expr, "Cannot assigned type to parameter, got type '%s', expected '%s'", got, expected); - gb_string_free(expected); - gb_string_free(got); - #endif - } - } - if (is_type_untyped(default_type(type))) { - gbString str = type_to_string(type); - error(op.expr, "Cannot determine type from the parameter, got '%s'", str); - gb_string_free(str); - success = false; - type = t_invalid; + if (is_type_untyped(default_type(type))) { + gbString str = type_to_string(type); + error(op.expr, "Cannot determine type from the parameter, got '%s'", str); + gb_string_free(str); + success = false; + type = t_invalid; + } } } @@ -1967,7 +1968,7 @@ gb_internal Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_res // NOTE(bill): 'operands' is for generating non generic procedure type -gb_internal bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, Array *operands) { +gb_internal bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, Array const *operands) { ast_node(pt, ProcType, proc_type_node); if (ctx->polymorphic_scope == nullptr && ctx->allow_polymorphic_types) { diff --git a/src/types.cpp b/src/types.cpp index 1223132e3..385ca926d 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2123,7 +2123,6 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) { if (t->Proc.is_polymorphic) { return true; } - #if 1 if (t->Proc.param_count > 0 && is_type_polymorphic(t->Proc.params, or_specialized)) { return true; @@ -2132,7 +2131,6 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) { is_type_polymorphic(t->Proc.results, or_specialized)) { return true; } - #endif break; case Type_Enum: From 3c5661b51b9da1d3017bf9d0082418627d832884 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 20 Jun 2023 20:29:40 +0100 Subject: [PATCH 056/144] Allow for positional and named arguments in procedure calls --- src/check_expr.cpp | 1440 +++++++------------------------------------- 1 file changed, 234 insertions(+), 1206 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ac3886268..05fd51119 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -67,11 +67,6 @@ gb_internal int valid_index_and_score_cmp(void const *a, void const *b) { -#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(CheckerContext *c, Ast *call, Type *proc_type, Entity *entity, Array positional_operands, Array const &named_operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data) -typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType); - - - gb_internal void check_expr (CheckerContext *c, Operand *operand, Ast *expression); gb_internal void check_multi_expr (CheckerContext *c, Operand *operand, Ast *expression); gb_internal void check_multi_expr_or_type (CheckerContext *c, Operand *operand, Ast *expression); @@ -5336,8 +5331,7 @@ gb_internal isize lookup_procedure_parameter(Type *type, String const ¶meter return lookup_procedure_parameter(&type->Proc, parameter_name); } - -gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { +gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, Ast *call, Type *proc_type, Entity *entity, Array positional_operands, Array const &named_operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data) { TEMPORARY_ALLOCATOR_GUARD(); CallArgumentError err = CallArgumentError_None; @@ -5373,517 +5367,300 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { err = CallArgumentError_NonVariadicExpand; } - if (ce->split_args) { - auto visited = slice_make(temporary_allocator(), pt->param_count); - auto ordered_operands = array_make(temporary_allocator(), pt->param_count); - defer ({ - for (Operand const &o : ordered_operands) { - if (o.expr != nullptr) { - call->viral_state_flags |= o.expr->viral_state_flags; - } - } - }); - - if (check_order_of_call_arguments(c, proc_type, call, show_error)) { - return CallArgumentError_OutOfOrderParameters; - } - - isize positional_operand_count = positional_operands.count; - if (variadic) { - positional_operand_count = gb_min(positional_operands.count, pt->variadic_index); - } else if (positional_operand_count > pt->param_count) { - err = CallArgumentError_TooManyArguments; - char const *err_fmt = "Too many arguments for '%s', expected %td arguments, got %td"; - if (show_error) { - gbString proc_str = expr_to_string(ce->proc); - defer (gb_string_free(proc_str)); - error(call, err_fmt, proc_str, param_count_excluding_defaults, positional_operands.count); - } - return err; - } - positional_operand_count = gb_min(positional_operand_count, pt->param_count); - - for (isize i = 0; i < positional_operand_count; i++) { - ordered_operands[i] = positional_operands[i]; - visited[i] = true; - } - - auto variadic_operands = slice(slice_from_array(positional_operands), positional_operand_count, positional_operands.count); - - if (named_operands.count != 0) { - GB_ASSERT(ce->split_args->named.count == named_operands.count); - for_array(i, ce->split_args->named) { - Ast *arg = ce->split_args->named[i]; - Operand operand = named_operands[i]; - - ast_node(fv, FieldValue, arg); - if (fv->field->kind != Ast_Ident) { - if (show_error) { - gbString expr_str = expr_to_string(fv->field); - error(arg, "Invalid parameter name '%s' in procedure call", expr_str); - gb_string_free(expr_str); - } - err = CallArgumentError_InvalidFieldValue; - continue; - } - String name = fv->field->Ident.token.string; - isize param_index = lookup_procedure_parameter(pt, name); - if (param_index < 0) { - if (show_error) { - error(arg, "No parameter named '%.*s' for this procedure type", LIT(name)); - } - err = CallArgumentError_ParameterNotFound; - continue; - } - if (visited[param_index]) { - if (show_error) { - error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name)); - } - err = CallArgumentError_DuplicateParameter; - continue; - } - Entity *e = pt->params->Tuple.variables[param_index]; - - check_assignment(c, &operand, e->type, str_lit("named argument")); - - visited[param_index] = true; - ordered_operands[param_index] = operand; - - err = CallArgumentError_DuplicateParameter; - } - } - - if (variadic) { - if (visited[pt->variadic_index] && - positional_operand_count < positional_operands.count) { - if (show_error) { - String name = pt->params->Tuple.variables[pt->variadic_index]->token.string; - error(call, "Variadic parameters already handled with a named argument '%.*s' in procedure call", LIT(name)); - } - err = CallArgumentError_DuplicateParameter; - } else { - visited[pt->variadic_index] = true; - - if (vari_expand) { - GB_ASSERT(variadic_operands.count != 0); - ordered_operands[pt->variadic_index] = variadic_operands[0]; - } else { - AstFile *f = call->file(); - - // HACK(bill): this is an awful hack - Operand o = {}; - o.mode = Addressing_Value; - o.expr = ast_ident(f, make_token_ident("nil")); - o.expr->Ident.token.pos = ast_token(call).pos; - if (variadic_operands.count != 0) { - o.expr->Ident.token.pos = ast_token(variadic_operands[0].expr).pos; - - Entity *vt = pt->params->Tuple.variables[pt->variadic_index]; - if (is_type_polymorphic(vt->type)) { - o.type = alloc_type_slice(default_type(variadic_operands[0].type)); - } else { - o.type = vt->type; - } - } else { - o.type = t_untyped_nil; - } - ordered_operands[pt->variadic_index] = o; - } - } - - } - + GB_ASSERT(ce->split_args); + auto visited = slice_make(temporary_allocator(), pt->param_count); + auto ordered_operands = array_make(temporary_allocator(), pt->param_count); + defer ({ for (Operand const &o : ordered_operands) { - if (o.mode != Addressing_Invalid) { - check_no_copy_assignment(o, str_lit("procedure call expression")); + if (o.expr != nullptr) { + call->viral_state_flags |= o.expr->viral_state_flags; } } + }); - for (isize i = 0; i < pt->param_count; i++) { - if (!visited[i]) { - Entity *e = pt->params->Tuple.variables[i]; - if (is_blank_ident(e->token)) { - continue; - } - if (e->kind == Entity_Variable) { - if (e->Variable.param_value.kind != ParameterValue_Invalid) { - score += assign_score_function(1); - continue; - } - } + if (check_order_of_call_arguments(c, proc_type, call, show_error)) { + return CallArgumentError_OutOfOrderParameters; + } + isize positional_operand_count = positional_operands.count; + if (variadic) { + positional_operand_count = gb_min(positional_operands.count, pt->variadic_index); + } else if (positional_operand_count > pt->param_count) { + err = CallArgumentError_TooManyArguments; + char const *err_fmt = "Too many arguments for '%s', expected %td arguments, got %td"; + if (show_error) { + gbString proc_str = expr_to_string(ce->proc); + defer (gb_string_free(proc_str)); + error(call, err_fmt, proc_str, param_count_excluding_defaults, positional_operands.count); + } + return err; + } + positional_operand_count = gb_min(positional_operand_count, pt->param_count); + + for (isize i = 0; i < positional_operand_count; i++) { + ordered_operands[i] = positional_operands[i]; + visited[i] = true; + } + + auto variadic_operands = slice(slice_from_array(positional_operands), positional_operand_count, positional_operands.count); + + if (named_operands.count != 0) { + GB_ASSERT(ce->split_args->named.count == named_operands.count); + for_array(i, ce->split_args->named) { + Ast *arg = ce->split_args->named[i]; + Operand operand = named_operands[i]; + + ast_node(fv, FieldValue, arg); + if (fv->field->kind != Ast_Ident) { if (show_error) { - if (e->kind == Entity_TypeName) { - error(call, "Type parameter '%.*s' is missing in procedure call", - LIT(e->token.string)); - } else if (e->kind == Entity_Constant && e->Constant.value.kind != ExactValue_Invalid) { - // Ignore - } else { - gbString str = type_to_string(e->type); - error(call, "Parameter '%.*s' of type '%s' is missing in procedure call", - LIT(e->token.string), str); - gb_string_free(str); - } + gbString expr_str = expr_to_string(fv->field); + error(arg, "Invalid parameter name '%s' in procedure call", expr_str); + gb_string_free(expr_str); } - err = CallArgumentError_ParameterMissing; + err = CallArgumentError_InvalidFieldValue; + continue; + } + String name = fv->field->Ident.token.string; + isize param_index = lookup_procedure_parameter(pt, name); + if (param_index < 0) { + if (show_error) { + error(arg, "No parameter named '%.*s' for this procedure type", LIT(name)); + } + err = CallArgumentError_ParameterNotFound; + continue; + } + if (visited[param_index]) { + if (show_error) { + error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name)); + } + err = CallArgumentError_DuplicateParameter; + continue; + } + Entity *e = pt->params->Tuple.variables[param_index]; + + check_assignment(c, &operand, e->type, str_lit("named argument")); + + visited[param_index] = true; + ordered_operands[param_index] = operand; + + err = CallArgumentError_DuplicateParameter; + } + } + + if (variadic) { + if (visited[pt->variadic_index] && + positional_operand_count < positional_operands.count) { + if (show_error) { + String name = pt->params->Tuple.variables[pt->variadic_index]->token.string; + error(call, "Variadic parameters already handled with a named argument '%.*s' in procedure call", LIT(name)); + } + err = CallArgumentError_DuplicateParameter; + } else { + visited[pt->variadic_index] = true; + + if (vari_expand) { + GB_ASSERT(variadic_operands.count != 0); + ordered_operands[pt->variadic_index] = variadic_operands[0]; + } else { + AstFile *f = call->file(); + + // HACK(bill): this is an awful hack + Operand o = {}; + o.mode = Addressing_Value; + o.expr = ast_ident(f, make_token_ident("nil")); + o.expr->Ident.token.pos = ast_token(call).pos; + if (variadic_operands.count != 0) { + o.expr->Ident.token.pos = ast_token(variadic_operands[0].expr).pos; + + Entity *vt = pt->params->Tuple.variables[pt->variadic_index]; + if (is_type_polymorphic(vt->type)) { + o.type = alloc_type_slice(default_type(variadic_operands[0].type)); + } else { + o.type = vt->type; + } + } else { + o.type = t_untyped_nil; + } + ordered_operands[pt->variadic_index] = o; } } - if (ordered_operands.count == 0 && param_count_excluding_defaults == 0) { - err = CallArgumentError_None; + } - if (variadic) { - GB_ASSERT(pt->params != nullptr && pt->params->Tuple.variables.count > 0); - Type *t = pt->params->Tuple.variables[0]->type; - if (is_type_polymorphic(t)) { - error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input"); - err = CallArgumentError_AmbiguousPolymorphicVariadic; - } + for (Operand const &o : ordered_operands) { + if (o.mode != Addressing_Invalid) { + check_no_copy_assignment(o, str_lit("procedure call expression")); + } + } + + for (isize i = 0; i < pt->param_count; i++) { + if (!visited[i]) { + Entity *e = pt->params->Tuple.variables[i]; + if (is_blank_ident(e->token)) { + continue; } - } else { - if (pt->is_polymorphic && !pt->is_poly_specialized && err == CallArgumentError_None) { - PolyProcData poly_proc_data = {}; - if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &ordered_operands, call, &poly_proc_data)) { - gen_entity = poly_proc_data.gen_entity; - Type *gept = base_type(gen_entity->type); - GB_ASSERT(is_type_proc(gept)); - final_proc_type = gen_entity->type; - pt = &gept->Proc; - - } else { - err = CallArgumentError_WrongTypes; - } - } - - for (isize i = 0; i < pt->param_count; i++) { - Operand *o = &ordered_operands[i]; - if (o->mode == Addressing_Invalid) { + if (e->kind == Entity_Variable) { + if (e->Variable.param_value.kind != ParameterValue_Invalid) { + ordered_operands[i].mode = Addressing_Value; + ordered_operands[i].type = e->type; + ordered_operands[i].expr = e->Variable.param_value.original_ast_expr; + score += assign_score_function(1); continue; } + } - Entity *e = pt->params->Tuple.variables[i]; - bool param_is_variadic = pt->variadic && pt->variadic_index == i; - + if (show_error) { if (e->kind == Entity_TypeName) { - GB_ASSERT(pt->is_polymorphic); - if (o->mode != Addressing_Type) { - if (show_error) { - error(o->expr, "Expected a type for the argument '%.*s'", LIT(e->token.string)); - } - err = CallArgumentError_WrongTypes; - } - if (are_types_identical(e->type, o->type)) { - score += assign_score_function(1); - } else { - score += assign_score_function(MAXIMUM_TYPE_DISTANCE); - } + error(call, "Type parameter '%.*s' is missing in procedure call", + LIT(e->token.string)); + } else if (e->kind == Entity_Constant && e->Constant.value.kind != ExactValue_Invalid) { + // Ignore } else { - i64 s = 0; - if (!check_is_assignable_to_with_score(c, o, e->type, &s, param_is_variadic)) { - bool ok = false; - if (e->flags & EntityFlag_AnyInt) { - if (is_type_integer(e->type)) { - ok = check_is_castable_to(c, o, e->type); - } - } - if (ok) { - s = assign_score_function(MAXIMUM_TYPE_DISTANCE); - } else { - if (show_error) { - check_assignment(c, o, e->type, str_lit("procedure argument")); - } - err = CallArgumentError_WrongTypes; - } - - if (e->flags & EntityFlag_ConstInput) { - if (o->mode != Addressing_Constant) { - if (show_error) { - error(o->expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string)); - } - err = CallArgumentError_NoneConstantParameter; - } - } - } else if (show_error) { - check_assignment(c, o, e->type, str_lit("procedure argument")); - } - score += s; - } - - if (o->mode == Addressing_Type && is_type_typeid(e->type)) { - add_type_info_type(c, o->type); - add_type_and_value(c, o->expr, Addressing_Value, e->type, exact_value_typeid(o->type)); - } else if (show_error && is_type_untyped(o->type)) { - update_untyped_expr_type(c, o->expr, e->type, true); + gbString str = type_to_string(e->type); + error(call, "Parameter '%.*s' of type '%s' is missing in procedure call", + LIT(e->token.string), str); + gb_string_free(str); } } + err = CallArgumentError_ParameterMissing; } - - if (variadic) { - Type *slice = pt->params->Tuple.variables[pt->variadic_index]->type; - GB_ASSERT(is_type_slice(slice)); - Type *elem = base_type(slice)->Slice.elem; - Type *t = elem; - - if (is_type_polymorphic(t)) { - error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input %s", type_to_string(final_proc_type)); - err = CallArgumentError_AmbiguousPolymorphicVariadic; - } - - for_array(operand_index, variadic_operands) { - Operand &o = variadic_operands[operand_index]; - if (vari_expand) { - t = slice; - if (operand_index > 0) { - if (show_error) { - error(o.expr, "'..' in a variadic procedure can only have one variadic argument at the end"); - } - if (data) { - data->score = score; - data->result_type = final_proc_type->Proc.results; - data->gen_entity = gen_entity; - } - return CallArgumentError_MultipleVariadicExpand; - } - } - i64 s = 0; - if (!check_is_assignable_to_with_score(c, &o, t, &s, true)) { - if (show_error) { - check_assignment(c, &o, t, str_lit("variadic argument")); - } - err = CallArgumentError_WrongTypes; - } else if (show_error) { - check_assignment(c, &o, t, str_lit("variadic argument")); - } - score += s; - if (is_type_any(elem)) { - add_type_info_type(c, o.type); - } - if (o.mode == Addressing_Type && is_type_typeid(t)) { - add_type_info_type(c, o.type); - add_type_and_value(c, o.expr, Addressing_Value, t, exact_value_typeid(o.type)); - } else if (show_error && is_type_untyped(o.type)) { - update_untyped_expr_type(c, o.expr, t, true); - } - } - } - - if (data) { - data->score = score; - data->result_type = final_proc_type->Proc.results; - data->gen_entity = gen_entity; - - - Ast *proc_lit = nullptr; - if (ce->proc->tav.value.kind == ExactValue_Procedure) { - Ast *vp = unparen_expr(ce->proc->tav.value.value_procedure); - if (vp && vp->kind == Ast_ProcLit) { - proc_lit = vp; - } - } - if (proc_lit == nullptr) { - add_type_and_value(c, ce->proc, Addressing_Value, final_proc_type, {}); - } - } - - return err; } - if (err) { - return err; - } - - auto operands = positional_operands; - TypeTuple *param_tuple = nullptr; - if (pt->params != nullptr) { - param_tuple = &pt->params->Tuple; - } - - if (operands.count == 0 && param_count_excluding_defaults == 0) { + if (ordered_operands.count == 0 && param_count_excluding_defaults == 0) { err = CallArgumentError_None; if (variadic) { - GB_ASSERT(param_tuple != nullptr && param_tuple->variables.count > 0); - Type *t = param_tuple->variables[0]->type; + GB_ASSERT(pt->params != nullptr && pt->params->Tuple.variables.count > 0); + Type *t = pt->params->Tuple.variables[0]->type; if (is_type_polymorphic(t)) { error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input"); err = CallArgumentError_AmbiguousPolymorphicVariadic; } } } else { - i32 error_code = 0; - if (operands.count < param_count_excluding_defaults) { - error_code = -1; - } else if (!variadic && operands.count > param_count) { - error_code = +1; + if (pt->is_polymorphic && !pt->is_poly_specialized && err == CallArgumentError_None) { + PolyProcData poly_proc_data = {}; + if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &ordered_operands, call, &poly_proc_data)) { + gen_entity = poly_proc_data.gen_entity; + Type *gept = base_type(gen_entity->type); + GB_ASSERT(is_type_proc(gept)); + final_proc_type = gen_entity->type; + pt = &gept->Proc; + + } else { + err = CallArgumentError_WrongTypes; + } } - if (error_code != 0) { - err = CallArgumentError_TooManyArguments; - char const *err_fmt = "Too many arguments for '%s', expected %td arguments, got %td"; - if (error_code < 0) { - err = CallArgumentError_TooFewArguments; - err_fmt = "Too few arguments for '%s', expected %td arguments, got %td"; + + for (isize i = 0; i < pt->param_count; i++) { + Operand *o = &ordered_operands[i]; + if (o->mode == Addressing_Invalid) { + continue; } - if (show_error) { - gbString proc_str = expr_to_string(ce->proc); - defer (gb_string_free(proc_str)); - error(call, err_fmt, proc_str, param_count_excluding_defaults, operands.count); + Entity *e = pt->params->Tuple.variables[i]; + bool param_is_variadic = pt->variadic && pt->variadic_index == i; - #if 0 - error_line("\t"); - for_array(i, operands) { - if (i > 0) { - error_line(", "); + if (e->kind == Entity_TypeName) { + GB_ASSERT(pt->is_polymorphic); + if (o->mode != Addressing_Type) { + if (show_error) { + error(o->expr, "Expected a type for the argument '%.*s'", LIT(e->token.string)); } - gbString s = expr_to_string(operands[i].expr); - error_line("%s", s); - gb_string_free(s); - } - error_line("\n"); - #endif - } - } else { - // NOTE(bill): Generate the procedure type for this generic instance - if (pt->is_polymorphic && !pt->is_poly_specialized) { - PolyProcData poly_proc_data = {}; - if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &operands, call, &poly_proc_data)) { - gen_entity = poly_proc_data.gen_entity; - GB_ASSERT(is_type_proc(gen_entity->type)); - final_proc_type = gen_entity->type; - } else { err = CallArgumentError_WrongTypes; } - } - - GB_ASSERT(is_type_proc(final_proc_type)); - TypeProc *pt = &final_proc_type->Proc; - - GB_ASSERT(pt->params != nullptr); - auto sig_params = pt->params->Tuple.variables; - isize operand_index = 0; - isize max_operand_count = gb_min(param_count, operands.count); - for (; operand_index < max_operand_count; operand_index++) { - Entity *e = sig_params[operand_index]; - Type *t = e->type; - Operand o = operands[operand_index]; - if (o.expr != nullptr) { - call->viral_state_flags |= o.expr->viral_state_flags; + if (are_types_identical(e->type, o->type)) { + score += assign_score_function(1); + } else { + score += assign_score_function(MAXIMUM_TYPE_DISTANCE); } - - if (e->kind == Entity_TypeName) { - // GB_ASSERT(!variadic); - if (o.mode == Addressing_Invalid) { - continue; - } else if (o.mode != Addressing_Type) { - if (show_error) { - error(o.expr, "Expected a type for the argument '%.*s'", LIT(e->token.string)); - } - err = CallArgumentError_WrongTypes; - } - - if (are_types_identical(e->type, o.type)) { - score += assign_score_function(1); - } else { - score += assign_score_function(MAXIMUM_TYPE_DISTANCE); - } - - continue; - } - - bool param_is_variadic = pt->variadic && pt->variadic_index == operand_index; - + } else { i64 s = 0; - if (!check_is_assignable_to_with_score(c, &o, t, &s, param_is_variadic)) { + if (!check_is_assignable_to_with_score(c, o, e->type, &s, param_is_variadic)) { bool ok = false; if (e->flags & EntityFlag_AnyInt) { - if (is_type_integer(t)) { - ok = check_is_castable_to(c, &o, t); + if (is_type_integer(e->type)) { + ok = check_is_castable_to(c, o, e->type); } } if (ok) { s = assign_score_function(MAXIMUM_TYPE_DISTANCE); } else { if (show_error) { - check_assignment(c, &o, t, str_lit("argument")); + check_assignment(c, o, e->type, str_lit("procedure argument")); } - // TODO(bill, 2021-05-05): Is this incorrect logic to only fail if there is ambiguity for definite? - if (o.mode == Addressing_Invalid) { - err = CallArgumentError_WrongTypes; + err = CallArgumentError_WrongTypes; + } + + if (e->flags & EntityFlag_ConstInput) { + if (o->mode != Addressing_Constant) { + if (show_error) { + error(o->expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string)); + } + err = CallArgumentError_NoneConstantParameter; } } } else if (show_error) { - check_assignment(c, &o, t, str_lit("argument")); + check_assignment(c, o, e->type, str_lit("procedure argument")); } score += s; - - if (e->flags & EntityFlag_ConstInput) { - if (o.mode != Addressing_Constant) { - if (show_error) { - error(o.expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string)); - } - err = CallArgumentError_NoneConstantParameter; - } - } - - if (o.mode == Addressing_Type && is_type_typeid(e->type)) { - add_type_info_type(c, o.type); - add_type_and_value(c, o.expr, Addressing_Value, e->type, exact_value_typeid(o.type)); - } else if (show_error && is_type_untyped(o.type)) { - update_untyped_expr_type(c, o.expr, t, true); - } - } - if (variadic) { - bool variadic_expand = false; - Type *slice = sig_params[param_count]->type; - GB_ASSERT(is_type_slice(slice)); - Type *elem = base_type(slice)->Slice.elem; - Type *t = elem; + if (o->mode == Addressing_Type && is_type_typeid(e->type)) { + add_type_info_type(c, o->type); + add_type_and_value(c, o->expr, Addressing_Value, e->type, exact_value_typeid(o->type)); + } else if (show_error && is_type_untyped(o->type)) { + update_untyped_expr_type(c, o->expr, e->type, true); + } + } + } - if (is_type_polymorphic(t)) { - error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input"); - err = CallArgumentError_AmbiguousPolymorphicVariadic; - } + if (variadic) { + Type *slice = pt->params->Tuple.variables[pt->variadic_index]->type; + GB_ASSERT(is_type_slice(slice)); + Type *elem = base_type(slice)->Slice.elem; + Type *t = elem; - for (; operand_index < operands.count; operand_index++) { - Operand o = operands[operand_index]; - if (vari_expand) { - variadic_expand = true; - t = slice; - if (operand_index != param_count) { - if (show_error) { - error(o.expr, "'..' in a variadic procedure can only have one variadic argument at the end"); - } - if (data) { - data->score = score; - data->result_type = final_proc_type->Proc.results; - data->gen_entity = gen_entity; - } - return CallArgumentError_MultipleVariadicExpand; - } + if (is_type_polymorphic(t)) { + error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input %s", type_to_string(final_proc_type)); + err = CallArgumentError_AmbiguousPolymorphicVariadic; + } + + for_array(operand_index, variadic_operands) { + Operand &o = variadic_operands[operand_index]; + if (vari_expand) { + t = slice; + if (operand_index > 0) { + if (show_error) { + error(o.expr, "'..' in a variadic procedure can only have one variadic argument at the end"); } - i64 s = 0; - if (!check_is_assignable_to_with_score(c, &o, t, &s, true)) { - if (show_error) { - check_assignment(c, &o, t, str_lit("argument")); - } - err = CallArgumentError_WrongTypes; - } else if (show_error) { - check_assignment(c, &o, t, str_lit("argument")); - } - score += s; - if (is_type_any(elem)) { - add_type_info_type(c, o.type); - } - if (o.mode == Addressing_Type && is_type_typeid(t)) { - add_type_info_type(c, o.type); - add_type_and_value(c, o.expr, Addressing_Value, t, exact_value_typeid(o.type)); - } else if (show_error && is_type_untyped(o.type)) { - update_untyped_expr_type(c, o.expr, t, true); + if (data) { + data->score = score; + data->result_type = final_proc_type->Proc.results; + data->gen_entity = gen_entity; } + return CallArgumentError_MultipleVariadicExpand; } } + i64 s = 0; + if (!check_is_assignable_to_with_score(c, &o, t, &s, true)) { + if (show_error) { + check_assignment(c, &o, t, str_lit("variadic argument")); + } + err = CallArgumentError_WrongTypes; + } else if (show_error) { + check_assignment(c, &o, t, str_lit("variadic argument")); + } + score += s; + if (is_type_any(elem)) { + add_type_info_type(c, o.type); + } + if (o.mode == Addressing_Type && is_type_typeid(t)) { + add_type_info_type(c, o.type); + add_type_and_value(c, o.expr, Addressing_Value, t, exact_value_typeid(o.type)); + } else if (show_error && is_type_untyped(o.type)) { + update_untyped_expr_type(c, o.expr, t, true); + } } } @@ -5917,193 +5694,6 @@ gb_internal bool is_call_expr_field_value(AstCallExpr *ce) { return ce->args[0]->kind == Ast_FieldValue; } -gb_internal CALL_ARGUMENT_CHECKER(check_named_call_arguments) { - auto operands = positional_operands; - ast_node(ce, CallExpr, call); - GB_ASSERT(is_type_proc(proc_type)); - proc_type = base_type(proc_type); - TypeProc *pt = &proc_type->Proc; - - i64 score = 0; - bool show_error = show_error_mode == CallArgumentMode_ShowErrors; - CallArgumentError err = CallArgumentError_None; - - TEMPORARY_ALLOCATOR_GUARD(); - - isize param_count = pt->param_count; - bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count); - auto ordered_operands = array_make(temporary_allocator(), param_count); - defer ({ - for (Operand const &o : ordered_operands) { - if (o.expr != nullptr) { - call->viral_state_flags |= o.expr->viral_state_flags; - } - } - }); - - if (check_order_of_call_arguments(c, proc_type, call, show_error)) { - return CallArgumentError_OutOfOrderParameters; - } - - for_array(i, ce->args) { - Ast *arg = ce->args[i]; - ast_node(fv, FieldValue, arg); - if (fv->field->kind != Ast_Ident) { - if (show_error) { - gbString expr_str = expr_to_string(fv->field); - error(arg, "Invalid parameter name '%s' in procedure call", expr_str); - gb_string_free(expr_str); - } - err = CallArgumentError_InvalidFieldValue; - continue; - } - String name = fv->field->Ident.token.string; - isize index = lookup_procedure_parameter(pt, name); - if (index < 0) { - if (show_error) { - error(arg, "No parameter named '%.*s' for this procedure type", LIT(name)); - } - err = CallArgumentError_ParameterNotFound; - continue; - } - if (visited[index]) { - if (show_error) { - error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name)); - } - err = CallArgumentError_DuplicateParameter; - continue; - } - - visited[index] = true; - ordered_operands[index] = operands[i]; - } - - // NOTE(bill): Check for default values and missing parameters - isize param_count_to_check = param_count; - if (pt->variadic) { - param_count_to_check--; - } - for (isize i = 0; i < param_count_to_check; i++) { - if (!visited[i]) { - Entity *e = pt->params->Tuple.variables[i]; - if (is_blank_ident(e->token)) { - continue; - } - if (e->kind == Entity_Variable) { - if (e->Variable.param_value.kind != ParameterValue_Invalid) { - score += assign_score_function(1); - continue; - } - } - - if (show_error) { - if (e->kind == Entity_TypeName) { - error(call, "Type parameter '%.*s' is missing in procedure call", - LIT(e->token.string)); - } else if (e->kind == Entity_Constant && e->Constant.value.kind != ExactValue_Invalid) { - // Ignore - } else { - gbString str = type_to_string(e->type); - error(call, "Parameter '%.*s' of type '%s' is missing in procedure call", - LIT(e->token.string), str); - gb_string_free(str); - } - } - err = CallArgumentError_ParameterMissing; - } - } - - Entity *gen_entity = nullptr; - if (pt->is_polymorphic && !pt->is_poly_specialized && err == CallArgumentError_None) { - PolyProcData poly_proc_data = {}; - if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &ordered_operands, call, &poly_proc_data)) { - gen_entity = poly_proc_data.gen_entity; - Type *gept = base_type(gen_entity->type); - GB_ASSERT(is_type_proc(gept)); - proc_type = gept; - pt = &gept->Proc; - } else { - err = CallArgumentError_WrongTypes; - } - } - - - for (isize i = 0; i < param_count; i++) { - Entity *e = pt->params->Tuple.variables[i]; - Operand *o = &ordered_operands[i]; - bool param_is_variadic = pt->variadic && pt->variadic_index == i; - - - if (o->mode == Addressing_Invalid) { - if (param_is_variadic) { - Type *slice = e->type; - GB_ASSERT(is_type_slice(slice)); - Type *elem = base_type(slice)->Slice.elem; - if (is_type_polymorphic(elem)) { - error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input"); - err = CallArgumentError_AmbiguousPolymorphicVariadic; - return err; - } - } - continue; - } - - if (e->kind == Entity_TypeName) { - GB_ASSERT(pt->is_polymorphic); - if (o->mode != Addressing_Type) { - if (show_error) { - error(o->expr, "Expected a type for the argument '%.*s'", LIT(e->token.string)); - } - err = CallArgumentError_WrongTypes; - } - if (are_types_identical(e->type, o->type)) { - score += assign_score_function(1); - } else { - score += assign_score_function(MAXIMUM_TYPE_DISTANCE); - } - } else { - i64 s = 0; - if (!check_is_assignable_to_with_score(c, o, e->type, &s, param_is_variadic)) { - bool ok = false; - if (ok) { - s = assign_score_function(MAXIMUM_TYPE_DISTANCE); - } else { - if (show_error) { - check_assignment(c, o, e->type, str_lit("procedure argument")); - } - err = CallArgumentError_WrongTypes; - } - - if (e->flags & EntityFlag_ConstInput) { - if (o->mode != Addressing_Constant) { - if (show_error) { - error(o->expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string)); - } - err = CallArgumentError_NoneConstantParameter; - } - } - } else if (show_error) { - check_assignment(c, o, e->type, str_lit("procedure argument")); - } - score += s; - } - - if (o->mode == Addressing_Type && is_type_typeid(e->type)) { - add_type_info_type(c, o->type); - add_type_and_value(c, o->expr, Addressing_Value, e->type, exact_value_typeid(o->type)); - } - } - - if (data) { - data->score = score; - data->result_type = pt->results; - data->gen_entity = gen_entity; - add_type_and_value(c, ce->proc, Addressing_Value, proc_type, {}); - } - - return err; -} - gb_internal Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize *lhs_count_, bool *is_variadic) { Entity **lhs = nullptr; isize lhs_count = -1; @@ -7014,568 +6604,6 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex return data; } - -gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Ast *call) { - if (operand->mode == Addressing_ProcGroup) { - Array procs = proc_group_entities(c, *operand); - bool all_non_poly = true; - for (Entity *e : procs) { - if (is_type_polymorphic(e->type)) { - all_non_poly = false; - break; - } - } - if (true) { - return check_call_arguments_new_and_improved(c, operand, call); - } - } else { - return check_call_arguments_new_and_improved(c, operand, call); - } - - ast_node(ce, CallExpr, call); - - CallArgumentCheckerType *call_checker = check_call_arguments_internal; - Array operands = {}; - defer (array_free(&operands)); - - Type *result_type = t_invalid; - Type *proc_type = base_type(operand->type); - - Slice const &args = ce->args; - if (is_call_expr_field_value(ce)) { - Type *ptype = nullptr; - bool single_case = true; - - if (operand->mode == Addressing_ProcGroup) { - single_case = false; - Array procs = proc_group_entities(c, *operand); - if (procs.count == 1) { - ptype = procs[0]->type; - single_case = true; - } - } else { - ptype = proc_type; - } - - if (check_order_of_call_arguments(c, ptype, call, true)) { - CallArgumentData data = {}; - data.result_type = t_invalid; - if (ptype && is_type_proc(ptype) && !is_type_polymorphic(ptype)) { - data.result_type = reduce_tuple_to_single_type(ptype->Proc.results); - } - return data; - } - - call_checker = check_named_call_arguments; - - operands = array_make(heap_allocator(), args.count); - - // NOTE(bill): This is give type hints for the named parameters - // in order to improve the type inference system - - StringMap type_hint_map = {}; // Key: String - string_map_init(&type_hint_map, 2*args.count); - defer (string_map_destroy(&type_hint_map)); - - - if (single_case) { - Type *bptype = base_type(ptype); - if (is_type_proc(bptype)) { - TypeProc *pt = &bptype->Proc; - TypeTuple *param_tuple = nullptr; - if (pt->params != nullptr) { - param_tuple = &pt->params->Tuple; - } - if (param_tuple != nullptr) { - for (Entity *e : param_tuple->variables) { - if (is_blank_ident(e->token)) { - continue; - } - string_map_set(&type_hint_map, e->token.string, e->type); - } - } - } - } else { - Array procs = proc_group_entities(c, *operand); - for (Entity *proc : procs) { - Type *proc_type = base_type(proc->type); - if (is_type_proc(proc_type)) { - TypeProc *pt = &proc_type->Proc; - TypeTuple *param_tuple = nullptr; - if (pt->params != nullptr) { - param_tuple = &pt->params->Tuple; - } - if (param_tuple == nullptr) { - continue; - } - for (Entity *e : param_tuple->variables) { - if (is_blank_ident(e->token)) { - continue; - } - StringHashKey key = string_hash_string(e->token.string); - Type **found = string_map_get(&type_hint_map, key); - if (found) { - Type *t = *found; - if (t == nullptr) { - // NOTE(bill): Ambiguous named parameter across all types - continue; - } - if (are_types_identical(t, e->type)) { - // NOTE(bill): No need to set again - } else { - // NOTE(bill): Ambiguous named parameter across all types so set it to a nullptr - string_map_set(&type_hint_map, key, cast(Type *)nullptr); - } - } else { - string_map_set(&type_hint_map, key, e->type); - } - } - } - } - - } - - - for_array(i, args) { - Ast *arg = args[i]; - ast_node(fv, FieldValue, arg); - Ast *field = fv->field; - - Type *type_hint = nullptr; - - if (field != nullptr && field->kind == Ast_Ident) { - String key = field->Ident.token.string; - Type **found = string_map_get(&type_hint_map, key); - if (found) { - type_hint = *found; - } - } - check_expr_or_type(c, &operands[i], fv->value, type_hint); - } - } else { - operands = array_make(heap_allocator(), 0, 2*args.count); - Entity **lhs = nullptr; - isize lhs_count = -1; - bool is_variadic = false; - if (proc_type != nullptr && is_type_proc(proc_type)) { - lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic); - } - if (operand->mode != Addressing_ProcGroup) { - check_unpack_arguments(c, lhs, lhs_count, &operands, args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); - } - } - - for (Operand const &o : operands) { - check_no_copy_assignment(o, str_lit("procedure call expression")); - } - - if (operand->mode == Addressing_ProcGroup) { - check_entity_decl(c, operand->proc_group, nullptr, nullptr); - - auto procs = proc_group_entities_cloned(c, *operand); - - if (procs.count > 1) { - isize max_arg_count = args.count; - for (Ast *arg : args) { - // NOTE(bill): The only thing that may have multiple values - // will be a call expression (assuming `or_return` and `()` will be stripped) - arg = strip_or_return_expr(arg); - if (arg && arg->kind == Ast_CallExpr) { - max_arg_count = ISIZE_MAX; - break; - } - } - - for (isize proc_index = 0; proc_index < procs.count; /**/) { - Entity *proc = procs[proc_index]; - Type *pt = base_type(proc->type); - if (!(pt != nullptr && is_type_proc(pt))) { - proc_index++; - continue; - } - - isize param_count = 0; - isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, ¶m_count); - - if (param_count_excluding_defaults > max_arg_count) { - array_unordered_remove(&procs, proc_index); - } else { - proc_index++; - } - } - } - - if (procs.count == 1) { - Ast *ident = operand->expr; - while (ident->kind == Ast_SelectorExpr) { - Ast *s = ident->SelectorExpr.selector; - ident = s; - } - - Entity *e = procs[0]; - - Entity **lhs = nullptr; - isize lhs_count = -1; - bool is_variadic = false; - lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic); - check_unpack_arguments(c, lhs, lhs_count, &operands, args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); - - Array named_operands = {}; - CallArgumentData data = {}; - CallArgumentError err = call_checker(c, call, e->type, e, operands, named_operands, CallArgumentMode_ShowErrors, &data); - if (err != CallArgumentError_None) { - // handle error - } - Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; - add_entity_use(c, ident, entity_to_use); - if (entity_to_use != nullptr) { - update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); - } - return data; - } - - - Entity **lhs = nullptr; - isize lhs_count = -1; - - { - // NOTE(bill, 2019-07-13): This code is used to improve the type inference for procedure groups - // where the same positional parameter has the same type value (and ellipsis) - bool proc_arg_count_all_equal = true; - isize proc_arg_count = -1; - for (Entity *p : procs) { - Type *pt = base_type(p->type); - if (pt != nullptr && is_type_proc(pt)) { - if (proc_arg_count < 0) { - proc_arg_count = pt->Proc.param_count; - } else { - if (proc_arg_count != pt->Proc.param_count) { - proc_arg_count_all_equal = false; - break; - } - } - } - } - - - - if (proc_arg_count >= 0 && proc_arg_count_all_equal) { - lhs_count = proc_arg_count; - if (lhs_count > 0) { - lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count); - for (isize param_index = 0; param_index < lhs_count; param_index++) { - Entity *e = nullptr; - for (Entity *p : procs) { - Type *pt = base_type(p->type); - if (pt != nullptr && is_type_proc(pt)) { - if (e == nullptr) { - e = pt->Proc.params->Tuple.variables[param_index]; - } else { - Entity *f = pt->Proc.params->Tuple.variables[param_index]; - if (e == f) { - continue; - } - if (are_types_identical(e->type, f->type)) { - bool ee = (e->flags & EntityFlag_Ellipsis) != 0; - bool fe = (f->flags & EntityFlag_Ellipsis) != 0; - if (ee == fe) { - continue; - } - } - // NOTE(bill): Entities are not close enough to be used - e = nullptr; - break; - } - } - } - lhs[param_index] = e; - } - } - } - } - - - check_unpack_arguments(c, lhs, lhs_count, &operands, args, UnpackFlag_None); - - if (lhs != nullptr) { - gb_free(heap_allocator(), lhs); - } - - auto valids = array_make(heap_allocator(), 0, procs.count); - defer (array_free(&valids)); - - auto proc_entities = array_make(heap_allocator(), 0, procs.count*2 + 1); - defer (array_free(&proc_entities)); - for (Entity *proc : procs) { - array_add(&proc_entities, proc); - } - - - gbString expr_name = expr_to_string(operand->expr); - defer (gb_string_free(expr_name)); - - for_array(i, procs) { - Entity *p = procs[i]; - Type *pt = base_type(p->type); - if (pt != nullptr && is_type_proc(pt)) { - CallArgumentError err = CallArgumentError_None; - CallArgumentData data = {}; - CheckerContext ctx = *c; - - ctx.no_polymorphic_errors = true; - ctx.allow_polymorphic_types = is_type_polymorphic(pt); - ctx.hide_polymorphic_errors = true; - - Array named_operands = {}; - err = call_checker(&ctx, call, pt, p, operands, named_operands, CallArgumentMode_NoErrors, &data); - if (err != CallArgumentError_None) { - continue; - } - isize index = i; - - if (data.gen_entity != nullptr) { - Entity *e = data.gen_entity; - DeclInfo *decl = data.gen_entity->decl_info; - ctx.scope = decl->scope; - ctx.decl = decl; - ctx.proc_name = e->token.string; - ctx.curr_proc_decl = decl; - ctx.curr_proc_sig = e->type; - - GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); - if (!evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) { - continue; - } - - array_add(&proc_entities, data.gen_entity); - index = proc_entities.count-1; - } - - ValidIndexAndScore item = {}; - item.index = index; - item.score = data.score; - array_add(&valids, item); - } - } - - if (valids.count > 1) { - gb_sort_array(valids.data, valids.count, valid_index_and_score_cmp); - i64 best_score = valids[0].score; - Entity *best_entity = proc_entities[valids[0].index]; - GB_ASSERT(best_entity != nullptr); - for (isize i = 1; i < valids.count; i++) { - if (best_score > valids[i].score) { - valids.count = i; - break; - } - if (best_entity == proc_entities[valids[i].index]) { - valids.count = i; - break; - } - } - } - - - if (valids.count == 0) { - begin_error_block(); - defer (end_error_block()); - - error(operand->expr, "No procedures or ambiguous call for procedure group '%s' that match with the given arguments", expr_name); - if (operands.count == 0) { - error_line("\tNo given arguments\n"); - } else { - error_line("\tGiven argument types: ("); - for_array(i, operands) { - Operand o = operands[i]; - if (i > 0) error_line(", "); - gbString type = type_to_string(o.type); - defer (gb_string_free(type)); - error_line("%s", type); - } - error_line(")\n"); - } - - if (procs.count > 0) { - error_line("Did you mean to use one of the following:\n"); - } - for (Entity *proc : procs) { - TokenPos pos = proc->token.pos; - Type *t = base_type(proc->type); - if (t == t_invalid) continue; - GB_ASSERT(t->kind == Type_Proc); - gbString pt; - defer (gb_string_free(pt)); - if (t->Proc.node != nullptr) { - pt = expr_to_string(t->Proc.node); - } else { - pt = type_to_string(t); - } - String prefix = {}; - String prefix_sep = {}; - if (proc->pkg) { - prefix = proc->pkg->name; - prefix_sep = str_lit("."); - } - String name = proc->token.string; - - char const *sep = "::"; - if (proc->kind == Entity_Variable) { - sep = ":="; - } - error_line("\t%.*s%.*s%.*s %s %s at %s\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, token_pos_to_string(pos)); - } - if (procs.count > 0) { - error_line("\n"); - } - - result_type = t_invalid; - } else if (valids.count > 1) { - begin_error_block(); - defer (end_error_block()); - - error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name); - error_line("\tGiven argument types: ("); - for_array(i, operands) { - Operand o = operands[i]; - if (i > 0) error_line(", "); - gbString type = type_to_string(o.type); - defer (gb_string_free(type)); - error_line("%s", type); - } - error_line(")\n"); - - for (isize i = 0; i < valids.count; i++) { - Entity *proc = proc_entities[valids[i].index]; - GB_ASSERT(proc != nullptr); - TokenPos pos = proc->token.pos; - Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc); - gbString pt = nullptr; - defer (gb_string_free(pt)); - if (t->Proc.node != nullptr) { - pt = expr_to_string(t->Proc.node); - } else { - pt = type_to_string(t); - } - String name = proc->token.string; - char const *sep = "::"; - if (proc->kind == Entity_Variable) { - sep = ":="; - } - error_line("\t%.*s %s %s ", LIT(name), sep, pt); - if (proc->decl_info->proc_lit != nullptr) { - GB_ASSERT(proc->decl_info->proc_lit->kind == Ast_ProcLit); - auto *pl = &proc->decl_info->proc_lit->ProcLit; - if (pl->where_token.kind != Token_Invalid) { - error_line("\n\t\twhere "); - for_array(j, pl->where_clauses) { - Ast *clause = pl->where_clauses[j]; - if (j != 0) { - error_line("\t\t "); - } - gbString str = expr_to_string(clause); - error_line("%s", str); - gb_string_free(str); - - if (j != pl->where_clauses.count-1) { - error_line(","); - } - } - error_line("\n\t"); - } - } - error_line("at %s\n", token_pos_to_string(pos)); - } - result_type = t_invalid; - } else { - GB_ASSERT(valids.count == 1); - Ast *ident = operand->expr; - while (ident->kind == Ast_SelectorExpr) { - Ast *s = ident->SelectorExpr.selector; - ident = s; - } - - Entity *e = proc_entities[valids[0].index]; - GB_ASSERT(e != nullptr); - - Array named_operands = {}; - - proc_type = e->type; - CallArgumentData data = {}; - CallArgumentError err = call_checker(c, call, proc_type, e, operands, named_operands, CallArgumentMode_ShowErrors, &data); - gb_unused(err); - Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; - add_entity_use(c, ident, entity_to_use); - if (entity_to_use != nullptr) { - update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); - } - - if (data.gen_entity != nullptr) { - Entity *e = data.gen_entity; - DeclInfo *decl = data.gen_entity->decl_info; - CheckerContext ctx = *c; - ctx.scope = decl->scope; - ctx.decl = decl; - ctx.proc_name = e->token.string; - ctx.curr_proc_decl = decl; - ctx.curr_proc_sig = e->type; - - GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); - bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); - decl->where_clauses_evaluated = true; - - if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { - check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); - } - } - return data; - } - } else { - Ast *ident = operand->expr; - while (ident->kind == Ast_SelectorExpr) { - Ast *s = ident->SelectorExpr.selector; - ident = s; - } - - Entity *e = entity_of_node(ident); - - Array named_operands = {}; - - CallArgumentData data = {}; - CallArgumentError err = call_checker(c, call, proc_type, e, operands, named_operands, CallArgumentMode_ShowErrors, &data); - gb_unused(err); - Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; - add_entity_use(c, ident, entity_to_use); - if (entity_to_use != nullptr) { - update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); - } - if (data.gen_entity != nullptr) { - Entity *e = data.gen_entity; - DeclInfo *decl = data.gen_entity->decl_info; - CheckerContext ctx = *c; - ctx.scope = decl->scope; - ctx.decl = decl; - ctx.proc_name = e->token.string; - ctx.curr_proc_decl = decl; - ctx.curr_proc_sig = e->type; - - GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); - bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); - decl->where_clauses_evaluated = true; - - if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { - check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); - } - } - return data; - } - - - CallArgumentData data = {}; - data.result_type = t_invalid; - return data; -} - - gb_internal isize lookup_polymorphic_record_parameter(Type *t, String parameter_name) { if (!is_type_polymorphic_record(t)) { return -1; @@ -8136,7 +7164,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } - CallArgumentData data = check_call_arguments(c, operand, call); + CallArgumentData data = check_call_arguments_new_and_improved(c, operand, call); Type *result_type = data.result_type; gb_zero_item(operand); operand->expr = call; From fb756e346380288ba2a1e14150d9f99619c4c6e8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 20 Jun 2023 21:02:57 +0100 Subject: [PATCH 057/144] Correct procedure group handling --- src/check_expr.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 05fd51119..48fdfe871 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5447,6 +5447,9 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } } + isize dummy_argument_count = 0; + bool actually_variadic = false; + if (variadic) { if (visited[pt->variadic_index] && positional_operand_count < positional_operands.count) { @@ -5461,6 +5464,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A if (vari_expand) { GB_ASSERT(variadic_operands.count != 0); ordered_operands[pt->variadic_index] = variadic_operands[0]; + actually_variadic = true; } else { AstFile *f = call->file(); @@ -5470,6 +5474,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A o.expr = ast_ident(f, make_token_ident("nil")); o.expr->Ident.token.pos = ast_token(call).pos; if (variadic_operands.count != 0) { + actually_variadic = true; o.expr->Ident.token.pos = ast_token(variadic_operands[0].expr).pos; Entity *vt = pt->params->Tuple.variables[pt->variadic_index]; @@ -5479,6 +5484,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A o.type = vt->type; } } else { + dummy_argument_count += 1; o.type = t_untyped_nil; } ordered_operands[pt->variadic_index] = o; @@ -5504,6 +5510,8 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A ordered_operands[i].mode = Addressing_Value; ordered_operands[i].type = e->type; ordered_operands[i].expr = e->Variable.param_value.original_ast_expr; + + dummy_argument_count += 1; score += assign_score_function(1); continue; } @@ -5603,7 +5611,9 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } else if (show_error) { check_assignment(c, o, e->type, str_lit("procedure argument")); } - score += s; + if (!param_is_variadic) { + score += s; + } } if (o->mode == Addressing_Type && is_type_typeid(e->type)) { @@ -6070,9 +6080,10 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch CallArgumentError err = check_call_arguments_internal(c, call, e->type, e, positional_operands, named_operands, CallArgumentMode_ShowErrors, &data); if (err != CallArgumentError_None) { - // handle error + } + Type *proc_type = base_type(operand->type); Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; @@ -6086,8 +6097,6 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch if (proc_type && proc_type->kind == Type_Proc) { data.result_type = proc_type->Proc.results; add_type_and_value(c, operand->expr, operand->mode, proc_type, operand->value); - } else if (err == CallArgumentError_None) { - data.result_type = nullptr; } if (data.gen_entity != nullptr) { @@ -6108,6 +6117,7 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); } } + return data; } @@ -6419,6 +6429,7 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; add_entity_use(c, ident, entity_to_use); if (entity_to_use != nullptr) { + proc_type = base_type(entity_to_use->type); update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); add_type_and_value(c, operand->expr, operand->mode, entity_to_use->type, operand->value); } @@ -6441,6 +6452,11 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); } } + + if (is_type_proc(proc_type)) { + data.result_type = proc_type->Proc.results; + } + return data; } From 2a002c3882ab0c82d5d9553da7926532e832ef4e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 20 Jun 2023 22:55:33 +0100 Subject: [PATCH 058/144] Fix scoring for untyped ternary expressions --- src/check_expr.cpp | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 48fdfe871..2e5b23e78 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -700,23 +700,34 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand } return -1; } - if (src->kind == Type_Basic && src->Basic.kind == Basic_UntypedRune) { - if (is_type_integer(dst) || is_type_rune(dst)) { - if (is_type_typed(type)) { - return 2; + if (src->kind == Type_Basic) { + switch (src->Basic.kind) { + case Basic_UntypedRune: + if (is_type_integer(dst) || is_type_rune(dst)) { + if (is_type_typed(type)) { + return 2; + } + return 1; } - return 1; - } - return -1; - } - if (src->kind == Type_Basic && src->Basic.kind == Basic_UntypedBool) { - if (is_type_boolean(dst)) { - if (is_type_typed(type)) { - return 2; + return -1; + case Basic_UntypedBool: + if (is_type_boolean(dst)) { + if (is_type_typed(type)) { + return 2; + } + return 1; } - return 1; + return -1; + case Basic_UntypedString: + if (is_type_string(dst) || is_type_cstring(dst)) { + if (is_type_typed(type)) { + return 2; + } + return 1; + } + return -1; } - return -1; + } } } @@ -5461,9 +5472,12 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } else { visited[pt->variadic_index] = true; + Operand *variadic_operand = &ordered_operands[pt->variadic_index]; + if (vari_expand) { GB_ASSERT(variadic_operands.count != 0); - ordered_operands[pt->variadic_index] = variadic_operands[0]; + *variadic_operand = variadic_operands[0]; + variadic_operand->type = default_type(variadic_operand->type); actually_variadic = true; } else { AstFile *f = call->file(); @@ -5487,7 +5501,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A dummy_argument_count += 1; o.type = t_untyped_nil; } - ordered_operands[pt->variadic_index] = o; + *variadic_operand = o; } } From 3998d0c81e2d6aa408b6fd143af1b569d8e69d71 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 20 Jun 2023 22:55:47 +0100 Subject: [PATCH 059/144] Make error checks diverging where possible --- core/runtime/error_checks.odin | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/runtime/error_checks.odin b/core/runtime/error_checks.odin index a667b9065..c189642af 100644 --- a/core/runtime/error_checks.odin +++ b/core/runtime/error_checks.odin @@ -22,7 +22,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index return } @(cold) - handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) { + handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Index ") print_i64(i64(index)) @@ -83,7 +83,7 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32, return } @(cold) - handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) { + handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid dynamic array indices ") print_i64(i64(low)) @@ -104,7 +104,7 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32 return } @(cold) - handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) { + handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Matrix indices [") print_i64(i64(row_index)) @@ -128,7 +128,7 @@ when ODIN_NO_RTTI { return } @(cold) - handle_error :: proc "contextless" (file: string, line, column: i32) { + handle_error :: proc "contextless" (file: string, line, column: i32) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid type assertion\n") type_assertion_trap() @@ -141,7 +141,7 @@ when ODIN_NO_RTTI { return } @(cold) - handle_error :: proc "contextless" (file: string, line, column: i32) { + handle_error :: proc "contextless" (file: string, line, column: i32) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid type assertion\n") type_assertion_trap() @@ -154,7 +154,7 @@ when ODIN_NO_RTTI { return } @(cold) - handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) { + handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid type assertion from ") print_typeid(from) @@ -199,7 +199,7 @@ when ODIN_NO_RTTI { } @(cold) - handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) { + handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! { actual := variant_type(from, from_data) @@ -225,7 +225,7 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio return } @(cold) - handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) { + handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) -> ! { print_caller_location(loc) print_string(" Invalid slice length for make: ") print_i64(i64(len)) @@ -240,7 +240,7 @@ make_dynamic_array_error_loc :: #force_inline proc "contextless" (using loc := # return } @(cold) - handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) { + handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) -> ! { print_caller_location(loc) print_string(" Invalid dynamic array parameters for make: ") print_i64(i64(len)) @@ -257,7 +257,7 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca return } @(cold) - handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) { + handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) -> ! { print_caller_location(loc) print_string(" Invalid map capacity for make: ") print_i64(i64(cap)) From 09f366bec72087fb8ed835cda6d2b8cc115627e4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Jun 2023 00:03:56 +0100 Subject: [PATCH 060/144] Correct purely named argument handling --- src/check_expr.cpp | 181 ++++++++++++++------------- src/llvm_backend_proc.cpp | 250 +------------------------------------- 2 files changed, 99 insertions(+), 332 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 2e5b23e78..5b1f1a73c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -676,6 +676,11 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand return 1; } break; + case Basic_UntypedString: + if (is_type_string(dst)) { + return 1; + } + break; case Basic_UntypedFloat: if (is_type_float(dst)) { return 1; @@ -701,33 +706,48 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand return -1; } if (src->kind == Type_Basic) { + i64 score = -1; switch (src->Basic.kind) { case Basic_UntypedRune: if (is_type_integer(dst) || is_type_rune(dst)) { - if (is_type_typed(type)) { - return 2; - } - return 1; + score = 1; } - return -1; - case Basic_UntypedBool: - if (is_type_boolean(dst)) { - if (is_type_typed(type)) { - return 2; - } - return 1; + break; + case Basic_UntypedInteger: + if (is_type_integer(dst) || is_type_rune(dst)) { + score = 1; } - return -1; + break; case Basic_UntypedString: - if (is_type_string(dst) || is_type_cstring(dst)) { - if (is_type_typed(type)) { - return 2; - } - return 1; + if (is_type_string(dst)) { + score = 1; } - return -1; + break; + case Basic_UntypedFloat: + if (is_type_float(dst)) { + score = 1; + } + break; + case Basic_UntypedComplex: + if (is_type_complex(dst)) { + score = 1; + } + if (is_type_quaternion(dst)) { + score = 2; + } + break; + case Basic_UntypedQuaternion: + if (is_type_quaternion(dst)) { + score = 1; + } + break; } - + if (score > 0) { + if (is_type_typed(dst)) { + score += 1; + } + } + return score; } } } @@ -5447,14 +5467,9 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A err = CallArgumentError_DuplicateParameter; continue; } - Entity *e = pt->params->Tuple.variables[param_index]; - - check_assignment(c, &operand, e->type, str_lit("named argument")); visited[param_index] = true; ordered_operands[param_index] = operand; - - err = CallArgumentError_DuplicateParameter; } } @@ -5469,7 +5484,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A error(call, "Variadic parameters already handled with a named argument '%.*s' in procedure call", LIT(name)); } err = CallArgumentError_DuplicateParameter; - } else { + } else if (!visited[pt->variadic_index]) { visited[pt->variadic_index] = true; Operand *variadic_operand = &ordered_operands[pt->variadic_index]; @@ -5548,6 +5563,52 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } } + auto eval_param_and_score = [](CheckerContext *c, Operand *o, Type *param_type, CallArgumentError &err, bool param_is_variadic, Entity *e, bool show_error) -> i64 { + i64 s = 0; + if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic)) { + bool ok = false; + if (e && e->flags & EntityFlag_AnyInt) { + if (is_type_integer(param_type)) { + ok = check_is_castable_to(c, o, param_type); + } + } + if (ok) { + s = assign_score_function(MAXIMUM_TYPE_DISTANCE); + } else { + if (show_error) { + check_assignment(c, o, param_type, str_lit("procedure argument")); + } + err = CallArgumentError_WrongTypes; + } + + } else if (show_error) { + check_assignment(c, o, param_type, str_lit("procedure argument")); + } + + if (e && e->flags & EntityFlag_ConstInput) { + if (o->mode != Addressing_Constant) { + if (show_error) { + error(o->expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string)); + } + err = CallArgumentError_NoneConstantParameter; + } + } + + + if (!err && is_type_any(param_type)) { + add_type_info_type(c, o->type); + } + if (o->mode == Addressing_Type && is_type_typeid(param_type)) { + add_type_info_type(c, o->type); + add_type_and_value(c, o->expr, Addressing_Value, param_type, exact_value_typeid(o->type)); + } else if (show_error && is_type_untyped(o->type)) { + update_untyped_expr_type(c, o->expr, param_type, true); + } + + return s; + }; + + if (ordered_operands.count == 0 && param_count_excluding_defaults == 0) { err = CallArgumentError_None; @@ -5555,7 +5616,9 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A GB_ASSERT(pt->params != nullptr && pt->params->Tuple.variables.count > 0); Type *t = pt->params->Tuple.variables[0]->type; if (is_type_polymorphic(t)) { - error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input"); + if (show_error) { + error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input"); + } err = CallArgumentError_AmbiguousPolymorphicVariadic; } } @@ -5596,46 +5659,14 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } else { score += assign_score_function(MAXIMUM_TYPE_DISTANCE); } - } else { - i64 s = 0; - if (!check_is_assignable_to_with_score(c, o, e->type, &s, param_is_variadic)) { - bool ok = false; - if (e->flags & EntityFlag_AnyInt) { - if (is_type_integer(e->type)) { - ok = check_is_castable_to(c, o, e->type); - } - } - if (ok) { - s = assign_score_function(MAXIMUM_TYPE_DISTANCE); - } else { - if (show_error) { - check_assignment(c, o, e->type, str_lit("procedure argument")); - } - err = CallArgumentError_WrongTypes; - } - - if (e->flags & EntityFlag_ConstInput) { - if (o->mode != Addressing_Constant) { - if (show_error) { - error(o->expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string)); - } - err = CallArgumentError_NoneConstantParameter; - } - } - } else if (show_error) { - check_assignment(c, o, e->type, str_lit("procedure argument")); - } - if (!param_is_variadic) { - score += s; - } + continue; } - if (o->mode == Addressing_Type && is_type_typeid(e->type)) { - add_type_info_type(c, o->type); - add_type_and_value(c, o->expr, Addressing_Value, e->type, exact_value_typeid(o->type)); - } else if (show_error && is_type_untyped(o->type)) { - update_untyped_expr_type(c, o->expr, e->type, true); + if (param_is_variadic) { + continue; } + + score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error); } } @@ -5651,12 +5682,12 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } for_array(operand_index, variadic_operands) { - Operand &o = variadic_operands[operand_index]; + Operand *o = &variadic_operands[operand_index]; if (vari_expand) { t = slice; if (operand_index > 0) { if (show_error) { - error(o.expr, "'..' in a variadic procedure can only have one variadic argument at the end"); + error(o->expr, "'..' in a variadic procedure can only have one variadic argument at the end"); } if (data) { data->score = score; @@ -5666,25 +5697,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A return CallArgumentError_MultipleVariadicExpand; } } - i64 s = 0; - if (!check_is_assignable_to_with_score(c, &o, t, &s, true)) { - if (show_error) { - check_assignment(c, &o, t, str_lit("variadic argument")); - } - err = CallArgumentError_WrongTypes; - } else if (show_error) { - check_assignment(c, &o, t, str_lit("variadic argument")); - } - score += s; - if (is_type_any(elem)) { - add_type_info_type(c, o.type); - } - if (o.mode == Addressing_Type && is_type_typeid(t)) { - add_type_info_type(c, o.type); - add_type_and_value(c, o.expr, Addressing_Value, t, exact_value_typeid(o.type)); - } else if (show_error && is_type_untyped(o.type)) { - update_untyped_expr_type(c, o.expr, t, true); - } + score += eval_param_and_score(c, o, t, err, true, nullptr, show_error); } } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index b7ee88804..fc58d584e 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3336,253 +3336,7 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { GB_ASSERT(proc_type_->kind == Type_Proc); TypeProc *pt = &proc_type_->Proc; - if (ce->split_args) { - return lb_build_call_expr_internal_with_arg_split_args(p, value, pt, expr, ce->split_args); - } - - if (is_call_expr_field_value(ce)) { - auto args = array_make(permanent_allocator(), pt->param_count); - - for_array(arg_index, ce->args) { - Ast *arg = ce->args[arg_index]; - ast_node(fv, FieldValue, arg); - GB_ASSERT(fv->field->kind == Ast_Ident); - String name = fv->field->Ident.token.string; - isize index = lookup_procedure_parameter(pt, name); - GB_ASSERT(index >= 0); - TypeAndValue tav = type_and_value_of_expr(fv->value); - if (tav.mode == Addressing_Type) { - args[index] = lb_const_nil(m, tav.type); - } else { - args[index] = lb_build_expr(p, fv->value); - } - } - TypeTuple *params = &pt->params->Tuple; - for (isize i = 0; i < args.count; i++) { - Entity *e = params->variables[i]; - if (e->kind == Entity_TypeName) { - args[i] = lb_const_nil(m, e->type); - } else if (e->kind == Entity_Constant) { - continue; - } else { - GB_ASSERT(e->kind == Entity_Variable); - if (args[i].value == nullptr) { - args[i] = lb_handle_param_value(p, e->type, e->Variable.param_value, ast_token(expr).pos); - } else if (is_type_typeid(e->type) && !is_type_typeid(args[i].type)) { - args[i] = lb_typeid(p->module, args[i].type); - } else { - args[i] = lb_emit_conv(p, args[i], e->type); - } - } - } - - for (isize i = 0; i < args.count; i++) { - Entity *e = params->variables[i]; - if (args[i].type == nullptr) { - continue; - } else if (is_type_untyped_uninit(args[i].type)) { - args[i] = lb_const_undef(m, e->type); - } else if (is_type_untyped_nil(args[i].type)) { - args[i] = lb_const_nil(m, e->type); - } - } - - return lb_emit_call(p, value, args, ce->inlining); - } - - isize arg_index = 0; - - isize arg_count = 0; - for_array(i, ce->args) { - Ast *arg = ce->args[i]; - if (arg->kind == Ast_FieldValue) { - arg = arg->FieldValue.value; - } - TypeAndValue tav = type_and_value_of_expr(arg); - GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s %d", expr_to_string(arg), expr_to_string(expr), tav.mode); - GB_ASSERT_MSG(tav.mode != Addressing_ProcGroup, "%s", expr_to_string(arg)); - Type *at = tav.type; - if (is_type_tuple(at)) { - arg_count += at->Tuple.variables.count; - } else { - arg_count++; - } - } - - isize param_count = 0; - if (pt->params) { - GB_ASSERT(pt->params->kind == Type_Tuple); - param_count = pt->params->Tuple.variables.count; - } - - auto args = array_make(permanent_allocator(), cast(isize)gb_max(param_count, arg_count)); - isize variadic_index = pt->variadic_index; - bool variadic = pt->variadic && variadic_index >= 0; - bool vari_expand = ce->ellipsis.pos.line != 0; - bool is_c_vararg = pt->c_vararg; - - String proc_name = {}; - if (p->entity != nullptr) { - proc_name = p->entity->token.string; - } - TokenPos pos = ast_token(ce->proc).pos; - - TypeTuple *param_tuple = nullptr; - if (pt->params) { - GB_ASSERT(pt->params->kind == Type_Tuple); - param_tuple = &pt->params->Tuple; - } - - for_array(i, ce->args) { - Ast *arg = ce->args[i]; - TypeAndValue arg_tv = type_and_value_of_expr(arg); - if (arg_tv.mode == Addressing_Type) { - args[arg_index++] = lb_const_nil(m, arg_tv.type); - } else { - lbValue a = lb_build_expr(p, arg); - Type *at = a.type; - if (at->kind == Type_Tuple) { - lbTupleFix *tf = map_get(&p->tuple_fix_map, a.value); - if (tf) { - for_array(j, tf->values) { - args[arg_index++] = tf->values[j]; - } - } else { - for_array(j, at->Tuple.variables) { - lbValue v = lb_emit_struct_ev(p, a, cast(i32)j); - args[arg_index++] = v; - } - } - } else { - args[arg_index++] = a; - } - } - } - - - if (param_count > 0) { - GB_ASSERT_MSG(pt->params != nullptr, "%s %td", expr_to_string(expr), pt->param_count); - GB_ASSERT(param_count < 1000000); - - if (arg_count < param_count) { - isize end = cast(isize)param_count; - if (variadic) { - end = variadic_index; - } - while (arg_index < end) { - Entity *e = param_tuple->variables[arg_index]; - GB_ASSERT(e->kind == Entity_Variable); - args[arg_index++] = lb_handle_param_value(p, e->type, e->Variable.param_value, ast_token(expr).pos); - } - } - - if (is_c_vararg) { - GB_ASSERT(variadic); - GB_ASSERT(!vari_expand); - isize i = 0; - for (; i < variadic_index; i++) { - Entity *e = param_tuple->variables[i]; - if (e->kind == Entity_Variable) { - args[i] = lb_emit_conv(p, args[i], e->type); - } - } - Type *variadic_type = param_tuple->variables[i]->type; - GB_ASSERT(is_type_slice(variadic_type)); - variadic_type = base_type(variadic_type)->Slice.elem; - if (!is_type_any(variadic_type)) { - for (; i < arg_count; i++) { - args[i] = lb_emit_conv(p, args[i], variadic_type); - } - } else { - for (; i < arg_count; i++) { - args[i] = lb_emit_conv(p, args[i], default_type(args[i].type)); - } - } - } else if (variadic) { - isize i = 0; - for (; i < variadic_index; i++) { - Entity *e = param_tuple->variables[i]; - if (e->kind == Entity_Variable) { - args[i] = lb_emit_conv(p, args[i], e->type); - } - } - if (!vari_expand) { - Type *variadic_type = param_tuple->variables[i]->type; - GB_ASSERT(is_type_slice(variadic_type)); - variadic_type = base_type(variadic_type)->Slice.elem; - for (; i < arg_count; i++) { - args[i] = lb_emit_conv(p, args[i], variadic_type); - } - } - } else { - for (isize i = 0; i < param_count; i++) { - Entity *e = param_tuple->variables[i]; - if (e->kind == Entity_Variable) { - if (args[i].value == nullptr) { - continue; - } - GB_ASSERT_MSG(args[i].value != nullptr, "%.*s", LIT(e->token.string)); - if (is_type_typeid(e->type) && !is_type_typeid(args[i].type)) { - GB_ASSERT(LLVMIsNull(args[i].value)); - args[i] = lb_typeid(p->module, args[i].type); - } else { - args[i] = lb_emit_conv(p, args[i], e->type); - } - } - } - } - - if (variadic && !vari_expand && !is_c_vararg) { - // variadic call argument generation - Type *slice_type = param_tuple->variables[variadic_index]->type; - Type *elem_type = base_type(slice_type)->Slice.elem; - lbAddr slice = lb_add_local_generated(p, slice_type, true); - isize slice_len = arg_count+1 - (variadic_index+1); - - if (slice_len > 0) { - lbAddr base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true); - - for (isize i = variadic_index, j = 0; i < arg_count; i++, j++) { - lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)j); - lb_emit_store(p, addr, args[i]); - } - - lbValue base_elem = lb_emit_array_epi(p, base_array.addr, 0); - lbValue len = lb_const_int(m, t_int, slice_len); - lb_fill_slice(p, slice, base_elem, len); - } - - arg_count = param_count; - args[variadic_index] = lb_addr_load(p, slice); - } - } - - if (variadic && variadic_index+1 < param_count) { - for (isize i = variadic_index+1; i < param_count; i++) { - Entity *e = param_tuple->variables[i]; - args[i] = lb_handle_param_value(p, e->type, e->Variable.param_value, ast_token(expr).pos); - } - } - - isize final_count = param_count; - if (is_c_vararg) { - final_count = arg_count; - } - - if (param_tuple != nullptr) { - for (isize i = 0; i < gb_min(args.count, param_tuple->variables.count); i++) { - Entity *e = param_tuple->variables[i]; - if (args[i].type == nullptr) { - continue; - } else if (is_type_untyped_uninit(args[i].type)) { - args[i] = lb_const_undef(m, e->type); - } else if (is_type_untyped_nil(args[i].type)) { - args[i] = lb_const_nil(m, e->type); - } - } - } - - auto call_args = array_slice(args, 0, final_count); - return lb_emit_call(p, value, call_args, ce->inlining); + GB_ASSERT(ce->split_args != nullptr); + return lb_build_call_expr_internal_with_arg_split_args(p, value, pt, expr, ce->split_args); } From 18746c1444c8137fc0d78659a66c8f63079963e8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Jun 2023 00:40:02 +0100 Subject: [PATCH 061/144] Refactor call argument checking to a single procedure --- src/check_expr.cpp | 249 ++++++++++++++++++--------------------------- 1 file changed, 97 insertions(+), 152 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 5b1f1a73c..b9cc311a8 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5994,6 +5994,75 @@ gb_internal bool check_named_arguments(CheckerContext *c, Type *type, Slice const &positional_operands, Array const &named_operands, + CallArgumentErrorMode show_error_mode, + CallArgumentData *data) { + + bool return_on_failure = show_error_mode == CallArgumentMode_NoErrors; + + Ast *ident = operand->expr; + while (ident->kind == Ast_SelectorExpr) { + Ast *s = ident->SelectorExpr.selector; + ident = s; + } + + if (e == nullptr) { + GB_ASSERT(proc_type == nullptr); + e = entity_of_node(ident); + proc_type = e->type; + } + + proc_type = base_type(proc_type); + GB_ASSERT(proc_type->kind == Type_Proc); + + CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, positional_operands, named_operands, show_error_mode, data); + if (return_on_failure && err != CallArgumentError_None) { + return false; + } + + Entity *entity_to_use = data->gen_entity != nullptr ? data->gen_entity : e; + if (!return_on_failure) { + add_entity_use(c, ident, entity_to_use); + if (entity_to_use != nullptr) { + update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); + add_type_and_value(c, operand->expr, operand->mode, entity_to_use->type, operand->value); + } + } + + if (data->gen_entity != nullptr) { + Entity *e = data->gen_entity; + DeclInfo *decl = data->gen_entity->decl_info; + CheckerContext ctx = *c; + ctx.scope = decl->scope; + ctx.decl = decl; + ctx.proc_name = e->token.string; + ctx.curr_proc_decl = decl; + ctx.curr_proc_sig = e->type; + + GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); + bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, !return_on_failure); + if (return_on_failure) { + if (!ok) { + return false; + } + + } else { + decl->where_clauses_evaluated = true; + if (ok && (data->gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { + check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); + } + if (is_type_proc(data->gen_entity->type)) { + Type *t = base_type(entity_to_use->type); + data->result_type = t->Proc.results; + } + } + } + + return true; +} + gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(CheckerContext *c, Operand *operand, Ast *call) { ast_node(ce, CallExpr, call); @@ -6089,11 +6158,6 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch defer (array_free(&named_operands)); if (procs.count == 1) { - Ast *ident = operand->expr; - while (ident->kind == Ast_SelectorExpr) { - Ast *s = ident->SelectorExpr.selector; - ident = s; - } Entity *e = procs[0]; @@ -6105,46 +6169,11 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch return data; } - CallArgumentError err = check_call_arguments_internal(c, call, e->type, e, positional_operands, named_operands, CallArgumentMode_ShowErrors, &data); - if (err != CallArgumentError_None) { - - } - - - Type *proc_type = base_type(operand->type); - - Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; - add_entity_use(c, ident, entity_to_use); - if (entity_to_use != nullptr) { - update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); - - proc_type = base_type(entity_to_use->type); - } - - if (proc_type && proc_type->kind == Type_Proc) { - data.result_type = proc_type->Proc.results; - add_type_and_value(c, operand->expr, operand->mode, proc_type, operand->value); - } - - if (data.gen_entity != nullptr) { - Entity *e = data.gen_entity; - DeclInfo *decl = data.gen_entity->decl_info; - CheckerContext ctx = *c; - ctx.scope = decl->scope; - ctx.decl = decl; - ctx.proc_name = e->token.string; - ctx.curr_proc_decl = decl; - ctx.curr_proc_sig = e->type; - - GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); - bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); - decl->where_clauses_evaluated = true; - - if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { - check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); - } - } - + check_call_arguments_new_and_improved_single(c, call, operand, + e, e->type, + positional_operands, named_operands, + CallArgumentMode_ShowErrors, + &data); return data; } @@ -6256,7 +6285,6 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch Entity *p = procs[i]; Type *pt = base_type(p->type); if (pt != nullptr && is_type_proc(pt)) { - CallArgumentError err = CallArgumentError_None; CallArgumentData data = {}; CheckerContext ctx = *c; @@ -6264,26 +6292,17 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch ctx.allow_polymorphic_types = is_type_polymorphic(pt); ctx.hide_polymorphic_errors = true; - err = check_call_arguments_internal(&ctx, call, pt, p, positional_operands, named_operands, CallArgumentMode_NoErrors, &data); - if (err != CallArgumentError_None) { + bool is_a_candidate = check_call_arguments_new_and_improved_single(&ctx, call, operand, + p, pt, + positional_operands, named_operands, + CallArgumentMode_NoErrors, + &data); + if (!is_a_candidate) { continue; } isize index = i; if (data.gen_entity != nullptr) { - Entity *e = data.gen_entity; - DeclInfo *decl = data.gen_entity->decl_info; - ctx.scope = decl->scope; - ctx.decl = decl; - ctx.proc_name = e->token.string; - ctx.curr_proc_decl = decl; - ctx.curr_proc_sig = e->type; - - GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); - if (!evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) { - continue; - } - array_add(&proc_entities, data.gen_entity); index = proc_entities.count-1; } @@ -6449,41 +6468,11 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch Array named_operands = {}; - Type *proc_type = e->type; - CallArgumentData data = {}; - CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, positional_operands, named_operands, CallArgumentMode_ShowErrors, &data); - gb_unused(err); - Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; - add_entity_use(c, ident, entity_to_use); - if (entity_to_use != nullptr) { - proc_type = base_type(entity_to_use->type); - update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); - add_type_and_value(c, operand->expr, operand->mode, entity_to_use->type, operand->value); - } - - if (data.gen_entity != nullptr) { - Entity *e = data.gen_entity; - DeclInfo *decl = data.gen_entity->decl_info; - CheckerContext ctx = *c; - ctx.scope = decl->scope; - ctx.decl = decl; - ctx.proc_name = e->token.string; - ctx.curr_proc_decl = decl; - ctx.curr_proc_sig = e->type; - - GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); - bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); - decl->where_clauses_evaluated = true; - - if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { - check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); - } - } - - if (is_type_proc(proc_type)) { - data.result_type = proc_type->Proc.results; - } - + check_call_arguments_new_and_improved_single(c, call, operand, + e, e->type, + positional_operands, named_operands, + CallArgumentMode_ShowErrors, + &data); return data; } @@ -6539,24 +6528,18 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex return check_call_arguments_new_and_improved_proc_group(c, operand, call); } - auto positional_operands = array_make(heap_allocator(), 0, positional_args.count); auto named_operands = array_make(heap_allocator(), 0, 0); defer (array_free(&positional_operands)); defer (array_free(&named_operands)); - - Slice visited = {}; - if (positional_args.count > 0) { isize lhs_count = -1; bool is_variadic = false; Entity **lhs = nullptr; if (pt != nullptr) { lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic); - } else if (operand->mode == Addressing_ProcGroup) { - // TODO } check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); } @@ -6597,49 +6580,11 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex } if (!any_failure) { - Ast *ident = operand->expr; - while (ident->kind == Ast_SelectorExpr) { - Ast *s = ident->SelectorExpr.selector; - ident = s; - } - - Entity *e = entity_of_node(ident); - - TokenPos pos = ast_token(call).pos; - - CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, positional_operands, named_operands, CallArgumentMode_ShowErrors, &data); - gb_unused(err); - Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; - add_entity_use(c, ident, entity_to_use); - if (entity_to_use != nullptr) { - update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); - add_type_and_value(c, operand->expr, operand->mode, entity_to_use->type, operand->value); - } - if (data.gen_entity != nullptr) { - Entity *e = data.gen_entity; - DeclInfo *decl = data.gen_entity->decl_info; - CheckerContext ctx = *c; - ctx.scope = decl->scope; - ctx.decl = decl; - ctx.proc_name = e->token.string; - ctx.curr_proc_decl = decl; - ctx.curr_proc_sig = e->type; - - GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); - bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); - decl->where_clauses_evaluated = true; - - if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { - check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); - } - - if (is_type_proc(data.gen_entity->type)) { - Type *t = base_type(entity_to_use->type); - // GB_ASSERT_MSG(!is_type_polymorphic(t), "%s", expr_to_string(call)); - data.result_type = t->Proc.results; - - } - } + check_call_arguments_new_and_improved_single(c, call, operand, + nullptr, nullptr, + positional_operands, named_operands, + CallArgumentMode_ShowErrors, + &data); } else if (pt) { data.result_type = pt->results; } @@ -6991,16 +6936,16 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O -// returns true on failure -gb_internal bool check_call_parameter_mixture(Operand *operand, Slice const &args, char const *context, bool allow_mixed=false) { - bool failure = false; +// returns true on success +gb_internal bool check_call_parameter_mixture(Slice const &args, char const *context, bool allow_mixed=false) { + bool success = true; if (args.count > 0) { if (allow_mixed) { bool was_named = false; for (Ast *arg : args) { if (was_named && arg->kind != Ast_FieldValue) { error(arg, "Non-named parameter is not allowed to follow named parameter i.e. 'field = value' in a %s", context); - failure = true; + success = false; break; } was_named = was_named || arg->kind == Ast_FieldValue; @@ -7016,16 +6961,16 @@ gb_internal bool check_call_parameter_mixture(Operand *operand, Slice con } if (mix) { error(arg, "Mixture of 'field = value' and value elements in a %s is not allowed", context); - failure = true; + success = false; } } } } - return failure; + return success; } -#define CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN(context_, ...) if (check_call_parameter_mixture(operand, args, context_, ##__VA_ARGS__)) { \ +#define CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN(context_, ...) if (!check_call_parameter_mixture(args, context_, ##__VA_ARGS__)) { \ operand->mode = Addressing_Invalid; \ operand->expr = call; \ return Expr_Stmt; \ From b2ced834ba01c61c8ec842af2de5f480d1e93462 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Jun 2023 00:43:51 +0100 Subject: [PATCH 062/144] Minor code reshuffle --- src/check_expr.cpp | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b9cc311a8..8731a0caf 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -38,9 +38,9 @@ gb_global char const *CallArgumentError_strings[CallArgumentError_MAX] = { }; -enum CallArgumentErrorMode { - CallArgumentMode_NoErrors, - CallArgumentMode_ShowErrors, +enum struct CallArgumentErrorMode { + NoErrors, + ShowErrors, }; struct CallArgumentData { @@ -5362,7 +5362,11 @@ gb_internal isize lookup_procedure_parameter(Type *type, String const ¶meter return lookup_procedure_parameter(&type->Proc, parameter_name); } -gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, Ast *call, Type *proc_type, Entity *entity, Array positional_operands, Array const &named_operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data) { +gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, Ast *call, + Entity *entity, Type *proc_type, + Array positional_operands, Array const &named_operands, + CallArgumentErrorMode show_error_mode, + CallArgumentData *data) { TEMPORARY_ALLOCATOR_GUARD(); CallArgumentError err = CallArgumentError_None; @@ -5377,7 +5381,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A bool variadic = pt->variadic; bool vari_expand = (ce->ellipsis.pos.line != 0); i64 score = 0; - bool show_error = show_error_mode == CallArgumentMode_ShowErrors; + bool show_error = show_error_mode == CallArgumentErrorMode::ShowErrors; Type *final_proc_type = proc_type; Entity *gen_entity = nullptr; @@ -6000,7 +6004,7 @@ gb_internal bool check_call_arguments_new_and_improved_single(CheckerContext *c, CallArgumentErrorMode show_error_mode, CallArgumentData *data) { - bool return_on_failure = show_error_mode == CallArgumentMode_NoErrors; + bool return_on_failure = show_error_mode == CallArgumentErrorMode::NoErrors; Ast *ident = operand->expr; while (ident->kind == Ast_SelectorExpr) { @@ -6017,7 +6021,7 @@ gb_internal bool check_call_arguments_new_and_improved_single(CheckerContext *c, proc_type = base_type(proc_type); GB_ASSERT(proc_type->kind == Type_Proc); - CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, positional_operands, named_operands, show_error_mode, data); + CallArgumentError err = check_call_arguments_internal(c, call, e, proc_type, positional_operands, named_operands, show_error_mode, data); if (return_on_failure && err != CallArgumentError_None) { return false; } @@ -6158,22 +6162,18 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch defer (array_free(&named_operands)); if (procs.count == 1) { - - Entity *e = procs[0]; lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic); check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); - if (!check_named_arguments(c, e->type, named_args, &named_operands, true)) { - return data; + if (check_named_arguments(c, e->type, named_args, &named_operands, true)) { + check_call_arguments_new_and_improved_single(c, call, operand, + e, e->type, + positional_operands, named_operands, + CallArgumentErrorMode::ShowErrors, + &data); } - - check_call_arguments_new_and_improved_single(c, call, operand, - e, e->type, - positional_operands, named_operands, - CallArgumentMode_ShowErrors, - &data); return data; } @@ -6295,7 +6295,7 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch bool is_a_candidate = check_call_arguments_new_and_improved_single(&ctx, call, operand, p, pt, positional_operands, named_operands, - CallArgumentMode_NoErrors, + CallArgumentErrorMode::NoErrors, &data); if (!is_a_candidate) { continue; @@ -6471,7 +6471,7 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch check_call_arguments_new_and_improved_single(c, call, operand, e, e->type, positional_operands, named_operands, - CallArgumentMode_ShowErrors, + CallArgumentErrorMode::ShowErrors, &data); return data; } @@ -6583,7 +6583,7 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex check_call_arguments_new_and_improved_single(c, call, operand, nullptr, nullptr, positional_operands, named_operands, - CallArgumentMode_ShowErrors, + CallArgumentErrorMode::ShowErrors, &data); } else if (pt) { data.result_type = pt->results; From 67ca9166d36375fd42725c16ca3933d4c91ebfee Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Jun 2023 01:03:21 +0100 Subject: [PATCH 063/144] Allow named arguments variadic expansion `..` --- src/parser.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index 96b7c96ea..b756412ff 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2752,9 +2752,9 @@ gb_internal Ast *parse_call_expr(AstFile *f, Ast *operand) { open_paren = expect_token(f, Token_OpenParen); + bool seen_ellipsis = false; while (f->curr_token.kind != Token_CloseParen && - f->curr_token.kind != Token_EOF && - ellipsis.pos.line == 0) { + f->curr_token.kind != Token_EOF) { if (f->curr_token.kind == Token_Comma) { syntax_error(f->curr_token, "Expected an expression not ,"); } else if (f->curr_token.kind == Token_Eq) { @@ -2777,9 +2777,15 @@ gb_internal Ast *parse_call_expr(AstFile *f, Ast *operand) { Ast *value = parse_value(f); arg = ast_field_value(f, arg, value, eq); + } else if (seen_ellipsis) { + syntax_error(arg, "Positional arguments are not allowed after '..'"); } array_add(&args, arg); + if (ellipsis.pos.line != 0) { + seen_ellipsis = true; + } + if (!allow_field_separator(f)) { break; } From 9b54b99bf696a65bc5c8358b2a5ace1f6dc4fdcc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Jun 2023 01:17:05 +0100 Subject: [PATCH 064/144] Use positional and named arguments within the core library --- core/c/frontend/preprocessor/preprocess.odin | 4 +- core/compress/gzip/gzip.odin | 2 +- core/compress/zlib/zlib.odin | 6 +-- core/fmt/fmt.odin | 18 +++---- core/fmt/fmt_os.odin | 12 ++--- core/image/png/helpers.odin | 8 +-- core/log/log.odin | 26 +++++----- core/log/log_allocator.odin | 54 ++++++++++---------- core/math/big/radix.odin | 2 +- core/mem/virtual/arena.odin | 2 +- core/runtime/core_builtin.odin | 12 ++--- core/testing/runner_windows.odin | 2 +- core/testing/testing.odin | 10 ++-- core/text/table/table.odin | 2 +- examples/demo/demo.odin | 4 +- 15 files changed, 82 insertions(+), 82 deletions(-) diff --git a/core/c/frontend/preprocessor/preprocess.odin b/core/c/frontend/preprocessor/preprocess.odin index 4cfad2c1c..b5eab0bb3 100644 --- a/core/c/frontend/preprocessor/preprocess.odin +++ b/core/c/frontend/preprocessor/preprocess.odin @@ -1118,7 +1118,7 @@ expand_macro :: proc(cpp: ^Preprocessor, rest: ^^Token, tok: ^Token) -> bool { search_include_next :: proc(cpp: ^Preprocessor, filename: string) -> (path: string, ok: bool) { for ; cpp.include_next_index < len(cpp.include_paths); cpp.include_next_index += 1 { - tpath := filepath.join(elems={cpp.include_paths[cpp.include_next_index], filename}, allocator=context.temp_allocator) + tpath := filepath.join({cpp.include_paths[cpp.include_next_index], filename}, allocator=context.temp_allocator) if os.exists(tpath) { return strings.clone(tpath), true } @@ -1136,7 +1136,7 @@ search_include_paths :: proc(cpp: ^Preprocessor, filename: string) -> (path: str } for include_path in cpp.include_paths { - tpath := filepath.join(elems={include_path, filename}, allocator=context.temp_allocator) + tpath := filepath.join({include_path, filename}, allocator=context.temp_allocator) if os.exists(tpath) { path, ok = strings.clone(tpath), true cpp.filepath_cache[filename] = path diff --git a/core/compress/gzip/gzip.odin b/core/compress/gzip/gzip.odin index 4de4d1b63..b0ca4491b 100644 --- a/core/compress/gzip/gzip.odin +++ b/core/compress/gzip/gzip.odin @@ -335,7 +335,7 @@ load_from_context :: proc(z: ^$C, buf: ^bytes.Buffer, known_gzip_size := -1, exp // fmt.printf("GZIP: Expected Payload Size: %v\n", expected_output_size); - zlib_error := zlib.inflate_raw(z=z, expected_output_size=expected_output_size) + zlib_error := zlib.inflate_raw(z, expected_output_size=expected_output_size) if zlib_error != nil { return zlib_error } diff --git a/core/compress/zlib/zlib.odin b/core/compress/zlib/zlib.odin index 855eef7a8..21172e8e8 100644 --- a/core/compress/zlib/zlib.odin +++ b/core/compress/zlib/zlib.odin @@ -471,7 +471,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f } // Parse ZLIB stream without header. - inflate_raw(z=ctx, expected_output_size=expected_output_size) or_return + inflate_raw(ctx, expected_output_size=expected_output_size) or_return if !raw { compress.discard_to_next_byte_lsb(ctx) @@ -665,7 +665,7 @@ inflate_from_byte_array :: proc(input: []u8, buf: ^bytes.Buffer, raw := false, e ctx.input_data = input ctx.output = buf - return inflate_from_context(ctx=&ctx, raw=raw, expected_output_size=expected_output_size) + return inflate_from_context(&ctx, raw=raw, expected_output_size=expected_output_size) } inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := false, expected_output_size := -1) -> (err: Error) { @@ -674,7 +674,7 @@ inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := fals ctx.input_data = input ctx.output = buf - return inflate_raw(z=&ctx, expected_output_size=expected_output_size) + return inflate_raw(&ctx, expected_output_size=expected_output_size) } inflate :: proc{inflate_from_context, inflate_from_byte_array} diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 61f7b5d99..f1f94b1b3 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -123,7 +123,7 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist aprint :: proc(args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str) - sbprint(buf=&str, args=args, sep=sep) + sbprint(&str, ..args, sep=sep) return strings.to_string(str) } // Creates a formatted string with a newline character at the end @@ -139,7 +139,7 @@ aprint :: proc(args: ..any, sep := " ") -> string { aprintln :: proc(args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str) - sbprintln(buf=&str, args=args, sep=sep) + sbprintln(&str, ..args, sep=sep) return strings.to_string(str) } // Creates a formatted string using a format string and arguments @@ -171,7 +171,7 @@ aprintf :: proc(fmt: string, args: ..any) -> string { tprint :: proc(args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) - sbprint(buf=&str, args=args, sep=sep) + sbprint(&str, ..args, sep=sep) return strings.to_string(str) } // Creates a formatted string with a newline character at the end @@ -187,7 +187,7 @@ tprint :: proc(args: ..any, sep := " ") -> string { tprintln :: proc(args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) - sbprintln(buf=&str, args=args, sep=sep) + sbprintln(&str, ..args, sep=sep) return strings.to_string(str) } // Creates a formatted string using a format string and arguments @@ -217,7 +217,7 @@ tprintf :: proc(fmt: string, args: ..any) -> string { // bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string { sb := strings.builder_from_bytes(buf[0:len(buf)]) - return sbprint(buf=&sb, args=args, sep=sep) + return sbprint(&sb, ..args, sep=sep) } // Creates a formatted string using a supplied buffer as the backing array, appends newline. Writes into the buffer. // @@ -230,7 +230,7 @@ bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string { // bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string { sb := strings.builder_from_bytes(buf[0:len(buf)]) - return sbprintln(buf=&sb, args=args, sep=sep) + return sbprintln(&sb, ..args, sep=sep) } // Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer. // @@ -327,7 +327,7 @@ ctprintf :: proc(format: string, args: ..any) -> cstring { // Returns: A formatted string // sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { - wprint(w=strings.to_writer(buf), args=args, sep=sep) + wprint(strings.to_writer(buf), ..args, sep=sep) return strings.to_string(buf^) } // Formats and writes to a strings.Builder buffer using the default print settings @@ -340,7 +340,7 @@ sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { // Returns: The resulting formatted string // sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { - wprintln(w=strings.to_writer(buf), args=args, sep=sep) + wprintln(strings.to_writer(buf), ..args, sep=sep) return strings.to_string(buf^) } // Formats and writes to a strings.Builder buffer according to the specified format string @@ -353,7 +353,7 @@ sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { // Returns: The resulting formatted string // sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string { - wprintf(w=strings.to_writer(buf), fmt=fmt, args=args) + wprintf(strings.to_writer(buf), fmt, ..args) return strings.to_string(buf^) } // Formats and writes to an io.Writer using the default print settings diff --git a/core/fmt/fmt_os.odin b/core/fmt/fmt_os.odin index 861b0c3b9..5b3a68502 100644 --- a/core/fmt/fmt_os.odin +++ b/core/fmt/fmt_os.odin @@ -14,7 +14,7 @@ fprint :: proc(fd: os.Handle, args: ..any, sep := " ") -> int { bufio.writer_init_with_buf(&b, {os.stream_from_handle(fd)}, buf[:]) w := bufio.writer_to_writer(&b) - return wprint(w=w, args=args, sep=sep) + return wprint(w, ..args, sep=sep) } // fprintln formats using the default print settings and writes to fd @@ -26,7 +26,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int { bufio.writer_init_with_buf(&b, {os.stream_from_handle(fd)}, buf[:]) w := bufio.writer_to_writer(&b) - return wprintln(w=w, args=args, sep=sep) + return wprintln(w, ..args, sep=sep) } // fprintf formats according to the specified format string and writes to fd fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int { @@ -61,15 +61,15 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) { } // print formats using the default print settings and writes to os.stdout -print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) } +print :: proc(args: ..any, sep := " ") -> int { return fprint(os.stdout, ..args, sep=sep) } // println formats using the default print settings and writes to os.stdout -println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) } +println :: proc(args: ..any, sep := " ") -> int { return fprintln(os.stdout, ..args, sep=sep) } // printf formats according to the specified format string and writes to os.stdout printf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) } // eprint formats using the default print settings and writes to os.stderr -eprint :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) } +eprint :: proc(args: ..any, sep := " ") -> int { return fprint(os.stderr, ..args, sep=sep) } // eprintln formats using the default print settings and writes to os.stderr -eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) } +eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(os.stderr, ..args, sep=sep) } // eprintf formats according to the specified format string and writes to os.stderr eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) } diff --git a/core/image/png/helpers.odin b/core/image/png/helpers.odin index bfe56d62e..025c2b866 100644 --- a/core/image/png/helpers.odin +++ b/core/image/png/helpers.odin @@ -99,7 +99,7 @@ text :: proc(c: image.PNG_Chunk) -> (res: Text, ok: bool) { case .tEXt: ok = true - fields := bytes.split(s=c.data, sep=[]u8{0}, allocator=context.temp_allocator) + fields := bytes.split(c.data, sep=[]u8{0}, allocator=context.temp_allocator) if len(fields) == 2 { res.keyword = strings.clone(string(fields[0])) res.text = strings.clone(string(fields[1])) @@ -110,7 +110,7 @@ text :: proc(c: image.PNG_Chunk) -> (res: Text, ok: bool) { case .zTXt: ok = true - fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator) + fields := bytes.split_n(c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator) if len(fields) != 3 || len(fields[1]) != 0 { // Compression method must be 0=Deflate, which thanks to the split above turns // into an empty slice @@ -199,7 +199,7 @@ text_destroy :: proc(text: Text) { iccp :: proc(c: image.PNG_Chunk) -> (res: iCCP, ok: bool) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) - fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator) + fields := bytes.split_n(c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator) if len(fields[0]) < 1 || len(fields[0]) > 79 { // Invalid profile name @@ -263,7 +263,7 @@ splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) { } runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) - fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=2, allocator=context.temp_allocator) + fields := bytes.split_n(c.data, sep=[]u8{0}, n=2, allocator=context.temp_allocator) if len(fields) != 2 { return } diff --git a/core/log/log.odin b/core/log/log.odin index a699247b8..f3554791b 100644 --- a/core/log/log.odin +++ b/core/log/log.odin @@ -76,43 +76,43 @@ nil_logger :: proc() -> Logger { } debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) { - logf(level=.Debug, fmt_str=fmt_str, args=args, location=location) + logf(.Debug, fmt_str, ..args, location=location) } infof :: proc(fmt_str: string, args: ..any, location := #caller_location) { - logf(level=.Info, fmt_str=fmt_str, args=args, location=location) + logf(.Info, fmt_str, ..args, location=location) } warnf :: proc(fmt_str: string, args: ..any, location := #caller_location) { - logf(level=.Warning, fmt_str=fmt_str, args=args, location=location) + logf(.Warning, fmt_str, ..args, location=location) } errorf :: proc(fmt_str: string, args: ..any, location := #caller_location) { - logf(level=.Error, fmt_str=fmt_str, args=args, location=location) + logf(.Error, fmt_str, ..args, location=location) } fatalf :: proc(fmt_str: string, args: ..any, location := #caller_location) { - logf(level=.Fatal, fmt_str=fmt_str, args=args, location=location) + logf(.Fatal, fmt_str, ..args, location=location) } debug :: proc(args: ..any, sep := " ", location := #caller_location) { - log(level=.Debug, args=args, sep=sep, location=location) + log(.Debug, ..args, sep=sep, location=location) } info :: proc(args: ..any, sep := " ", location := #caller_location) { - log(level=.Info, args=args, sep=sep, location=location) + log(.Info, ..args, sep=sep, location=location) } warn :: proc(args: ..any, sep := " ", location := #caller_location) { - log(level=.Warning, args=args, sep=sep, location=location) + log(.Warning, ..args, sep=sep, location=location) } error :: proc(args: ..any, sep := " ", location := #caller_location) { - log(level=.Error, args=args, sep=sep, location=location) + log(.Error, ..args, sep=sep, location=location) } fatal :: proc(args: ..any, sep := " ", location := #caller_location) { - log(level=.Fatal, args=args, sep=sep, location=location) + log(.Fatal, ..args, sep=sep, location=location) } panic :: proc(args: ..any, location := #caller_location) -> ! { - log(level=.Fatal, args=args, location=location) + log(.Fatal, ..args, location=location) runtime.panic("log.panic", location) } panicf :: proc(fmt_str: string, args: ..any, location := #caller_location) -> ! { - logf(level=.Fatal, fmt_str=fmt_str, args=args, location=location) + logf(.Fatal, fmt_str, ..args, location=location) runtime.panic("log.panicf", location) } @@ -127,7 +127,7 @@ log :: proc(level: Level, args: ..any, sep := " ", location := #caller_location) if level < logger.lowest_level { return } - str := fmt.tprint(args=args, sep=sep) //NOTE(Hoej): While tprint isn't thread-safe, no logging is. + str := fmt.tprint(..args, sep=sep) //NOTE(Hoej): While tprint isn't thread-safe, no logging is. logger.procedure(logger.data, level, str, logger.options, location) } diff --git a/core/log/log_allocator.odin b/core/log/log_allocator.odin index f4d1841db..934f0d643 100644 --- a/core/log/log_allocator.odin +++ b/core/log/log_allocator.odin @@ -38,60 +38,60 @@ log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode, switch mode { case .Alloc: logf( - level=la.level, - fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc, size=%d, alignment=%d)", - args = {la.prefix, padding, size, alignment}, + la.level, + "%s%s>>> ALLOCATOR(mode=.Alloc, size=%d, alignment=%d)", + la.prefix, padding, size, alignment, location = location, ) case .Alloc_Non_Zeroed: logf( - level=la.level, - fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc_Non_Zeroed, size=%d, alignment=%d)", - args = {la.prefix, padding, size, alignment}, + la.level, + "%s%s>>> ALLOCATOR(mode=.Alloc_Non_Zeroed, size=%d, alignment=%d)", + la.prefix, padding, size, alignment, location = location, ) case .Free: if old_size != 0 { logf( - level=la.level, - fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p, size=%d)", - args = {la.prefix, padding, old_memory, old_size}, + la.level, + "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p, size=%d)", + la.prefix, padding, old_memory, old_size, location = location, ) } else { logf( - level=la.level, - fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p)", - args = {la.prefix, padding, old_memory}, + la.level, + "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p)", + la.prefix, padding, old_memory, location = location, ) } case .Free_All: logf( - level=la.level, - fmt_str = "%s%s<<< ALLOCATOR(mode=.Free_All)", - args = {la.prefix, padding}, + la.level, + "%s%s<<< ALLOCATOR(mode=.Free_All)", + la.prefix, padding, location = location, ) case .Resize: logf( - level=la.level, - fmt_str = "%s%s>>> ALLOCATOR(mode=.Resize, ptr=%p, old_size=%d, size=%d, alignment=%d)", - args = {la.prefix, padding, old_memory, old_size, size, alignment}, + la.level, + "%s%s>>> ALLOCATOR(mode=.Resize, ptr=%p, old_size=%d, size=%d, alignment=%d)", + la.prefix, padding, old_memory, old_size, size, alignment, location = location, ) case .Query_Features: logf( - level=la.level, - fmt_str = "%s%ALLOCATOR(mode=.Query_Features)", - args = {la.prefix, padding}, + la.level, + "%s%ALLOCATOR(mode=.Query_Features)", + la.prefix, padding, location = location, ) case .Query_Info: logf( - level=la.level, - fmt_str = "%s%ALLOCATOR(mode=.Query_Info)", - args = {la.prefix, padding}, + la.level, + "%s%ALLOCATOR(mode=.Query_Info)", + la.prefix, padding, location = location, ) } @@ -103,9 +103,9 @@ log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode, defer la.locked = false if err != nil { logf( - level=la.level, - fmt_str = "%s%ALLOCATOR ERROR=%v", - args = {la.prefix, padding, error}, + la.level, + "%s%ALLOCATOR ERROR=%v", + la.prefix, padding, error, location = location, ) } diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index 2b758dc35..d15ce0e98 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -429,7 +429,7 @@ internal_int_write_to_ascii_file :: proc(a: ^Int, filename: string, radix := i8( len = l, } - ok := os.write_entire_file(name=filename, data=data, truncate=true) + ok := os.write_entire_file(filename, data, truncate=true) return nil if ok else .Cannot_Write_File } diff --git a/core/mem/virtual/arena.odin b/core/mem/virtual/arena.odin index 027a6ce6e..e73a39660 100644 --- a/core/mem/virtual/arena.odin +++ b/core/mem/virtual/arena.odin @@ -120,7 +120,7 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l if arena.minimum_block_size == 0 { arena.minimum_block_size = DEFAULT_ARENA_STATIC_RESERVE_SIZE } - arena_init_static(arena=arena, reserved=arena.minimum_block_size, commit_size=DEFAULT_ARENA_STATIC_COMMIT_SIZE) or_return + arena_init_static(arena, reserved=arena.minimum_block_size, commit_size=DEFAULT_ARENA_STATIC_COMMIT_SIZE) or_return } fallthrough case .Buffer: diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index c5cb8cc07..9f2899bcc 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -112,7 +112,7 @@ remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_locatio // Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic. @builtin pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { - assert(len(array) > 0, "", loc) + assert(len(array) > 0, loc=loc) res = array[len(array)-1] (^Raw_Dynamic_Array)(array).len -= 1 return res @@ -136,7 +136,7 @@ pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check // Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic. @builtin pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { - assert(len(array) > 0, "", loc) + assert(len(array) > 0, loc=loc) res = array[0] if len(array) > 1 { copy(array[0:], array[1:]) @@ -424,7 +424,7 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> a := (^Raw_Dynamic_Array)(array) when size_of(E) != 0 { data := ([^]E)(a.data) - assert(condition=data != nil, loc=loc) + assert(data != nil, loc=loc) data[a.len] = arg } a.len += 1 @@ -459,7 +459,7 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) a := (^Raw_Dynamic_Array)(array) when size_of(E) != 0 { data := ([^]E)(a.data) - assert(condition=data != nil, loc=loc) + assert(data != nil, loc=loc) intrinsics.mem_copy(&data[a.len], raw_data(args), size_of(E) * arg_len) } a.len += arg_len @@ -472,7 +472,7 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) @builtin append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { args := transmute([]E)arg - return append_elems(array=array, args=args, loc=loc) + return append_elems(array, ..args, loc=loc) } @@ -481,7 +481,7 @@ append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #ca append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { n_arg: int for arg in args { - n_arg, err = append(array = array, args = transmute([]E)(arg), loc = loc) + n_arg, err = append(array, ..transmute([]E)(arg), loc=loc) n += n_arg if err != nil { return diff --git a/core/testing/runner_windows.odin b/core/testing/runner_windows.odin index 525eae685..17bcfce26 100644 --- a/core/testing/runner_windows.odin +++ b/core/testing/runner_windows.odin @@ -191,7 +191,7 @@ run_internal_test :: proc(t: ^T, it: Internal_Test) { global_exception_handler = win32.AddVectoredExceptionHandler(0, exception_handler_proc) context.assertion_failure_proc = proc(prefix, message: string, loc: runtime.Source_Code_Location) -> ! { - errorf(t=global_current_t, format="%s %s", args={prefix, message}, loc=loc) + errorf(global_current_t, "%s %s", prefix, message, loc=loc) intrinsics.trap() } diff --git a/core/testing/testing.odin b/core/testing/testing.odin index 37f9fe4d9..0ceb58e33 100644 --- a/core/testing/testing.odin +++ b/core/testing/testing.odin @@ -46,15 +46,15 @@ errorf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) { } fail :: proc(t: ^T, loc := #caller_location) { - error(t=t, args={"FAIL"}, loc=loc) + error(t, "FAIL", loc=loc) t.error_count += 1 } fail_now :: proc(t: ^T, msg := "", loc := #caller_location) { if msg != "" { - error(t=t, args={"FAIL:", msg}, loc=loc) + error(t, "FAIL:", msg, loc=loc) } else { - error(t=t, args={"FAIL"}, loc=loc) + error(t, "FAIL", loc=loc) } t.error_count += 1 if t._fail_now != nil { @@ -84,14 +84,14 @@ cleanup :: proc(t: ^T, procedure: proc(rawptr), user_data: rawptr) { expect :: proc(t: ^T, ok: bool, msg: string = "", loc := #caller_location) -> bool { if !ok { - error(t=t, args={msg}, loc=loc) + error(t, msg, loc=loc) } return ok } expect_value :: proc(t: ^T, value, expected: $T, loc := #caller_location) -> bool where intrinsics.type_is_comparable(T) { ok := value == expected if !ok { - errorf(t=t, format="expected %v, got %v", args={expected, value}, loc=loc) + errorf(t, "expected %v, got %v", expected, value, loc=loc) } return ok } diff --git a/core/text/table/table.odin b/core/text/table/table.odin index 2b60df98f..8d96cb26f 100644 --- a/core/text/table/table.odin +++ b/core/text/table/table.odin @@ -114,7 +114,7 @@ set_cell_alignment :: proc(tbl: ^Table, row, col: int, alignment: Cell_Alignment format :: proc(tbl: ^Table, _fmt: string, args: ..any, loc := #caller_location) -> string { context.allocator = tbl.format_allocator - return fmt.aprintf(fmt = _fmt, args = args) + return fmt.aprintf(_fmt, ..args) } header :: proc(tbl: ^Table, values: ..any, loc := #caller_location) { diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 6863a7768..7c98ca728 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -1175,13 +1175,13 @@ threading_example :: proc() { N :: 3 pool: thread.Pool - thread.pool_init(pool=&pool, allocator=context.allocator, thread_count=N) + thread.pool_init(&pool, allocator=context.allocator, thread_count=N) defer thread.pool_destroy(&pool) for i in 0..<30 { // be mindful of the allocator used for tasks. The allocator needs to be thread safe, or be owned by the task for exclusive use - thread.pool_add_task(pool=&pool, allocator=context.allocator, procedure=task_proc, data=nil, user_index=i) + thread.pool_add_task(&pool, allocator=context.allocator, procedure=task_proc, data=nil, user_index=i) } thread.pool_start(&pool) From 15e31e47fa73fdf9d71321c4a3021c37c063cca8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Jun 2023 01:24:57 +0100 Subject: [PATCH 065/144] Remove in order requirement for named parameters --- src/check_expr.cpp | 107 --------------------------------------------- 1 file changed, 107 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8731a0caf..9d87ffdea 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -117,8 +117,6 @@ gb_internal void check_or_return_split_types(CheckerContext *c, Operand *x, Stri gb_internal bool is_diverging_expr(Ast *expr); -gb_internal CallArgumentError check_order_of_call_arguments(CheckerContext *c, Type *proc_type, Ast *call, bool show_error); - gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_); enum LoadDirectiveResult { @@ -5413,10 +5411,6 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } }); - if (check_order_of_call_arguments(c, proc_type, call, show_error)) { - return CallArgumentError_OutOfOrderParameters; - } - isize positional_operand_count = positional_operands.count; if (variadic) { positional_operand_count = gb_min(positional_operands.count, pt->variadic_index); @@ -5839,99 +5833,6 @@ gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Sco return true; } -gb_internal CallArgumentError check_order_of_call_arguments(CheckerContext *c, Type *proc_type, Ast *call, bool show_error) { - CallArgumentError err = CallArgumentError_None; - - if (proc_type == nullptr || !is_type_proc(proc_type)) { - // ignore for the time being - return err; - } - - ast_node(ce, CallExpr, call); - if (!is_call_expr_field_value(ce)) { - return err; - } - - TypeProc *pt = &base_type(proc_type)->Proc; - isize param_count = pt->param_count; - - TEMPORARY_ALLOCATOR_GUARD(); - - auto arg_order_indices = slice_make(temporary_allocator(), ce->args.count); - auto visited = slice_make(temporary_allocator(), param_count); - - for_array(i, ce->args) { - Ast *arg = ce->args[i]; - ast_node(fv, FieldValue, arg); - - Ast *field = fv->field; - String name = {}; - if (field != nullptr && field->kind == Ast_Ident) { - name = field->Ident.token.string; - } else { - err = CallArgumentError_InvalidFieldValue; - if (show_error) { - gbString s = expr_to_string(arg); - error(arg, "Invalid parameter name '%s' in procedure call", s); - gb_string_free(s); - } - } - - isize index = lookup_procedure_parameter(pt, name); - if (index < 0) { - err = CallArgumentError_InvalidFieldValue; - if (show_error) { - error(arg, "No parameter named '%.*s' for this procedure type", LIT(name)); - } - } - if (visited[index]) { - err = CallArgumentError_DuplicateParameter; - if (show_error) { - error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name)); - } - } - arg_order_indices[i] = index; - } - - if (err) { - return err; - } - - for (isize i = 0; i < arg_order_indices.count-1; i++) { - isize a = arg_order_indices[i+0]; - isize b = arg_order_indices[i+1]; - GB_ASSERT(a != b); - if (a > b) { - err = CallArgumentError_OutOfOrderParameters; - if (show_error) { - Ast *arg_a = ce->args[i+0]; - Ast *arg_b = ce->args[i+1]; - - isize curr_a_order = a; - for (isize j = i; j >= 0; j--) { - isize j_order = arg_order_indices[j]; - if (b < j_order && j_order < curr_a_order) { - curr_a_order = j_order; - arg_a = ce->args[j]; - } - } - - - ast_node(fv_a, FieldValue, arg_a); - ast_node(fv_b, FieldValue, arg_b); - - gbString s_a = expr_to_string(fv_a->field); - gbString s_b = expr_to_string(fv_b->field); - error(arg_b, "Parameter names out of order, expected '%s' to be called before '%s'", s_b, s_a); - gb_string_free(s_b); - gb_string_free(s_a); - } - } - } - - return err; -} - gb_internal bool check_named_arguments(CheckerContext *c, Type *type, Slice const &named_args, Array *named_operands, bool show_error) { bool success = true; @@ -5974,12 +5875,6 @@ gb_internal bool check_named_arguments(CheckerContext *c, Type *type, Sliceparams->Tuple.variables[param_index]; if (!is_type_polymorphic(e->type)) { type_hint = e->type; @@ -6498,8 +6393,6 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex bool any_failure = false; - // bool vari_expand = (ce->ellipsis.pos.line != 0); - // Split positional and named args into separate arrays/slices Slice positional_args = {}; Slice named_args = {}; From 8a890fd3d3cc1d11417d1da37bced9ba5715e612 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Jun 2023 01:32:35 +0100 Subject: [PATCH 066/144] Remove `new_and_improved` in the name --- src/check_expr.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 9d87ffdea..99017f434 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5893,7 +5893,7 @@ gb_internal bool check_named_arguments(CheckerContext *c, Type *type, Slice const &positional_operands, Array const &named_operands, CallArgumentErrorMode show_error_mode, @@ -5963,7 +5963,7 @@ gb_internal bool check_call_arguments_new_and_improved_single(CheckerContext *c, } -gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(CheckerContext *c, Operand *operand, Ast *call) { +gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, Operand *operand, Ast *call) { ast_node(ce, CallExpr, call); GB_ASSERT(ce->split_args != nullptr); @@ -6063,7 +6063,7 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); if (check_named_arguments(c, e->type, named_args, &named_operands, true)) { - check_call_arguments_new_and_improved_single(c, call, operand, + check_call_arguments_single(c, call, operand, e, e->type, positional_operands, named_operands, CallArgumentErrorMode::ShowErrors, @@ -6187,7 +6187,7 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch ctx.allow_polymorphic_types = is_type_polymorphic(pt); ctx.hide_polymorphic_errors = true; - bool is_a_candidate = check_call_arguments_new_and_improved_single(&ctx, call, operand, + bool is_a_candidate = check_call_arguments_single(&ctx, call, operand, p, pt, positional_operands, named_operands, CallArgumentErrorMode::NoErrors, @@ -6363,7 +6363,7 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch Array named_operands = {}; - check_call_arguments_new_and_improved_single(c, call, operand, + check_call_arguments_single(c, call, operand, e, e->type, positional_operands, named_operands, CallArgumentErrorMode::ShowErrors, @@ -6375,7 +6375,7 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch } -gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContext *c, Operand *operand, Ast *call) { +gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Ast *call) { Type *proc_type = nullptr; CallArgumentData data = {}; @@ -6418,7 +6418,7 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex } if (operand->mode == Addressing_ProcGroup) { - return check_call_arguments_new_and_improved_proc_group(c, operand, call); + return check_call_arguments_proc_group(c, operand, call); } auto positional_operands = array_make(heap_allocator(), 0, positional_args.count); @@ -6473,7 +6473,7 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContex } if (!any_failure) { - check_call_arguments_new_and_improved_single(c, call, operand, + check_call_arguments_single(c, call, operand, nullptr, nullptr, positional_operands, named_operands, CallArgumentErrorMode::ShowErrors, @@ -7045,7 +7045,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } - CallArgumentData data = check_call_arguments_new_and_improved(c, operand, call); + CallArgumentData data = check_call_arguments(c, operand, call); Type *result_type = data.result_type; gb_zero_item(operand); operand->expr = call; From 735181dc0ec11bdd7293bae01cc07450e5dd788b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Jun 2023 01:33:37 +0100 Subject: [PATCH 067/144] Remove unnecessary indent --- src/check_type.cpp | 142 ++++++++++++++++++++++----------------------- 1 file changed, 70 insertions(+), 72 deletions(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index f241f35ee..a68f83ba9 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1665,81 +1665,79 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para if (operands != nullptr && variables.count < operands->count) { Operand op = (*operands)[variables.count]; - { - if (op.expr == nullptr) { - // NOTE(bill): 2019-03-30 - // This is just to add the error message to determine_type_from_polymorphic which - // depends on valid position information - op.expr = _params; - op.mode = Addressing_Invalid; - op.type = t_invalid; - } - if (is_type_polymorphic_type) { - type = determine_type_from_polymorphic(ctx, type, op); - if (type == t_invalid) { - success = false; - } else if (!ctx->no_polymorphic_errors) { - // NOTE(bill): The type should be determined now and thus, no need to determine the type any more - is_type_polymorphic_type = false; - Entity *proc_entity = entity_from_expr(op.expr); - if ((proc_entity != nullptr) && (op.value.kind == ExactValue_Procedure)) { - if (is_type_polymorphic(proc_entity->type, false)) { - error(op.expr, "Cannot determine complete type of partial polymorphic procedure"); - } - } - } - } - if (is_poly_name) { - bool valid = false; - if (is_type_proc(op.type)) { - Ast *expr = unparen_expr(op.expr); - Entity *proc_entity = entity_from_expr(expr); - if (proc_entity) { - poly_const = exact_value_procedure(proc_entity->identifier.load() ? proc_entity->identifier.load() : op.expr); - valid = true; - } else if (expr->kind == Ast_ProcLit) { - poly_const = exact_value_procedure(expr); - valid = true; - } - } - if (!valid) { - if (op.mode == Addressing_Constant) { - poly_const = op.value; - } else { - error(op.expr, "Expected a constant value for this polymorphic name parameter, got %s", expr_to_string(op.expr)); - success = false; - } - } - } - if (type != t_invalid && !check_is_assignable_to(ctx, &op, type)) { - bool ok = true; - if (p->flags&FieldFlag_any_int) { - if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) { - ok = false; - } else if (!check_is_castable_to(ctx, &op, type)) { - ok = false; - } - } - if (!ok) { - success = false; - #if 0 - gbString got = type_to_string(op.type); - gbString expected = type_to_string(type); - error(op.expr, "Cannot assigned type to parameter, got type '%s', expected '%s'", got, expected); - gb_string_free(expected); - gb_string_free(got); - #endif - } - } - - if (is_type_untyped(default_type(type))) { - gbString str = type_to_string(type); - error(op.expr, "Cannot determine type from the parameter, got '%s'", str); - gb_string_free(str); + if (op.expr == nullptr) { + // NOTE(bill): 2019-03-30 + // This is just to add the error message to determine_type_from_polymorphic which + // depends on valid position information + op.expr = _params; + op.mode = Addressing_Invalid; + op.type = t_invalid; + } + if (is_type_polymorphic_type) { + type = determine_type_from_polymorphic(ctx, type, op); + if (type == t_invalid) { success = false; - type = t_invalid; + } else if (!ctx->no_polymorphic_errors) { + // NOTE(bill): The type should be determined now and thus, no need to determine the type any more + is_type_polymorphic_type = false; + Entity *proc_entity = entity_from_expr(op.expr); + if ((proc_entity != nullptr) && (op.value.kind == ExactValue_Procedure)) { + if (is_type_polymorphic(proc_entity->type, false)) { + error(op.expr, "Cannot determine complete type of partial polymorphic procedure"); + } + } } } + if (is_poly_name) { + bool valid = false; + if (is_type_proc(op.type)) { + Ast *expr = unparen_expr(op.expr); + Entity *proc_entity = entity_from_expr(expr); + if (proc_entity) { + poly_const = exact_value_procedure(proc_entity->identifier.load() ? proc_entity->identifier.load() : op.expr); + valid = true; + } else if (expr->kind == Ast_ProcLit) { + poly_const = exact_value_procedure(expr); + valid = true; + } + } + if (!valid) { + if (op.mode == Addressing_Constant) { + poly_const = op.value; + } else { + error(op.expr, "Expected a constant value for this polymorphic name parameter, got %s", expr_to_string(op.expr)); + success = false; + } + } + } + if (type != t_invalid && !check_is_assignable_to(ctx, &op, type)) { + bool ok = true; + if (p->flags&FieldFlag_any_int) { + if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) { + ok = false; + } else if (!check_is_castable_to(ctx, &op, type)) { + ok = false; + } + } + if (!ok) { + success = false; + #if 0 + gbString got = type_to_string(op.type); + gbString expected = type_to_string(type); + error(op.expr, "Cannot assigned type to parameter, got type '%s', expected '%s'", got, expected); + gb_string_free(expected); + gb_string_free(got); + #endif + } + } + + if (is_type_untyped(default_type(type))) { + gbString str = type_to_string(type); + error(op.expr, "Cannot determine type from the parameter, got '%s'", str); + gb_string_free(str); + success = false; + type = t_invalid; + } } if (p->flags&FieldFlag_no_alias) { From 43ba2c6226dc71754c7aa74ed8ecb150231a9624 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Jun 2023 12:10:07 +0100 Subject: [PATCH 068/144] Fix constant parameter passing --- src/llvm_backend_proc.cpp | 44 ++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index fc58d584e..0beaf376b 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3168,10 +3168,13 @@ gb_internal lbValue lb_build_call_expr_internal_with_arg_split_args(lbProcedure if (e->kind == Entity_TypeName) { array_add(&args, lb_const_nil(p->module, e->type)); continue; - } else if (e->kind != Entity_Variable) { + } else if (e->kind == Entity_Constant) { + array_add(&args, lb_const_value(p->module, e->type, e->Constant.value)); continue; } + GB_ASSERT(e->kind == Entity_Variable); + if (pt->variadic && pt->variadic_index == i) { lbValue variadic_args = lb_const_nil(p->module, e->type); auto variadic = slice(split_args->positional, pt->variadic_index, split_args->positional.count); @@ -3238,23 +3241,30 @@ gb_internal lbValue lb_build_call_expr_internal_with_arg_split_args(lbProcedure TokenPos pos = ast_token(ce->proc).pos; - for_array(i, args) { - Entity *e = pt->params->Tuple.variables[i]; - lbValue arg = args[i]; - if (arg.value == nullptr) { - switch (e->kind) { - case Entity_TypeName: - args[i] = lb_const_nil(p->module, e->type); - break; - case Entity_Variable: - args[i] = lb_handle_param_value(p, e->type, e->Variable.param_value, pos); - break; - case Entity_Constant: - args[i] = lb_handle_param_value(p, e->type, e->Constant.param_value, pos); - break; + + if (pt->params != nullptr) { + for_array(arg_index, pt->params->Tuple.variables) { + Entity *e = pt->params->Tuple.variables[arg_index]; + + lbValue arg = args[arg_index]; + if (arg.value == nullptr) { + switch (e->kind) { + case Entity_TypeName: + args[arg_index] = lb_const_nil(p->module, e->type); + break; + case Entity_Variable: + args[arg_index] = lb_handle_param_value(p, e->type, e->Variable.param_value, pos); + break; + + case Entity_Constant: + args[arg_index] = lb_const_value(p->module, e->type, e->Constant.value); + break; + default: + GB_PANIC("Unknown entity kind %.*s\n", LIT(entity_strings[e->kind])); + } + } else { + args[arg_index] = lb_emit_conv(p, arg, e->type); } - } else { - args[i] = lb_emit_conv(p, arg, e->type); } } From 180003035659d56a898ddf7de59e8630493c2147 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Jun 2023 14:01:46 +0100 Subject: [PATCH 069/144] Correct deferred procedures --- src/llvm_backend_proc.cpp | 256 +++++++++++++++++++++----------------- 1 file changed, 139 insertions(+), 117 deletions(-) diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 0beaf376b..48b4b9330 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1160,7 +1160,13 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array c } - Entity **found = map_get(&p->module->procedure_values, value.value); + LLVMValueRef the_proc_value = value.value; + + if (LLVMIsAConstantExpr(the_proc_value)) { + // NOTE(bill): it's a bit cast + the_proc_value = LLVMGetOperand(the_proc_value, 0); + } + Entity **found = map_get(&p->module->procedure_values, the_proc_value); if (found != nullptr) { Entity *e = *found; if (e != nullptr && entity_has_deferred_procedure(e)) { @@ -3156,121 +3162,6 @@ gb_internal void lb_add_values_to_array(lbProcedure *p, Array *args, lb array_add(args, value); } } -gb_internal lbValue lb_build_call_expr_internal_with_arg_split_args(lbProcedure *p, lbValue value, TypeProc *pt, Ast *call, AstSplitArgs *split_args) { - ast_node(ce, CallExpr, call); - - auto args = array_make(permanent_allocator(), 0, pt->param_count); - - bool vari_expand = (ce->ellipsis.pos.line != 0); - - for_array(i, split_args->positional) { - Entity *e = pt->params->Tuple.variables[i]; - if (e->kind == Entity_TypeName) { - array_add(&args, lb_const_nil(p->module, e->type)); - continue; - } else if (e->kind == Entity_Constant) { - array_add(&args, lb_const_value(p->module, e->type, e->Constant.value)); - continue; - } - - GB_ASSERT(e->kind == Entity_Variable); - - if (pt->variadic && pt->variadic_index == i) { - lbValue variadic_args = lb_const_nil(p->module, e->type); - auto variadic = slice(split_args->positional, pt->variadic_index, split_args->positional.count); - if (variadic.count != 0) { - // variadic call argument generation - Type *slice_type = e->type; - GB_ASSERT(slice_type->kind == Type_Slice); - if (vari_expand) { - GB_ASSERT(variadic.count == 1); - variadic_args = lb_build_expr(p, variadic[0]); - variadic_args = lb_emit_conv(p, variadic_args, slice_type); - } else { - Type *elem_type = slice_type->Slice.elem; - - auto var_args = array_make(heap_allocator(), 0, variadic.count); - defer (array_free(&var_args)); - for (Ast *var_arg : variadic) { - lbValue v = lb_build_expr(p, var_arg); - lb_add_values_to_array(p, &var_args, v); - } - isize slice_len = var_args.count; - if (slice_len > 0) { - lbAddr slice = lb_add_local_generated(p, slice_type, true); - lbAddr base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true); - - for (isize i = 0; i < var_args.count; i++) { - lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)i); - lbValue var_arg = var_args[i]; - var_arg = lb_emit_conv(p, var_arg, elem_type); - lb_emit_store(p, addr, var_arg); - } - - lbValue base_elem = lb_emit_array_epi(p, base_array.addr, 0); - lbValue len = lb_const_int(p->module, t_int, slice_len); - lb_fill_slice(p, slice, base_elem, len); - - variadic_args = lb_addr_load(p, slice); - } - } - } - array_add(&args, variadic_args); - - break; - } else { - lbValue value = lb_build_expr(p, split_args->positional[i]); - lb_add_values_to_array(p, &args, value); - } - } - - array_resize(&args, pt->param_count); - - for (Ast *arg : split_args->named) { - ast_node(fv, FieldValue, arg); - GB_ASSERT(fv->field->kind == Ast_Ident); - String name = fv->field->Ident.token.string; - gb_unused(name); - isize param_index = lookup_procedure_parameter(pt, name); - GB_ASSERT(param_index >= 0); - - lbValue value = lb_build_expr(p, fv->value); - GB_ASSERT(!is_type_tuple(value.type)); - args[param_index] = value; - } - - TokenPos pos = ast_token(ce->proc).pos; - - - if (pt->params != nullptr) { - for_array(arg_index, pt->params->Tuple.variables) { - Entity *e = pt->params->Tuple.variables[arg_index]; - - lbValue arg = args[arg_index]; - if (arg.value == nullptr) { - switch (e->kind) { - case Entity_TypeName: - args[arg_index] = lb_const_nil(p->module, e->type); - break; - case Entity_Variable: - args[arg_index] = lb_handle_param_value(p, e->type, e->Variable.param_value, pos); - break; - - case Entity_Constant: - args[arg_index] = lb_const_value(p->module, e->type, e->Constant.value); - break; - default: - GB_PANIC("Unknown entity kind %.*s\n", LIT(entity_strings[e->kind])); - } - } else { - args[arg_index] = lb_emit_conv(p, arg, e->type); - } - } - } - - - return lb_emit_call(p, value, args, ce->inlining); -} gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { lbModule *m = p->module; @@ -3347,6 +3238,137 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { TypeProc *pt = &proc_type_->Proc; GB_ASSERT(ce->split_args != nullptr); - return lb_build_call_expr_internal_with_arg_split_args(p, value, pt, expr, ce->split_args); + + auto args = array_make(permanent_allocator(), 0, pt->param_count); + + bool vari_expand = (ce->ellipsis.pos.line != 0); + bool is_c_vararg = pt->c_vararg; + + for_array(i, ce->split_args->positional) { + Entity *e = pt->params->Tuple.variables[i]; + if (e->kind == Entity_TypeName) { + array_add(&args, lb_const_nil(p->module, e->type)); + continue; + } else if (e->kind == Entity_Constant) { + array_add(&args, lb_const_value(p->module, e->type, e->Constant.value)); + continue; + } + + GB_ASSERT(e->kind == Entity_Variable); + + if (pt->variadic && pt->variadic_index == i) { + lbValue variadic_args = lb_const_nil(p->module, e->type); + auto variadic = slice(ce->split_args->positional, pt->variadic_index, ce->split_args->positional.count); + if (variadic.count != 0) { + // variadic call argument generation + Type *slice_type = e->type; + GB_ASSERT(slice_type->kind == Type_Slice); + + if (is_c_vararg) { + GB_PANIC("TODO #c_vararg"); + GB_ASSERT(!vari_expand); + + Type *elem_type = slice_type->Slice.elem; + + for (Ast *var_arg : variadic) { + lbValue arg = lb_build_expr(p, var_arg); + if (!is_type_any(elem_type)) { + array_add(&args, lb_emit_conv(p, arg, elem_type)); + } else { + array_add(&args, lb_emit_conv(p, arg, default_type(arg.type))); + } + } + break; + } else if (vari_expand) { + GB_ASSERT(variadic.count == 1); + variadic_args = lb_build_expr(p, variadic[0]); + variadic_args = lb_emit_conv(p, variadic_args, slice_type); + } else { + Type *elem_type = slice_type->Slice.elem; + + auto var_args = array_make(heap_allocator(), 0, variadic.count); + defer (array_free(&var_args)); + for (Ast *var_arg : variadic) { + lbValue v = lb_build_expr(p, var_arg); + lb_add_values_to_array(p, &var_args, v); + } + isize slice_len = var_args.count; + if (slice_len > 0) { + lbAddr slice = lb_add_local_generated(p, slice_type, true); + lbAddr base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true); + + for (isize i = 0; i < var_args.count; i++) { + lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)i); + lbValue var_arg = var_args[i]; + var_arg = lb_emit_conv(p, var_arg, elem_type); + lb_emit_store(p, addr, var_arg); + } + + lbValue base_elem = lb_emit_array_epi(p, base_array.addr, 0); + lbValue len = lb_const_int(p->module, t_int, slice_len); + lb_fill_slice(p, slice, base_elem, len); + + variadic_args = lb_addr_load(p, slice); + } + } + } + array_add(&args, variadic_args); + + break; + } else { + lbValue value = lb_build_expr(p, ce->split_args->positional[i]); + lb_add_values_to_array(p, &args, value); + } + } + + array_resize(&args, pt->param_count); + + for (Ast *arg : ce->split_args->named) { + ast_node(fv, FieldValue, arg); + GB_ASSERT(fv->field->kind == Ast_Ident); + String name = fv->field->Ident.token.string; + gb_unused(name); + isize param_index = lookup_procedure_parameter(pt, name); + GB_ASSERT(param_index >= 0); + + lbValue value = lb_build_expr(p, fv->value); + GB_ASSERT(!is_type_tuple(value.type)); + args[param_index] = value; + } + + TokenPos pos = ast_token(ce->proc).pos; + + + if (pt->params != nullptr) { + GB_ASSERT(args.count >= pt->params->Tuple.variables.count); + for_array(arg_index, pt->params->Tuple.variables) { + Entity *e = pt->params->Tuple.variables[arg_index]; + + lbValue arg = args[arg_index]; + if (arg.value == nullptr) { + switch (e->kind) { + case Entity_TypeName: + args[arg_index] = lb_const_nil(p->module, e->type); + break; + case Entity_Variable: + args[arg_index] = lb_handle_param_value(p, e->type, e->Variable.param_value, pos); + break; + + case Entity_Constant: + args[arg_index] = lb_const_value(p->module, e->type, e->Constant.value); + break; + default: + GB_PANIC("Unknown entity kind %.*s\n", LIT(entity_strings[e->kind])); + } + } else { + args[arg_index] = lb_emit_conv(p, arg, e->type); + } + } + } + + + isize final_count = is_c_vararg ? args.count : pt->param_count; + auto call_args = array_slice(args, 0, final_count); + return lb_emit_call(p, value, call_args, ce->inlining); } From c9fb078c0f1e09edcc023dce30dd0ca04f20cc24 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Jun 2023 14:07:14 +0100 Subject: [PATCH 070/144] Handle `#c_vararg` --- src/llvm_backend_proc.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 48b4b9330..7cf82b208 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3265,17 +3265,16 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { GB_ASSERT(slice_type->kind == Type_Slice); if (is_c_vararg) { - GB_PANIC("TODO #c_vararg"); GB_ASSERT(!vari_expand); Type *elem_type = slice_type->Slice.elem; for (Ast *var_arg : variadic) { lbValue arg = lb_build_expr(p, var_arg); - if (!is_type_any(elem_type)) { - array_add(&args, lb_emit_conv(p, arg, elem_type)); - } else { + if (is_type_any(elem_type)) { array_add(&args, lb_emit_conv(p, arg, default_type(arg.type))); + } else { + array_add(&args, lb_emit_conv(p, arg, elem_type)); } } break; @@ -3321,7 +3320,9 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { } } - array_resize(&args, pt->param_count); + if (!is_c_vararg) { + array_resize(&args, pt->param_count); + } for (Ast *arg : ce->split_args->named) { ast_node(fv, FieldValue, arg); @@ -3343,6 +3344,9 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { GB_ASSERT(args.count >= pt->params->Tuple.variables.count); for_array(arg_index, pt->params->Tuple.variables) { Entity *e = pt->params->Tuple.variables[arg_index]; + if (pt->variadic && arg_index == pt->variadic_index) { + continue; + } lbValue arg = args[arg_index]; if (arg.value == nullptr) { @@ -3366,7 +3370,6 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { } } - isize final_count = is_c_vararg ? args.count : pt->param_count; auto call_args = array_slice(args, 0, final_count); return lb_emit_call(p, value, call_args, ce->inlining); From ea76e09ea741f0bf090d967cf3aaded59079d001 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Jun 2023 14:30:39 +0100 Subject: [PATCH 071/144] Fix empty varargs --- src/llvm_backend_proc.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 7cf82b208..47137c540 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3345,6 +3345,9 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { for_array(arg_index, pt->params->Tuple.variables) { Entity *e = pt->params->Tuple.variables[arg_index]; if (pt->variadic && arg_index == pt->variadic_index) { + if (!is_c_vararg && args[arg_index].value == 0) { + args[arg_index] = lb_const_nil(p->module, e->type); + } continue; } From c48057081e451c81524c7727ec3ccf434a45726f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Jun 2023 14:39:23 +0100 Subject: [PATCH 072/144] Fix nullptr entity case --- src/check_expr.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 99017f434..83e44b39f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5908,11 +5908,13 @@ gb_internal bool check_call_arguments_single(CheckerContext *c, Ast *call, Opera } if (e == nullptr) { - GB_ASSERT(proc_type == nullptr); e = entity_of_node(ident); - proc_type = e->type; + if (e != nullptr) { + proc_type = e->type; + } } + GB_ASSERT(proc_type != nullptr); proc_type = base_type(proc_type); GB_ASSERT(proc_type->kind == Type_Proc); @@ -5922,12 +5924,10 @@ gb_internal bool check_call_arguments_single(CheckerContext *c, Ast *call, Opera } Entity *entity_to_use = data->gen_entity != nullptr ? data->gen_entity : e; - if (!return_on_failure) { + if (!return_on_failure && entity_to_use != nullptr) { add_entity_use(c, ident, entity_to_use); - if (entity_to_use != nullptr) { - update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); - add_type_and_value(c, operand->expr, operand->mode, entity_to_use->type, operand->value); - } + update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); + add_type_and_value(c, operand->expr, operand->mode, entity_to_use->type, operand->value); } if (data->gen_entity != nullptr) { @@ -6474,7 +6474,7 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op if (!any_failure) { check_call_arguments_single(c, call, operand, - nullptr, nullptr, + nullptr, proc_type, positional_operands, named_operands, CallArgumentErrorMode::ShowErrors, &data); From e3b43b6e1c5ddb459a7bfda46daa232c874c5771 Mon Sep 17 00:00:00 2001 From: Rehkitzdev Date: Thu, 22 Jun 2023 13:35:22 +0200 Subject: [PATCH 073/144] fixed dynlib wasm stub --- core/dynlib/lib_js.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/dynlib/lib_js.odin b/core/dynlib/lib_js.odin index 37dab8758..ace1b0939 100644 --- a/core/dynlib/lib_js.odin +++ b/core/dynlib/lib_js.odin @@ -3,13 +3,13 @@ package dynlib _load_library :: proc(path: string, global_symbols := false) -> (Library, bool) { - return + return nil, false } _unload_library :: proc(library: Library) -> bool { - return + return false } _symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) { - return + return nil, false } From 5a6d5374d780e726be82f3576b4f647d9096d012 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Jun 2023 14:30:02 +0100 Subject: [PATCH 074/144] Update WebGL procedures to `contextless` calling convention --- vendor/wasm/WebGL/webgl.odin | 67 +++++++++++++++++------------------ vendor/wasm/WebGL/webgl2.odin | 42 +++++++++++----------- 2 files changed, 53 insertions(+), 56 deletions(-) diff --git a/vendor/wasm/WebGL/webgl.odin b/vendor/wasm/WebGL/webgl.odin index 4fa6c4c1d..7337e2a11 100644 --- a/vendor/wasm/WebGL/webgl.odin +++ b/vendor/wasm/WebGL/webgl.odin @@ -27,7 +27,7 @@ ContextAttributes :: distinct bit_set[ContextAttribute; u32] DEFAULT_CONTEXT_ATTRIBUTES :: ContextAttributes{} -@(default_calling_convention="c") +@(default_calling_convention="contextless") foreign webgl { // CreateCurrentContextById must be called before `GetCurrentContextAttributes` if the user wants to // set specific attributes, otherwise the default attributes will be set for the WebGL context @@ -168,61 +168,59 @@ foreign webgl { Viewport :: proc(x, y, w, h: i32) --- } -Uniform1fv :: proc "c" (location: i32, v: f32) { Uniform1f(location, v) } -Uniform2fv :: proc "c" (location: i32, v: glm.vec2) { Uniform2f(location, v.x, v.y) } -Uniform3fv :: proc "c" (location: i32, v: glm.vec3) { Uniform3f(location, v.x, v.y, v.z) } -Uniform4fv :: proc "c" (location: i32, v: glm.vec4) { Uniform4f(location, v.x, v.y, v.z, v.w) } -Uniform1iv :: proc "c" (location: i32, v: i32) { Uniform1i(location, v) } -Uniform2iv :: proc "c" (location: i32, v: glm.ivec2) { Uniform2i(location, v.x, v.y) } -Uniform3iv :: proc "c" (location: i32, v: glm.ivec3) { Uniform3i(location, v.x, v.y, v.z) } -Uniform4iv :: proc "c" (location: i32, v: glm.ivec4) { Uniform4i(location, v.x, v.y, v.z, v.w) } +Uniform1fv :: proc "contextless" (location: i32, v: f32) { Uniform1f(location, v) } +Uniform2fv :: proc "contextless" (location: i32, v: glm.vec2) { Uniform2f(location, v.x, v.y) } +Uniform3fv :: proc "contextless" (location: i32, v: glm.vec3) { Uniform3f(location, v.x, v.y, v.z) } +Uniform4fv :: proc "contextless" (location: i32, v: glm.vec4) { Uniform4f(location, v.x, v.y, v.z, v.w) } +Uniform1iv :: proc "contextless" (location: i32, v: i32) { Uniform1i(location, v) } +Uniform2iv :: proc "contextless" (location: i32, v: glm.ivec2) { Uniform2i(location, v.x, v.y) } +Uniform3iv :: proc "contextless" (location: i32, v: glm.ivec3) { Uniform3i(location, v.x, v.y, v.z) } +Uniform4iv :: proc "contextless" (location: i32, v: glm.ivec4) { Uniform4i(location, v.x, v.y, v.z, v.w) } -VertexAttrib1fv :: proc "c" (index: i32, v: f32) { VertexAttrib1f(index, v) } -VertexAttrib2fv :: proc "c" (index: i32, v: glm.vec2){ VertexAttrib2f(index, v.x, v.y) } -VertexAttrib3fv :: proc "c" (index: i32, v: glm.vec3){ VertexAttrib3f(index, v.x, v.y, v.z) } -VertexAttrib4fv :: proc "c" (index: i32, v: glm.vec4){ VertexAttrib4f(index, v.x, v.y, v.z, v.w) } +VertexAttrib1fv :: proc "contextless" (index: i32, v: f32) { VertexAttrib1f(index, v) } +VertexAttrib2fv :: proc "contextless" (index: i32, v: glm.vec2){ VertexAttrib2f(index, v.x, v.y) } +VertexAttrib3fv :: proc "contextless" (index: i32, v: glm.vec3){ VertexAttrib3f(index, v.x, v.y, v.z) } +VertexAttrib4fv :: proc "contextless" (index: i32, v: glm.vec4){ VertexAttrib4f(index, v.x, v.y, v.z, v.w) } -UniformMatrix2fv :: proc "c" (location: i32, m: glm.mat2) { +UniformMatrix2fv :: proc "contextless" (location: i32, m: glm.mat2) { foreign webgl { @(link_name="UniformMatrix2fv") - _UniformMatrix2fv :: proc "c" (location: i32, value: [^]f32) --- + _UniformMatrix2fv :: proc "contextless" (location: i32, value: [^]f32) --- } value := transmute([2*2]f32)m _UniformMatrix2fv(location, &value[0]) } -UniformMatrix3fv :: proc "c" (location: i32, m: glm.mat3) { +UniformMatrix3fv :: proc "contextless" (location: i32, m: glm.mat3) { foreign webgl { @(link_name="UniformMatrix3fv") - _UniformMatrix3fv :: proc "c" (location: i32, value: [^]f32) --- + _UniformMatrix3fv :: proc "contextless" (location: i32, value: [^]f32) --- } value := transmute([3*3]f32)m _UniformMatrix3fv(location, &value[0]) } -UniformMatrix4fv :: proc "c" (location: i32, m: glm.mat4) { +UniformMatrix4fv :: proc "contextless" (location: i32, m: glm.mat4) { foreign webgl { @(link_name="UniformMatrix4fv") - _UniformMatrix4fv :: proc "c" (location: i32, value: [^]f32) --- + _UniformMatrix4fv :: proc "contextless" (location: i32, value: [^]f32) --- } value := transmute([4*4]f32)m _UniformMatrix4fv(location, &value[0]) } -GetShaderiv :: proc "c" (shader: Shader, pname: Enum) -> (p: i32) { - @(default_calling_convention="c") +GetShaderiv :: proc "contextless" (shader: Shader, pname: Enum) -> (p: i32) { foreign webgl { @(link_name="GetShaderiv") - _GetShaderiv :: proc "c" (shader: Shader, pname: Enum, p: ^i32) --- + _GetShaderiv :: proc "contextless" (shader: Shader, pname: Enum, p: ^i32) --- } _GetShaderiv(shader, pname, &p) return } -GetProgramInfoLog :: proc "c" (program: Program, buf: []byte) -> string { - @(default_calling_convention="c") +GetProgramInfoLog :: proc "contextless" (program: Program, buf: []byte) -> string { foreign webgl { @(link_name="GetProgramInfoLog") - _GetProgramInfoLog :: proc "c" (program: Program, buf: []byte, length: ^int) --- + _GetProgramInfoLog :: proc "contextless" (program: Program, buf: []byte, length: ^int) --- } length: int @@ -230,11 +228,10 @@ GetProgramInfoLog :: proc "c" (program: Program, buf: []byte) -> string { return string(buf[:length]) } -GetShaderInfoLog :: proc "c" (shader: Shader, buf: []byte) -> string { - @(default_calling_convention="c") +GetShaderInfoLog :: proc "contextless" (shader: Shader, buf: []byte) -> string { foreign webgl { @(link_name="GetShaderInfoLog") - _GetShaderInfoLog :: proc "c" (shader: Shader, buf: []byte, length: ^int) --- + _GetShaderInfoLog :: proc "contextless" (shader: Shader, buf: []byte, length: ^int) --- } length: int @@ -244,27 +241,27 @@ GetShaderInfoLog :: proc "c" (shader: Shader, buf: []byte) -> string { -BufferDataSlice :: proc "c" (target: Enum, slice: $S/[]$E, usage: Enum) { +BufferDataSlice :: proc "contextless" (target: Enum, slice: $S/[]$E, usage: Enum) { BufferData(target, len(slice)*size_of(E), raw_data(slice), usage) } -BufferSubDataSlice :: proc "c" (target: Enum, offset: uintptr, slice: $S/[]$E) { +BufferSubDataSlice :: proc "contextless" (target: Enum, offset: uintptr, slice: $S/[]$E) { BufferSubData(target, offset, len(slice)*size_of(E), raw_data(slice), usage) } -CompressedTexImage2DSlice :: proc "c" (target: Enum, level: i32, internalformat: Enum, width, height: i32, border: i32, slice: $S/[]$E) { +CompressedTexImage2DSlice :: proc "contextless" (target: Enum, level: i32, internalformat: Enum, width, height: i32, border: i32, slice: $S/[]$E) { CompressedTexImage2DSlice(target, level, internalformat, width, height, border, len(slice)*size_of(E), raw_data(slice)) } -CompressedTexSubImage2DSlice :: proc "c" (target: Enum, level: i32, xoffset, yoffset, width, height: i32, format: Enum, slice: $S/[]$E) { +CompressedTexSubImage2DSlice :: proc "contextless" (target: Enum, level: i32, xoffset, yoffset, width, height: i32, format: Enum, slice: $S/[]$E) { CompressedTexSubImage2DSlice(target, level, level, xoffset, yoffset, width, height, format, len(slice)*size_of(E), raw_data(slice)) } -ReadPixelsSlice :: proc "c" (x, y, width, height: i32, format: Enum, type: Enum, slice: $S/[]$E) { +ReadPixelsSlice :: proc "contextless" (x, y, width, height: i32, format: Enum, type: Enum, slice: $S/[]$E) { ReadnPixels(x, y, width, height, format, type, len(slice)*size_of(E), raw_data(slice)) } -TexImage2DSlice :: proc "c" (target: Enum, level: i32, internalformat: Enum, width, height: i32, border: i32, format, type: Enum, slice: $S/[]$E) { +TexImage2DSlice :: proc "contextless" (target: Enum, level: i32, internalformat: Enum, width, height: i32, border: i32, format, type: Enum, slice: $S/[]$E) { TexImage2D(target, level, internalformat, width, height, border, format, type, len(slice)*size_of(E), raw_data(slice)) } -TexSubImage2DSlice :: proc "c" (target: Enum, level: i32, xoffset, yoffset, width, height: i32, format, type: Enum, slice: $S/[]$E) { +TexSubImage2DSlice :: proc "contextless" (target: Enum, level: i32, xoffset, yoffset, width, height: i32, format, type: Enum, slice: $S/[]$E) { TexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, len(slice)*size_of(E), raw_data(slice)) } \ No newline at end of file diff --git a/vendor/wasm/WebGL/webgl2.odin b/vendor/wasm/WebGL/webgl2.odin index a1d55b703..d360bc396 100644 --- a/vendor/wasm/WebGL/webgl2.odin +++ b/vendor/wasm/WebGL/webgl2.odin @@ -10,13 +10,13 @@ Sync :: distinct u32 TransformFeedback :: distinct u32 VertexArrayObject :: distinct u32 -IsWebGL2Supported :: proc "c" () -> bool { +IsWebGL2Supported :: proc "contextless" () -> bool { major, minor: i32 GetWebGLVersion(&major, &minor) return major >= 2 } -@(default_calling_convention="c") +@(default_calling_convention="contextless") foreign webgl2 { /* Buffer objects */ CopyBufferSubData :: proc(readTarget, writeTarget: Enum, readOffset, writeOffset: int, size: int) --- @@ -110,7 +110,7 @@ foreign webgl2 { GetActiveUniformBlockName :: proc(program: Program, uniformBlockIndex: i32, buf: []byte) -> string { foreign webgl2 { - _GetActiveUniformBlockName :: proc(program: Program, uniformBlockIndex: i32, buf: []byte, length: ^int) --- + _GetActiveUniformBlockName :: proc "contextless" (program: Program, uniformBlockIndex: i32, buf: []byte, length: ^int) --- } n: int _GetActiveUniformBlockName(program, uniformBlockIndex, buf, &n) @@ -118,65 +118,65 @@ GetActiveUniformBlockName :: proc(program: Program, uniformBlockIndex: i32, buf: } -Uniform1uiv :: proc "c" (location: i32, v: u32) { +Uniform1uiv :: proc "contextless" (location: i32, v: u32) { Uniform1ui(location, v) } -Uniform2uiv :: proc "c" (location: i32, v: glm.uvec2) { +Uniform2uiv :: proc "contextless" (location: i32, v: glm.uvec2) { Uniform2ui(location, v.x, v.y) } -Uniform3uiv :: proc "c" (location: i32, v: glm.uvec3) { +Uniform3uiv :: proc "contextless" (location: i32, v: glm.uvec3) { Uniform3ui(location, v.x, v.y, v.z) } -Uniform4uiv :: proc "c" (location: i32, v: glm.uvec4) { +Uniform4uiv :: proc "contextless" (location: i32, v: glm.uvec4) { Uniform4ui(location, v.x, v.y, v.z, v.w) } -UniformMatrix3x2fv :: proc "c" (location: i32, m: glm.mat3x2) { +UniformMatrix3x2fv :: proc "contextless" (location: i32, m: glm.mat3x2) { foreign webgl2 { - _UniformMatrix3x2fv :: proc "c" (location: i32, addr: [^]f32) --- + _UniformMatrix3x2fv :: proc "contextless" (location: i32, addr: [^]f32) --- } array := matrix_flatten(m) _UniformMatrix3x2fv(location, &array[0]) } -UniformMatrix4x2fv :: proc "c" (location: i32, m: glm.mat4x2) { +UniformMatrix4x2fv :: proc "contextless" (location: i32, m: glm.mat4x2) { foreign webgl2 { - _UniformMatrix4x2fv :: proc "c" (location: i32, addr: [^]f32) --- + _UniformMatrix4x2fv :: proc "contextless" (location: i32, addr: [^]f32) --- } array := matrix_flatten(m) _UniformMatrix4x2fv(location, &array[0]) } -UniformMatrix2x3fv :: proc "c" (location: i32, m: glm.mat2x3) { +UniformMatrix2x3fv :: proc "contextless" (location: i32, m: glm.mat2x3) { foreign webgl2 { - _UniformMatrix2x3fv :: proc "c" (location: i32, addr: [^]f32) --- + _UniformMatrix2x3fv :: proc "contextless" (location: i32, addr: [^]f32) --- } array := matrix_flatten(m) _UniformMatrix2x3fv(location, &array[0]) } -UniformMatrix4x3fv :: proc "c" (location: i32, m: glm.mat4x3) { +UniformMatrix4x3fv :: proc "contextless" (location: i32, m: glm.mat4x3) { foreign webgl2 { - _UniformMatrix4x3fv :: proc "c" (location: i32, addr: [^]f32) --- + _UniformMatrix4x3fv :: proc "contextless" (location: i32, addr: [^]f32) --- } array := matrix_flatten(m) _UniformMatrix4x3fv(location, &array[0]) } -UniformMatrix2x4fv :: proc "c" (location: i32, m: glm.mat2x4) { +UniformMatrix2x4fv :: proc "contextless" (location: i32, m: glm.mat2x4) { foreign webgl2 { - _UniformMatrix2x4fv :: proc "c" (location: i32, addr: [^]f32) --- + _UniformMatrix2x4fv :: proc "contextless" (location: i32, addr: [^]f32) --- } array := matrix_flatten(m) _UniformMatrix2x4fv(location, &array[0]) } -UniformMatrix3x4fv :: proc "c" (location: i32, m: glm.mat3x4) { +UniformMatrix3x4fv :: proc "contextless" (location: i32, m: glm.mat3x4) { foreign webgl2 { - _UniformMatrix3x4fv :: proc "c" (location: i32, addr: [^]f32) --- + _UniformMatrix3x4fv :: proc "contextless" (location: i32, addr: [^]f32) --- } array := matrix_flatten(m) _UniformMatrix3x4fv(location, &array[0]) } -VertexAttribI4iv :: proc "c" (index: i32, v: glm.ivec4) { +VertexAttribI4iv :: proc "contextless" (index: i32, v: glm.ivec4) { VertexAttribI4i(index, v.x, v.y, v.z, v.w) } -VertexAttribI4uiv :: proc "c" (index: i32, v: glm.uvec4) { +VertexAttribI4uiv :: proc "contextless" (index: i32, v: glm.uvec4) { VertexAttribI4ui(index, v.x, v.y, v.z, v.w) } From 6b59aee336665d16a3c1dfc295301f0d9c92f4b1 Mon Sep 17 00:00:00 2001 From: skytrias Date: Fri, 23 Jun 2023 16:18:40 +0200 Subject: [PATCH 075/144] cleanup with -vet and add to all_vendor --- examples/all/all_vendor.odin | 10 ++++++- vendor/fontstash/fontstash.odin | 48 +++++++++++++++--------------- vendor/nanovg/gl/gl.odin | 12 ++++---- vendor/nanovg/nanovg.odin | 52 ++++++++++++++++----------------- 4 files changed, 63 insertions(+), 59 deletions(-) diff --git a/examples/all/all_vendor.odin b/examples/all/all_vendor.odin index fe1080e20..fa1e8d995 100644 --- a/examples/all/all_vendor.odin +++ b/examples/all/all_vendor.odin @@ -47,6 +47,10 @@ import CA "vendor:darwin/QuartzCore" // NOTE(bill): only one can be checked at a time import lua_5_4 "vendor:lua/5.4" +import nvg "vendor:nanovg" +import nvg_gl "vendor:nanovg/gl" +import fontstash "vendor:fontstash" + _ :: botan_bindings _ :: botan_blake2b _ :: gost @@ -92,4 +96,8 @@ _ :: MTL _ :: MTK _ :: CA -_ :: lua_5_4 \ No newline at end of file +_ :: lua_5_4 + +_ :: nvg +_ :: nvg_gl +_ :: fontstash \ No newline at end of file diff --git a/vendor/fontstash/fontstash.odin b/vendor/fontstash/fontstash.odin index b4bf86d46..64c1ce7cd 100644 --- a/vendor/fontstash/fontstash.odin +++ b/vendor/fontstash/fontstash.odin @@ -1,12 +1,10 @@ package fontstash import "core:runtime" -import "core:fmt" import "core:log" import "core:os" import "core:mem" import "core:math" -import "core:unicode" import "core:strings" import stbtt "vendor:stb/truetype" @@ -329,7 +327,7 @@ __AtlasAddWhiteRect :: proc(ctx: ^FontContext, w, h: int) { // Rasterize dst := ctx.textureData[gx + gy * ctx.width:] - for y in 0.. (res: ^Glyph) #no_bounds_check { if isize < 2 { return @@ -479,7 +477,7 @@ __getGlyph :: proc( if glyph.codepoint == codepoint && glyph.isize == isize && - glyph.blurSize == blurSize + glyph.blurSize == blur { res = glyph return @@ -506,10 +504,10 @@ __getGlyph :: proc( } pixel_size := f32(isize) / 10 - blurSize := min(blurSize, 20) + blurSize := min(blur, 20) padding := i16(blurSize + 2) // 2 minimum padding scale := __getPixelHeightScale(render_font, pixel_size) - advance, lsb, x0, y0, x1, y1 := __buildGlyphBitmap(render_font, glyph_index, pixel_size, scale) + advance, _, x0, y0, x1, y1 := __buildGlyphBitmap(render_font, glyph_index, pixel_size, scale) gw := (x1 - x0) + i32(padding) * 2 gh := (y1 - y0) + i32(padding) * 2 @@ -596,7 +594,7 @@ BLUR_ZPREC :: 7 __blurCols :: proc(dst: []u8, w, h, dstStride, alpha: int) { dst := dst - for y in 0..> BLUR_APREC @@ -657,38 +655,38 @@ __blur :: proc(dst: []u8, w, h, dstStride: int, blurSize: i16) { ///////////////////////////////// ExpandAtlas :: proc(ctx: ^FontContext, width, height: int, allocator := context.allocator) -> bool { - width := max(ctx.width, width) - height := max(ctx.height, height) + w := max(ctx.width, width) + h := max(ctx.height, height) - if width == ctx.width && height == ctx.height { + if w == ctx.width && h == ctx.height { return true } if ctx.callbackResize != nil { - ctx.callbackResize(ctx.userData, width, height) + ctx.callbackResize(ctx.userData, w, h) } - data := make([]byte, width * height, allocator) + data := make([]byte, w * h, allocator) for i in 0.. ctx.width { - mem.set(&data[i * width + ctx.width], 0, width - ctx.width) + if w > ctx.width { + mem.set(&data[i * w + ctx.width], 0, w - ctx.width) } } - if height > ctx.height { - mem.set(&data[ctx.height * width], 0, (height - ctx.height) * width) + if h > ctx.height { + mem.set(&data[ctx.height * w], 0, (h - ctx.height) * w) } delete(ctx.textureData) ctx.textureData = data // increase atlas size - __atlasExpand(ctx, width, height) + __atlasExpand(ctx, w, h) // add existing data as dirty maxy := i16(0) @@ -700,10 +698,10 @@ ExpandAtlas :: proc(ctx: ^FontContext, width, height: int, allocator := context. ctx.dirtyRect[2] = f32(ctx.width) ctx.dirtyRect[3] = f32(maxy) - ctx.width = width - ctx.height = height - ctx.itw = 1.0 / f32(width) - ctx.ith = 1.0 / f32(height) + ctx.width = w + ctx.height = h + ctx.itw = 1.0 / f32(w) + ctx.ith = 1.0 / f32(h) return true } @@ -994,7 +992,7 @@ __getQuad :: proc( } // fill props right - rx, ry, x0, y0, x1, y1, xoff, yoff, glyph_width, glyph_height: f32 + rx, ry, x0, y0, x1, y1, xoff, yoff: f32 xoff = f32(glyph.xoff + 1) yoff = f32(glyph.yoff + 1) x0 = f32(glyph.x0 + 1) diff --git a/vendor/nanovg/gl/gl.odin b/vendor/nanovg/gl/gl.odin index 698943e2b..fb1c063f7 100644 --- a/vendor/nanovg/gl/gl.odin +++ b/vendor/nanovg/gl/gl.odin @@ -1383,10 +1383,10 @@ BindFramebuffer :: proc(fb: ^framebuffer) { } CreateFramebuffer :: proc(ctx: ^nvg.Context, w, h: int, imageFlags: ImageFlags) -> (fb: framebuffer) { - defaultFBO: i32 - defaultRBO: i32 - gl.GetIntegerv(gl.FRAMEBUFFER_BINDING, &defaultFBO) - gl.GetIntegerv(gl.RENDERBUFFER_BINDING, &defaultRBO) + tempFBO: i32 + tempRBO: i32 + gl.GetIntegerv(gl.FRAMEBUFFER_BINDING, &tempFBO) + gl.GetIntegerv(gl.RENDERBUFFER_BINDING, &tempRBO) imageFlags := imageFlags incl(&imageFlags, ImageFlags { .FLIP_Y, .PREMULTIPLIED }) @@ -1422,8 +1422,8 @@ CreateFramebuffer :: proc(ctx: ^nvg.Context, w, h: int, imageFlags: ImageFlags) // goto error } - gl.BindFramebuffer(gl.FRAMEBUFFER, u32(defaultFBO)) - gl.BindRenderbuffer(gl.RENDERBUFFER, u32(defaultRBO)) + gl.BindFramebuffer(gl.FRAMEBUFFER, u32(tempFBO)) + gl.BindRenderbuffer(gl.RENDERBUFFER, u32(tempRBO)) return } diff --git a/vendor/nanovg/nanovg.odin b/vendor/nanovg/nanovg.odin index 8beb08cce..27054f049 100644 --- a/vendor/nanovg/nanovg.odin +++ b/vendor/nanovg/nanovg.odin @@ -4,7 +4,6 @@ package nanovg // TODO rename enums to old nanovg style! import "core:mem" -import "core:runtime" import "core:math" import "core:fmt" import "../fontstash" @@ -452,10 +451,10 @@ RGBA :: proc(r, g, b, a: u8) -> (res: Color) { // Linearly interpolates from color c0 to c1, and returns resulting color value. LerpRGBA :: proc(c0, c1: Color, u: f32) -> (cint: Color) { - u := clamp(u, 0.0, 1.0) - oneminu := 1.0 - u + clamped := clamp(u, 0.0, 1.0) + oneminu := 1.0 - clamped for i in 0..<4 { - cint[i] = c0[i] * oneminu + c1[i] * u + cint[i] = c0[i] * oneminu + c1[i] * clamped } return @@ -469,8 +468,8 @@ HSL :: proc(h, s, l: f32) -> Color { // Returns color value specified by hue, saturation and lightness and alpha. // HSL values are all in range [0..1], alpha in range [0..255] -HSLA :: proc(h, s, l: f32, a: u8) -> (col: Color) { - hue :: proc(h, m1, m2: f32) -> f32 { +HSLA :: proc(hue, saturation, lightness: f32, a: u8) -> (col: Color) { + hue_get :: proc(h, m1, m2: f32) -> f32 { h := h if h < 0 { @@ -492,17 +491,17 @@ HSLA :: proc(h, s, l: f32, a: u8) -> (col: Color) { return m1 } - h := math.mod(h, 1.0) + h := math.mod(hue, 1.0) if h < 0.0 { h += 1.0 } - s := clamp(s, 0.0, 1.0) - l := clamp(l, 0.0, 1.0) + s := clamp(saturation, 0.0, 1.0) + l := clamp(lightness, 0.0, 1.0) m2 := l <= 0.5 ? (l * (1 + s)) : (l + s - l * s) m1 := 2 * l - m2 - col.r = clamp(hue(h + 1.0/3.0, m1, m2), 0.0, 1.0) - col.g = clamp(hue(h, m1, m2), 0.0, 1.0) - col.b = clamp(hue(h - 1.0/3.0, m1, m2), 0.0, 1.0) + col.r = clamp(hue_get(h + 1.0/3.0, m1, m2), 0.0, 1.0) + col.g = clamp(hue_get(h, m1, m2), 0.0, 1.0) + col.b = clamp(hue_get(h - 1.0/3.0, m1, m2), 0.0, 1.0) col.a = f32(a) / 255.0 return } @@ -919,8 +918,8 @@ CreateImageMem :: proc(ctx: ^Context, data: []byte, imageFlags: ImageFlags) -> i return 0 } - data := mem.slice_ptr(img, int(w) * int(h) * int(n)) - image := CreateImageRGBA(ctx, int(w), int(h), imageFlags, data) + pixel_data := mem.slice_ptr(img, int(w) * int(h) * int(n)) + image := CreateImageRGBA(ctx, int(w), int(h), imageFlags, pixel_data) stbi.image_free(img) return image } @@ -1115,11 +1114,11 @@ ImagePattern :: proc( Scissor :: proc( ctx: ^Context, x, y: f32, - w, h: f32, + width, height: f32, ) { state := __getState(ctx) - w := max(w, 0) - h := max(h, 0) + w := max(width, 0) + h := max(height, 0) TransformIdentity(&state.scissor.xform) state.scissor.xform[4] = x + w * 0.5 @@ -1568,7 +1567,7 @@ __flattenPaths :: proc(ctx: ^Context) { } } - for k in 0.. bool { } // draw a single codepoint, useful for icons -TextIcon :: proc(ctx: ^Context, x, y: f32, codepoint: rune) -> f32 { +TextIcon :: proc(ctx: ^Context, xpos, ypos: f32, codepoint: rune) -> f32 { state := __getState(ctx) scale := __getFontScale(state) * ctx.devicePxRatio invscale := f32(1.0) / scale is_flipped := __isTransformFlipped(state.xform[:]) if state.fontId == -1 { - return x + return xpos } fs := &ctx.fs @@ -2871,8 +2870,8 @@ TextIcon :: proc(ctx: ^Context, x, y: f32, codepoint: rune) -> f32 { fscale := fontstash.__getPixelHeightScale(font, f32(isize) / 10) // transform x / y - x := x * scale - y := y * scale + x := xpos * scale + y := ypos * scale switch fstate.ah { case .LEFT: {} @@ -3160,7 +3159,7 @@ TextBreakLines :: proc( fontstash.SetAlignVertical(fs, state.alignVertical) fontstash.SetFont(fs, state.fontId) - break_row_width := break_row_width * scale + break_x := break_row_width * scale iter := fontstash.TextIterInit(fs, 0, 0, text^) prev_iter := iter q: fontstash.Quad @@ -3268,7 +3267,7 @@ TextBreakLines :: proc( } // Break to new line when a character is beyond break width. - if (type == .Char || type == .CJK) && next_width > break_row_width { + if (type == .Char || type == .CJK) && next_width > break_x { // The run length is too long, need to break to new line. if (break_end == row_start) { // The current word is longer than the row length, just break it from here. @@ -3374,7 +3373,6 @@ TextBoxBounds :: proc( // alignment halign := state.alignHorizontal - valign := state.alignVertical old_align := state.alignHorizontal defer state.alignHorizontal = old_align state.alignHorizontal = .LEFT From bbe50bdaf1dcebd1e6627929704f231c2a02bbcb Mon Sep 17 00:00:00 2001 From: skytrias Date: Fri, 23 Jun 2023 16:21:52 +0200 Subject: [PATCH 076/144] -strict-style fix --- vendor/nanovg/gl/gl.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/nanovg/gl/gl.odin b/vendor/nanovg/gl/gl.odin index fb1c063f7..630e36dd4 100644 --- a/vendor/nanovg/gl/gl.odin +++ b/vendor/nanovg/gl/gl.odin @@ -415,7 +415,7 @@ __renderCreateTexture :: proc( // No mips. if .GENERATE_MIPMAPS in imageFlags { - log.errorf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h); + log.errorf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h) excl(&imageFlags, ImageFlags { .GENERATE_MIPMAPS }) } } From ea00619c3bc1baacd249552397c445fd86dd7500 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 26 Jun 2023 15:20:40 +0100 Subject: [PATCH 077/144] `for &e, i in array` and `for k, &v in map` (alternative to passing the iterable by pointer) --- src/check_expr.cpp | 10 ++++- src/check_stmt.cpp | 33 +++++++++++++-- src/entity.cpp | 2 + src/llvm_backend_stmt.cpp | 88 ++++++++++++++++++++++++--------------- 4 files changed, 94 insertions(+), 39 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 83e44b39f..532555b0a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2273,7 +2273,15 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * defer (end_error_block()); error(op, "Cannot take the pointer address of '%s'", str); if (e != nullptr && (e->flags & EntityFlag_ForValue) != 0) { - error_line("\tSuggestion: Did you want to pass the iterable value to the for statement by pointer to get addressable semantics?\n"); + Type *parent_type = type_deref(e->Variable.for_loop_parent_type); + + if (parent_type != nullptr && is_type_string(parent_type)) { + error_line("\tSuggestion: Iterating over a string produces an intermediate 'rune' value which cannot be addressed.\n"); + } else if (parent_type != nullptr && is_type_tuple(parent_type)) { + error_line("\tSuggestion: Iterating over a procedure does not produce values which are addressable.\n"); + } else { + error_line("\tSuggestion: Did you want to pass the iterable value to the for statement by pointer to get addressable semantics?\n"); + } } if (e != nullptr && (e->flags & EntityFlag_SwitchValue) != 0) { error_line("\tSuggestion: Did you want to pass the value to the switch statement by pointer to get addressable semantics?\n"); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index cf6f998e5..30eef4f25 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1469,12 +1469,15 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) Ast *expr = unparen_expr(rs->expr); + bool is_possibly_addressable = true; isize max_val_count = 2; if (is_ast_range(expr)) { ast_node(ie, BinaryExpr, expr); Operand x = {}; Operand y = {}; + is_possibly_addressable = false; + bool ok = check_range(ctx, expr, true, &x, &y, nullptr); if (!ok) { goto skip_expr_range_stmt; @@ -1497,6 +1500,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) gb_string_free(t); goto skip_expr_range_stmt; } else { + is_possibly_addressable = false; + if (is_reverse) { error(node, "#reverse for is not supported for enum types"); } @@ -1510,7 +1515,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) Type *t = base_type(type_deref(operand.type)); switch (t->kind) { case Type_Basic: - if (is_type_string(t) && t->Basic.kind != Basic_cstring) { + if (t->Basic.kind == Basic_string) { + is_possibly_addressable = false; array_add(&vals, t_rune); array_add(&vals, t_int); if (is_reverse) { @@ -1529,6 +1535,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) case Type_Array: if (is_ptr) use_by_reference_for_value = true; + if (!is_ptr) is_possibly_addressable = operand.mode == Addressing_Variable; array_add(&vals, t->Array.elem); array_add(&vals, t_int); break; @@ -1575,6 +1582,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) array_add(&vals, e->type); } + is_possibly_addressable = false; + if (rs->vals.count > 1 && rs->vals[1] != nullptr && count < 3) { gbString s = type_to_string(t); error(operand.expr, "Expected a 3-valued expression on the rhs, got (%s)", s); @@ -1644,8 +1653,13 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } Ast * name = lhs[i]; Type *type = rhs[i]; - Entity *entity = nullptr; + + bool is_addressed = false; + if (name->kind == Ast_UnaryExpr && name->UnaryExpr.op.kind == Token_And) { + is_addressed = true; + name = name->UnaryExpr.expr; + } if (name->kind == Ast_Ident) { Token token = name->Ident.token; String str = token.string; @@ -1659,7 +1673,16 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) entity->flags |= EntityFlag_ForValue; entity->flags |= EntityFlag_Value; entity->identifier = name; - if (i == addressable_index && use_by_reference_for_value) { + entity->Variable.for_loop_parent_type = type_of_expr(expr); + + if (is_addressed) { + if (is_possibly_addressable && i == addressable_index) { + entity->flags &= ~EntityFlag_Value; + } else { + char const *idx_name = is_map ? "key" : "index"; + error(token, "The %s variable '%.*s' cannot be made addressable", idx_name, LIT(str)); + } + } else if (i == addressable_index && use_by_reference_for_value) { entity->flags &= ~EntityFlag_Value; } if (is_soa) { @@ -1678,7 +1701,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) entity = found; } } else { - error(name, "A variable declaration must be an identifier"); + gbString s = expr_to_string(lhs[i]); + error(name, "A variable declaration must be an identifier, got %s", s); + gb_string_free(s); } if (entity == nullptr) { diff --git a/src/entity.cpp b/src/entity.cpp index d6f4edece..2dcb76482 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -209,6 +209,8 @@ struct Entity { ParameterValue param_value; + Type *for_loop_parent_type; + String thread_local_model; Entity * foreign_library; Ast * foreign_library_ident; diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 0702af42e..ae6adb0e2 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -619,6 +619,18 @@ gb_internal void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_t } +gb_internal Ast *lb_strip_and_prefix(Ast *ident) { + if (ident != nullptr) { + if (ident->kind == Ast_UnaryExpr && ident->UnaryExpr.op.kind == Token_And) { + ident = ident->UnaryExpr.expr; + } + GB_ASSERT(ident->kind == Ast_Ident); + } + return ident; +} + + + gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node, AstRangeStmt *rs, Scope *scope) { bool ADD_EXTRA_WRAPPING_CHECK = true; @@ -627,13 +639,15 @@ gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node, lb_open_scope(p, scope); + Ast *val0 = rs->vals.count > 0 ? lb_strip_and_prefix(rs->vals[0]) : nullptr; + Ast *val1 = rs->vals.count > 1 ? lb_strip_and_prefix(rs->vals[1]) : nullptr; Type *val0_type = nullptr; Type *val1_type = nullptr; - if (rs->vals.count > 0 && rs->vals[0] != nullptr && !is_blank_ident(rs->vals[0])) { - val0_type = type_of_expr(rs->vals[0]); + if (val0 != nullptr && !is_blank_ident(val0)) { + val0_type = type_of_expr(val0); } - if (rs->vals.count > 1 && rs->vals[1] != nullptr && !is_blank_ident(rs->vals[1])) { - val1_type = type_of_expr(rs->vals[1]); + if (val1 != nullptr && !is_blank_ident(val1)) { + val1_type = type_of_expr(val1); } TokenKind op = Token_Lt; @@ -649,7 +663,7 @@ gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node, lbAddr value; if (val0_type != nullptr) { - Entity *e = entity_of_node(rs->vals[0]); + Entity *e = entity_of_node(val0); value = lb_add_local(p, val0_type, e, false); } else { value = lb_add_local_generated(p, lower.type, false); @@ -658,7 +672,7 @@ gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node, lbAddr index; if (val1_type != nullptr) { - Entity *e = entity_of_node(rs->vals[1]); + Entity *e = entity_of_node(val1); index = lb_add_local(p, val1_type, e, false); } else { index = lb_add_local_generated(p, t_int, false); @@ -680,8 +694,8 @@ gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node, lbValue val = lb_addr_load(p, value); lbValue idx = lb_addr_load(p, index); - if (val0_type) lb_store_range_stmt_val(p, rs->vals[0], val); - if (val1_type) lb_store_range_stmt_val(p, rs->vals[1], idx); + if (val0_type) lb_store_range_stmt_val(p, val0, val); + if (val1_type) lb_store_range_stmt_val(p, val1, idx); { // NOTE: this check block will most likely be optimized out, and is here @@ -815,12 +829,14 @@ gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs lb_open_scope(p, scope); + Ast *val0 = rs->vals.count > 0 ? lb_strip_and_prefix(rs->vals[0]) : nullptr; + Ast *val1 = rs->vals.count > 1 ? lb_strip_and_prefix(rs->vals[1]) : nullptr; Type *val_types[2] = {}; - if (rs->vals.count > 0 && rs->vals[0] != nullptr && !is_blank_ident(rs->vals[0])) { - val_types[0] = type_of_expr(rs->vals[0]); + if (val0 != nullptr && !is_blank_ident(val0)) { + val_types[0] = type_of_expr(val0); } - if (rs->vals.count > 1 && rs->vals[1] != nullptr && !is_blank_ident(rs->vals[1])) { - val_types[1] = type_of_expr(rs->vals[1]); + if (val1 != nullptr && !is_blank_ident(val1)) { + val_types[1] = type_of_expr(val1); } @@ -901,14 +917,14 @@ gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs if (val_types[0]) { - Entity *e = entity_of_node(rs->vals[0]); + Entity *e = entity_of_node(val0); if (e != nullptr) { lbAddr soa_val = lb_addr_soa_variable(array.addr, lb_addr_load(p, index), nullptr); map_set(&p->module->soa_values, e, soa_val); } } if (val_types[1]) { - lb_store_range_stmt_val(p, rs->vals[1], lb_addr_load(p, index)); + lb_store_range_stmt_val(p, val1, lb_addr_load(p, index)); } @@ -942,13 +958,15 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc lb_open_scope(p, scope); + Ast *val0 = rs->vals.count > 0 ? lb_strip_and_prefix(rs->vals[0]) : nullptr; + Ast *val1 = rs->vals.count > 1 ? lb_strip_and_prefix(rs->vals[1]) : nullptr; Type *val0_type = nullptr; Type *val1_type = nullptr; - if (rs->vals.count > 0 && rs->vals[0] != nullptr && !is_blank_ident(rs->vals[0])) { - val0_type = type_of_expr(rs->vals[0]); + if (val0 != nullptr && !is_blank_ident(val0)) { + val0_type = type_of_expr(val0); } - if (rs->vals.count > 1 && rs->vals[1] != nullptr && !is_blank_ident(rs->vals[1])) { - val1_type = type_of_expr(rs->vals[1]); + if (val1 != nullptr && !is_blank_ident(val1)) { + val1_type = type_of_expr(val1); } lbValue val = {}; @@ -1042,11 +1060,11 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc if (is_map) { - if (val0_type) lb_store_range_stmt_val(p, rs->vals[0], key); - if (val1_type) lb_store_range_stmt_val(p, rs->vals[1], val); + if (val0_type) lb_store_range_stmt_val(p, val0, key); + if (val1_type) lb_store_range_stmt_val(p, val1, val); } else { - if (val0_type) lb_store_range_stmt_val(p, rs->vals[0], val); - if (val1_type) lb_store_range_stmt_val(p, rs->vals[1], key); + if (val0_type) lb_store_range_stmt_val(p, val0, val); + if (val1_type) lb_store_range_stmt_val(p, val1, key); } lb_push_target_list(p, rs->label, done, loop, nullptr); @@ -1064,21 +1082,23 @@ gb_internal void lb_build_unroll_range_stmt(lbProcedure *p, AstUnrollRangeStmt * lb_open_scope(p, scope); // Open scope here + Ast *val0 = lb_strip_and_prefix(rs->val0); + Ast *val1 = lb_strip_and_prefix(rs->val1); Type *val0_type = nullptr; Type *val1_type = nullptr; - if (rs->val0 != nullptr && !is_blank_ident(rs->val0)) { - val0_type = type_of_expr(rs->val0); + if (val0 != nullptr && !is_blank_ident(val0)) { + val0_type = type_of_expr(val0); } - if (rs->val1 != nullptr && !is_blank_ident(rs->val1)) { - val1_type = type_of_expr(rs->val1); + if (val1 != nullptr && !is_blank_ident(val1)) { + val1_type = type_of_expr(val1); } if (val0_type != nullptr) { - Entity *e = entity_of_node(rs->val0); + Entity *e = entity_of_node(val0); lb_add_local(p, e->type, e, true); } if (val1_type != nullptr) { - Entity *e = entity_of_node(rs->val1); + Entity *e = entity_of_node(val1); lb_add_local(p, e->type, e, true); } @@ -1092,8 +1112,8 @@ gb_internal void lb_build_unroll_range_stmt(lbProcedure *p, AstUnrollRangeStmt * lbAddr val0_addr = {}; lbAddr val1_addr = {}; - if (val0_type) val0_addr = lb_build_addr(p, rs->val0); - if (val1_type) val1_addr = lb_build_addr(p, rs->val1); + if (val0_type) val0_addr = lb_build_addr(p, val0); + if (val1_type) val1_addr = lb_build_addr(p, val1); TokenKind op = expr->BinaryExpr.op.kind; Ast *start_expr = expr->BinaryExpr.left; @@ -1135,8 +1155,8 @@ gb_internal void lb_build_unroll_range_stmt(lbProcedure *p, AstUnrollRangeStmt * lbAddr val0_addr = {}; lbAddr val1_addr = {}; - if (val0_type) val0_addr = lb_build_addr(p, rs->val0); - if (val1_type) val1_addr = lb_build_addr(p, rs->val1); + if (val0_type) val0_addr = lb_build_addr(p, val0); + if (val1_type) val1_addr = lb_build_addr(p, val1); for_array(i, bet->Enum.fields) { Entity *field = bet->Enum.fields[i]; @@ -1149,8 +1169,8 @@ gb_internal void lb_build_unroll_range_stmt(lbProcedure *p, AstUnrollRangeStmt * } else { lbAddr val0_addr = {}; lbAddr val1_addr = {}; - if (val0_type) val0_addr = lb_build_addr(p, rs->val0); - if (val1_type) val1_addr = lb_build_addr(p, rs->val1); + if (val0_type) val0_addr = lb_build_addr(p, val0); + if (val1_type) val1_addr = lb_build_addr(p, val1); GB_ASSERT(expr->tav.mode == Addressing_Constant); From 00d60e28c2a3e3e3a2e8bf7617bd62c0f9b1aae8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 26 Jun 2023 15:41:53 +0100 Subject: [PATCH 078/144] Allow `switch &v in ...` work to be consistent with `for &e in ...` --- src/check_expr.cpp | 34 +++++++++++++++++++++++++++++++++- src/check_stmt.cpp | 20 +++++++++++++++++--- src/entity.cpp | 4 +++- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 532555b0a..e0e78b441 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2246,6 +2246,33 @@ gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) { return o->mode != Addressing_Variable && o->mode != Addressing_SoaVariable; } +gb_internal void check_old_for_or_switch_value_usage(Ast *expr) { + Entity *e = entity_of_node(expr); + if (e != nullptr && (e->flags & EntityFlag_OldForOrSwitchValue) != 0) { + GB_ASSERT(e->kind == Entity_Variable); + + begin_error_block(); + defer (end_error_block()); + + if ((e->flags & EntityFlag_ForValue) != 0) { + Type *parent_type = type_deref(e->Variable.for_loop_parent_type); + + error(expr, "Assuming a for-in defined value is addressable as the iterable is passed by value has been disallowed with '-strict-style'."); + + if (is_type_map(parent_type)) { + error_line("\tSuggestion: Prefer doing 'for key, &%.*s in ...'\n", LIT(e->token.string)); + } else { + error_line("\tSuggestion: Prefer doing 'for &%.*s in ...'\n", LIT(e->token.string)); + } + } else { + GB_ASSERT((e->flags & EntityFlag_SwitchValue) != 0); + + error(expr, "Assuming a switch-in defined value is addressable as the iterable is passed by value has been disallowed with '-strict-style'."); + error_line("\tSuggestion: Prefer doing 'switch &%.*s in ...'\n", LIT(e->token.string)); + } + } +} + gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) { switch (op.kind) { case Token_And: { // Pointer address @@ -2255,7 +2282,7 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * gbString str = expr_to_string(ue->expr); defer (gb_string_free(str)); - Entity *e = entity_of_node(o->expr); + Entity *e = entity_of_node(ue->expr); if (e != nullptr && (e->flags & EntityFlag_Param) != 0) { error(op, "Cannot take the pointer address of '%s' which is a procedure parameter", str); } else { @@ -2306,6 +2333,11 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * o->type = alloc_type_pointer(o->type); } } else { + if (build_context.strict_style && ast_node_expect(node, Ast_UnaryExpr)) { + ast_node(ue, UnaryExpr, node); + check_old_for_or_switch_value_usage(ue->expr); + } + o->type = alloc_type_pointer(o->type); } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 30eef4f25..964ff1842 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -417,6 +417,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O return nullptr; case Addressing_Variable: + check_old_for_or_switch_value_usage(lhs->expr); break; case Addressing_MapIndex: { @@ -1141,8 +1142,14 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_ syntax_error(as_token, "Expected 1 expression after 'in'"); return; } + bool is_addressed = false; + Ast *lhs = as->lhs[0]; Ast *rhs = as->rhs[0]; + if (lhs->kind == Ast_UnaryExpr && lhs->UnaryExpr.op.kind == Token_And) { + is_addressed = true; + lhs = lhs->UnaryExpr.expr; + } check_expr(ctx, &x, rhs); check_assignment(ctx, &x, nullptr, str_lit("type switch expression")); @@ -1281,12 +1288,15 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_ } } - bool is_reference = false; + bool is_reference = is_addressed; + bool old_style = false; - if (is_ptr && + if (!is_reference && + is_ptr && cc->list.count == 1 && case_type != nullptr) { is_reference = true; + old_style = true; } if (cc->list.count > 1 || saw_nil) { @@ -1305,9 +1315,12 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_ { Entity *tag_var = alloc_entity_variable(ctx->scope, lhs->Ident.token, case_type, EntityState_Resolved); tag_var->flags |= EntityFlag_Used; + tag_var->flags |= EntityFlag_SwitchValue; if (!is_reference) { tag_var->flags |= EntityFlag_Value; - tag_var->flags |= EntityFlag_SwitchValue; + } + if (old_style) { + tag_var->flags |= EntityFlag_OldForOrSwitchValue; } add_entity(ctx, ctx->scope, lhs, tag_var); add_entity_use(ctx, lhs, tag_var); @@ -1683,6 +1696,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) error(token, "The %s variable '%.*s' cannot be made addressable", idx_name, LIT(str)); } } else if (i == addressable_index && use_by_reference_for_value) { + entity->flags |= EntityFlag_OldForOrSwitchValue; entity->flags &= ~EntityFlag_Value; } if (is_soa) { diff --git a/src/entity.cpp b/src/entity.cpp index 2dcb76482..649dd900d 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -84,7 +84,9 @@ enum EntityFlag : u64 { EntityFlag_CustomLinkage_LinkOnce = 1ull<<44, EntityFlag_Require = 1ull<<50, - EntityFlag_ByPtr = 1ull<<51, // enforce parameter is passed by pointer + EntityFlag_ByPtr = 1ull<<51, // enforce parameter is passed by pointer + + EntityFlag_OldForOrSwitchValue = 1ull<<52, EntityFlag_Overridden = 1ull<<63, }; From 3dec55f009da4293aca870d50f7b15668c4bba7c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 26 Jun 2023 15:42:57 +0100 Subject: [PATCH 079/144] Replace `x in &y` Use `&v in y` syntax through core & vendor for `switch`/`for` statements --- .../container/topological_sort/topological_sort.odin | 4 ++-- core/encoding/hxa/read.odin | 4 ++-- core/encoding/json/unmarshal.odin | 10 +++++----- core/image/netpbm/netpbm.odin | 12 ++++++------ core/image/png/helpers.odin | 2 +- core/math/big/helpers.odin | 6 +++--- core/math/big/internal.odin | 4 ++-- core/math/big/rat.odin | 2 +- core/math/ease/ease.odin | 2 +- core/reflect/reflect.odin | 4 ++-- core/strings/strings.odin | 8 ++++---- core/text/i18n/i18n.odin | 4 ++-- core/thread/thread_pool.odin | 2 +- core/unicode/tools/generate_entity_table.odin | 2 +- 14 files changed, 33 insertions(+), 33 deletions(-) diff --git a/core/container/topological_sort/topological_sort.odin b/core/container/topological_sort/topological_sort.odin index 4b69930d5..314e3e070 100644 --- a/core/container/topological_sort/topological_sort.odin +++ b/core/container/topological_sort/topological_sort.odin @@ -32,7 +32,7 @@ init :: proc(sorter: ^$S/Sorter($K)) { } destroy :: proc(sorter: ^$S/Sorter($K)) { - for _, v in &sorter.relations { + for _, v in sorter.relations { delete(v.dependents) } delete(sorter.relations) @@ -80,7 +80,7 @@ sort :: proc(sorter: ^$S/Sorter($K)) -> (sorted, cycled: [dynamic]K) { } } - for root in &sorted do for k, _ in relations[root].dependents { + for root in sorted do for k, _ in relations[root].dependents { relation := &relations[k] relation.dependencies -= 1 if relation.dependencies == 0 { diff --git a/core/encoding/hxa/read.odin b/core/encoding/hxa/read.odin index abe295530..8a8636f19 100644 --- a/core/encoding/hxa/read.odin +++ b/core/encoding/hxa/read.odin @@ -83,7 +83,7 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato meta_data = make([]Meta, int(capacity)) count := 0 defer meta_data = meta_data[:count] - for m in &meta_data { + for &m in meta_data { m.name = read_name(r) or_return type := read_value(r, Meta_Value_Type) or_return @@ -116,7 +116,7 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato layer_count := 0 layers = make(Layer_Stack, stack_count) defer layers = layers[:layer_count] - for layer in &layers { + for &layer in layers { layer.name = read_name(r) or_return layer.components = read_value(r, u8) or_return type := read_value(r, Layer_Data_Type) or_return diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index e6c61d8fa..678f2dcfa 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -72,7 +72,7 @@ unmarshal_string :: proc(data: string, ptr: ^$T, spec := DEFAULT_SPECIFICATION, @(private) assign_bool :: proc(val: any, b: bool) -> bool { v := reflect.any_core(val) - switch dst in &v { + switch &dst in v { case bool: dst = bool(b) case b8: dst = b8 (b) case b16: dst = b16 (b) @@ -85,7 +85,7 @@ assign_bool :: proc(val: any, b: bool) -> bool { @(private) assign_int :: proc(val: any, i: $T) -> bool { v := reflect.any_core(val) - switch dst in &v { + switch &dst in v { case i8: dst = i8 (i) case i16: dst = i16 (i) case i16le: dst = i16le (i) @@ -122,7 +122,7 @@ assign_int :: proc(val: any, i: $T) -> bool { @(private) assign_float :: proc(val: any, f: $T) -> bool { v := reflect.any_core(val) - switch dst in &v { + switch &dst in v { case f16: dst = f16 (f) case f16le: dst = f16le(f) case f16be: dst = f16be(f) @@ -150,7 +150,7 @@ assign_float :: proc(val: any, f: $T) -> bool { @(private) unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> bool { val := val - switch dst in &val { + switch &dst in val { case string: dst = str return true @@ -215,7 +215,7 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) { } } - switch dst in &v { + switch &dst in v { // Handle json.Value as an unknown type case Value: dst = parse_value(p) or_return diff --git a/core/image/netpbm/netpbm.odin b/core/image/netpbm/netpbm.odin index cb07b1e3a..74e482cb4 100644 --- a/core/image/netpbm/netpbm.odin +++ b/core/image/netpbm/netpbm.odin @@ -161,18 +161,18 @@ save_to_buffer :: proc(img: ^Image, custom_info: Info = {}, allocator := context // convert from native endianness if img.depth == 16 { pixels := mem.slice_data_cast([]u16be, data.buf[len(header_buf):]) - for p in &pixels { + for &p in pixels { p = u16be(transmute(u16) p) } } else if header.format in PFM { if header.little_endian { pixels := mem.slice_data_cast([]f32le, data.buf[len(header_buf):]) - for p in &pixels { + for &p in pixels { p = f32le(transmute(f32) p) } } else { pixels := mem.slice_data_cast([]f32be, data.buf[len(header_buf):]) - for p in &pixels { + for &p in pixels { p = f32be(transmute(f32) p) } } @@ -578,18 +578,18 @@ decode_image :: proc(img: ^Image, header: Header, data: []byte, allocator := con if header.format in PFM { pixels := mem.slice_data_cast([]f32, img.pixels.buf[:]) if header.little_endian { - for p in &pixels { + for &p in pixels { p = f32(transmute(f32le) p) } } else { - for p in &pixels { + for &p in pixels { p = f32(transmute(f32be) p) } } } else { if img.depth == 16 { pixels := mem.slice_data_cast([]u16, img.pixels.buf[:]) - for p in &pixels { + for &p in pixels { p = u16(transmute(u16be) p) } } diff --git a/core/image/png/helpers.odin b/core/image/png/helpers.odin index 025c2b866..889b3cb6b 100644 --- a/core/image/png/helpers.odin +++ b/core/image/png/helpers.odin @@ -36,7 +36,7 @@ destroy :: proc(img: ^Image) { bytes.buffer_destroy(&img.pixels) if v, ok := img.metadata.(^image.PNG_Info); ok { - for chunk in &v.chunks { + for chunk in v.chunks { delete(chunk.data) } delete(v.chunks) diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 6c4b5dd01..a4313a244 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -19,7 +19,7 @@ import rnd "core:math/rand" int_destroy :: proc(integers: ..^Int) { integers := integers - for a in &integers { + for a in integers { assert_if_nil(a) } #force_inline internal_int_destroy(..integers) @@ -408,7 +408,7 @@ clear_if_uninitialized_multi :: proc(args: ..^Int, allocator := context.allocato args := args assert_if_nil(..args) - for i in &args { + for i in args { #force_inline internal_clear_if_uninitialized_single(i, allocator) or_return } return err @@ -435,7 +435,7 @@ int_init_multi :: proc(integers: ..^Int, allocator := context.allocator) -> (err assert_if_nil(..integers) integers := integers - for a in &integers { + for a in integers { #force_inline internal_clear(a, true, allocator) or_return } return nil diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 13aa96bef..968a26f8f 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -1857,7 +1857,7 @@ internal_root_n :: proc { internal_int_root_n, } internal_int_destroy :: proc(integers: ..^Int) { integers := integers - for a in &integers { + for &a in integers { if internal_int_allocated_cap(a) > 0 { mem.zero_slice(a.digit[:]) free(&a.digit[0]) @@ -2909,7 +2909,7 @@ internal_int_init_multi :: proc(integers: ..^Int, allocator := context.allocator context.allocator = allocator integers := integers - for a in &integers { + for a in integers { internal_clear(a) or_return } return nil diff --git a/core/math/big/rat.odin b/core/math/big/rat.odin index c3efc30aa..35618affb 100644 --- a/core/math/big/rat.odin +++ b/core/math/big/rat.odin @@ -137,7 +137,7 @@ rat_copy :: proc(dst, src: ^Rat, minimize := false, allocator := context.allocat internal_rat_destroy :: proc(rationals: ..^Rat) { rationals := rationals - for z in &rationals { + for &z in rationals { internal_int_destroy(&z.a, &z.b) } } diff --git a/core/math/ease/ease.odin b/core/math/ease/ease.odin index d5cb85dd8..0e6569bca 100644 --- a/core/math/ease/ease.odin +++ b/core/math/ease/ease.odin @@ -450,7 +450,7 @@ flux_tween_init :: proc(tween: ^Flux_Tween($T), duration: time.Duration) where i flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float(T) { clear(&flux.keys_to_be_deleted) - for key, tween in &flux.values { + for key, &tween in flux.values { delay_remainder := f64(0) // Update delay if necessary. diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index 2eb31c21b..a88557e0e 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -781,7 +781,7 @@ set_union_variant_raw_tag :: proc(a: any, tag: i64) { tag_ptr := uintptr(a.data) + info.tag_offset tag_any := any{rawptr(tag_ptr), info.tag_type.id} - switch i in &tag_any { + switch &i in tag_any { case u8: i = u8(tag) case i8: i = i8(tag) case u16: i = u16(tag) @@ -1312,7 +1312,7 @@ relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid) ptr_any := any{data, base_integer_id} ptr: rawptr - switch i in &ptr_any { + switch &i in ptr_any { case u8: ptr = _handle(&i) case u16: ptr = _handle(&i) case u32: ptr = _handle(&i) diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 6daa5f9c9..66a75f96a 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -1194,7 +1194,7 @@ Output: split_lines :: proc(s: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error { sep :: "\n" lines := _split(s, sep, 0, -1, allocator) or_return - for line in &lines { + for &line in lines { line = _trim_cr(line) } return lines, nil @@ -1234,7 +1234,7 @@ Output: split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error { sep :: "\n" lines := _split(s, sep, 0, n, allocator) or_return - for line in &lines { + for &line in lines { line = _trim_cr(line) } return lines, nil @@ -1273,7 +1273,7 @@ Output: split_lines_after :: proc(s: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error { sep :: "\n" lines := _split(s, sep, len(sep), -1, allocator) or_return - for line in &lines { + for &line in lines { line = _trim_cr(line) } return lines, nil @@ -1314,7 +1314,7 @@ Output: split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error { sep :: "\n" lines := _split(s, sep, len(sep), n, allocator) or_return - for line in &lines { + for &line in lines { line = _trim_cr(line) } return lines, nil diff --git a/core/text/i18n/i18n.odin b/core/text/i18n/i18n.odin index 9d030db16..8513f30c8 100644 --- a/core/text/i18n/i18n.odin +++ b/core/text/i18n/i18n.odin @@ -170,8 +170,8 @@ destroy :: proc(catalog: ^Translation = ACTIVE, allocator := context.allocator) return } - for section in &catalog.k_v { - for key in &catalog.k_v[section] { + for section in catalog.k_v { + for key in catalog.k_v[section] { delete(catalog.k_v[section][key]) } delete(catalog.k_v[section]) diff --git a/core/thread/thread_pool.odin b/core/thread/thread_pool.odin index 820de8ad4..1a4119e5f 100644 --- a/core/thread/thread_pool.odin +++ b/core/thread/thread_pool.odin @@ -81,7 +81,7 @@ pool_destroy :: proc(pool: ^Pool) { delete(pool.tasks) delete(pool.tasks_done) - for t in &pool.threads { + for &t in pool.threads { destroy(t) } diff --git a/core/unicode/tools/generate_entity_table.odin b/core/unicode/tools/generate_entity_table.odin index 328ba9091..fb4e4c2a4 100644 --- a/core/unicode/tools/generate_entity_table.odin +++ b/core/unicode/tools/generate_entity_table.odin @@ -221,7 +221,7 @@ named_xml_entity_to_rune :: proc(name: string) -> (decoded: rune, ok: bool) { delete(entity_map) delete(names) - for name in &names { + for &name in names { free(&name) } } From c8f475174e4cc0acd9255386f9f6aff27c7aaa38 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 26 Jun 2023 15:51:08 +0100 Subject: [PATCH 080/144] Fix tests for `-strict-style` --- tests/documentation/documentation_tester.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/documentation/documentation_tester.odin b/tests/documentation/documentation_tester.odin index a4d18d1eb..1f0f8ca97 100644 --- a/tests/documentation/documentation_tester.odin +++ b/tests/documentation/documentation_tester.odin @@ -238,10 +238,10 @@ find_and_add_examples :: proc(docs: string, package_name: string, entity_name: s } } // Remove first layer of tabs which are always present - for line in &example_block.lines { + for &line in example_block.lines { line = strings.trim_prefix(line, "\t") } - for line in &output_block.lines { + for &line in output_block.lines { line = strings.trim_prefix(line, "\t") } append(&g_examples_to_verify, Example_Test { From 6c6f9f7d25c2c4fd6af95dd2bbe91eb597fe1830 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 26 Jun 2023 15:55:35 +0100 Subject: [PATCH 081/144] Fix fmt implementation for `js` --- core/fmt/fmt_js.odin | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/core/fmt/fmt_js.odin b/core/fmt/fmt_js.odin index 5e06041f5..881cde867 100644 --- a/core/fmt/fmt_js.odin +++ b/core/fmt/fmt_js.odin @@ -11,27 +11,24 @@ foreign odin_env { } @(private="file") -write_vtable := io.Stream_VTable{ - impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { - fd := u32(uintptr(s.stream_data)) +write_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { + if mode == .Write { + fd := u32(uintptr(stream_data)) write(fd, p) - return len(p), nil - }, + return i64(len(p)), nil + } + return 0, .Empty } @(private="file") stdout := io.Writer{ - stream = { - stream_vtable = &write_vtable, - stream_data = rawptr(uintptr(1)), - }, + procedure = write_stream_proc, + data = rawptr(uintptr(1)), } @(private="file") stderr := io.Writer{ - stream = { - stream_vtable = &write_vtable, - stream_data = rawptr(uintptr(2)), - }, + procedure = write_stream_proc, + data = rawptr(uintptr(2)), } // print formats using the default print settings and writes to stdout From 3d9328fd79f45c85f1ecc513b2c05071f9c1d1a3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 26 Jun 2023 15:55:52 +0100 Subject: [PATCH 082/144] Default to `panic` allocator for wasm targets --- core/mem/allocators.odin | 12 +++--- core/runtime/default_allocators_js.odin | 4 +- core/runtime/default_allocators_nil.odin | 50 ++++++++++++++++++++++- core/runtime/default_allocators_wasi.odin | 4 +- 4 files changed, 59 insertions(+), 11 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 603c2a6c7..7767740c9 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -813,22 +813,22 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, switch mode { case .Alloc: if size > 0 { - panic("mem: panic allocator, .Alloc called") + panic("mem: panic allocator, .Alloc called", loc=loc) } case .Alloc_Non_Zeroed: if size > 0 { - panic("mem: panic allocator, .Alloc_Non_Zeroed called") + panic("mem: panic allocator, .Alloc_Non_Zeroed called", loc=loc) } case .Resize: if size > 0 { - panic("mem: panic allocator, .Resize called") + panic("mem: panic allocator, .Resize called", loc=loc) } case .Free: if old_memory != nil { - panic("mem: panic allocator, .Free called") + panic("mem: panic allocator, .Free called", loc=loc) } case .Free_All: - panic("mem: panic allocator, .Free_All called") + panic("mem: panic allocator, .Free_All called", loc=loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) @@ -838,7 +838,7 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, return nil, nil case .Query_Info: - panic("mem: panic allocator, .Query_Info called") + panic("mem: panic allocator, .Query_Info called", loc=loc) } return nil, nil diff --git a/core/runtime/default_allocators_js.odin b/core/runtime/default_allocators_js.odin index cc70b963a..715073f08 100644 --- a/core/runtime/default_allocators_js.odin +++ b/core/runtime/default_allocators_js.odin @@ -1,5 +1,5 @@ //+build js package runtime -default_allocator_proc :: nil_allocator_proc -default_allocator :: nil_allocator +default_allocator_proc :: panic_allocator_proc +default_allocator :: panic_allocator diff --git a/core/runtime/default_allocators_nil.odin b/core/runtime/default_allocators_nil.odin index f86990581..a340050eb 100644 --- a/core/runtime/default_allocators_nil.odin +++ b/core/runtime/default_allocators_nil.odin @@ -35,4 +35,52 @@ nil_allocator :: proc() -> Allocator { when ODIN_OS == .Freestanding { default_allocator_proc :: nil_allocator_proc default_allocator :: nil_allocator -} \ No newline at end of file +} + + + +panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, + size, alignment: int, + old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { + switch mode { + case .Alloc: + if size > 0 { + panic("panic allocator, .Alloc called", loc=loc) + } + case .Alloc_Non_Zeroed: + if size > 0 { + panic("panic allocator, .Alloc_Non_Zeroed called", loc=loc) + } + case .Resize: + if size > 0 { + panic("panic allocator, .Resize called", loc=loc) + } + case .Free: + if old_memory != nil { + panic("panic allocator, .Free called", loc=loc) + } + case .Free_All: + panic("panic allocator, .Free_All called", loc=loc) + + case .Query_Features: + set := (^Allocator_Mode_Set)(old_memory) + if set != nil { + set^ = {.Query_Features} + } + return nil, nil + + case .Query_Info: + panic("panic allocator, .Query_Info called", loc=loc) + } + + return nil, nil +} + +panic_allocator :: proc() -> Allocator { + return Allocator{ + procedure = nil_allocator_proc, + data = nil, + } +} + + diff --git a/core/runtime/default_allocators_wasi.odin b/core/runtime/default_allocators_wasi.odin index 2e475e055..a7e6842a6 100644 --- a/core/runtime/default_allocators_wasi.odin +++ b/core/runtime/default_allocators_wasi.odin @@ -1,5 +1,5 @@ //+build wasi package runtime -default_allocator_proc :: nil_allocator_proc -default_allocator :: nil_allocator +default_allocator_proc :: panic_allocator_proc +default_allocator :: panic_allocator From 8182ba4ee05473d13c6619500457da9a1e0845ca Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 26 Jun 2023 16:48:43 +0100 Subject: [PATCH 083/144] Improve internal names for parapoly records --- src/check_expr.cpp | 49 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index e0e78b441..b12c6d114 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6828,15 +6828,15 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O return err; } - String generated_name = make_string_c(expr_to_string(call)); - CheckerContext ctx = *c; // NOTE(bill): We need to make sure the lookup scope for the record is the same as where it was created ctx.scope = polymorphic_record_parent_scope(original_type); GB_ASSERT(ctx.scope != nullptr); - Type *named_type = alloc_type_named(generated_name, nullptr, nullptr); Type *bt = base_type(original_type); + String generated_name = make_string_c(expr_to_string(call)); + + Type *named_type = alloc_type_named(generated_name, nullptr, nullptr); if (bt->kind == Type_Struct) { Ast *node = clone_ast(bt->Struct.node); Type *struct_type = alloc_type_struct(); @@ -6861,6 +6861,49 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O GB_PANIC("Unsupported parametric polymorphic record type"); } + + bt = base_type(named_type); + if (bt->kind == Type_Struct || bt->kind == Type_Union) { + GB_ASSERT(original_type->kind == Type_Named); + Entity *e = original_type->Named.type_name; + GB_ASSERT(e->kind == Entity_TypeName); + + gbString s = gb_string_make_reserve(heap_allocator(), e->token.string.len+3); + s = gb_string_append_fmt(s, "%.*s(", LIT(e->token.string)); + + Type *params = nullptr; + switch (bt->kind) { + case Type_Struct: params = bt->Struct.polymorphic_params; break; + case Type_Union: params = bt->Union.polymorphic_params; break; + } + + if (params != nullptr) for_array(i, params->Tuple.variables) { + Entity *v = params->Tuple.variables[i]; + String name = v->token.string; + if (i > 0) { + s = gb_string_append_fmt(s, ", "); + } + s = gb_string_append_fmt(s, "$%.*s", LIT(name)); + + if (v->kind == Entity_TypeName) { + if (v->type->kind != Type_Generic) { + s = gb_string_append_fmt(s, "="); + s = write_type_to_string(s, v->type, false); + } + } else if (v->kind == Entity_Constant) { + s = gb_string_append_fmt(s, "="); + s = write_exact_value_to_string(s, v->Constant.value); + } + } + s = gb_string_append_fmt(s, ")"); + + String new_name = make_string_c(s); + named_type->Named.name = new_name; + if (named_type->Named.type_name) { + named_type->Named.type_name->token.string = new_name; + } + } + operand->mode = Addressing_Type; operand->type = named_type; } From 3c0f1caa41e758e5568ed4d32b3f8951548c8672 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 26 Jun 2023 17:03:40 +0100 Subject: [PATCH 084/144] Fix #2606 --- src/llvm_backend_proc.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 47137c540..42ca90828 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3341,7 +3341,11 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { if (pt->params != nullptr) { - GB_ASSERT(args.count >= pt->params->Tuple.variables.count); + isize min_count = pt->params->Tuple.variables.count; + if (is_c_vararg) { + min_count -= 1; + } + GB_ASSERT(args.count >= min_count); for_array(arg_index, pt->params->Tuple.variables) { Entity *e = pt->params->Tuple.variables[arg_index]; if (pt->variadic && arg_index == pt->variadic_index) { From cdcb64b0d08be85c3ea579270eeb4d193b4cb495 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 26 Jun 2023 17:06:42 +0100 Subject: [PATCH 085/144] Add missing `-strict-style` check --- src/check_expr.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b12c6d114..7b3ddfc73 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2247,6 +2247,10 @@ gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) { } gb_internal void check_old_for_or_switch_value_usage(Ast *expr) { + if (!build_context.strict_style) { + return; + } + Entity *e = entity_of_node(expr); if (e != nullptr && (e->flags & EntityFlag_OldForOrSwitchValue) != 0) { GB_ASSERT(e->kind == Entity_Variable); From 8b8310711e4924678e605278fca1f8dbf517b903 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 26 Jun 2023 17:36:27 +0100 Subject: [PATCH 086/144] Fix #2594 zero sized union code generation --- src/llvm_backend.cpp | 4 +++- src/llvm_backend_general.cpp | 1 + src/llvm_backend_type.cpp | 5 ++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 730610ad9..12abe7b16 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -218,7 +218,9 @@ gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) { LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 0, false)); } else if (type->kind == Type_Union) { - if (is_type_union_maybe_pointer(type)) { + if (type_size_of(type) == 0) { + LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 1, false)); + } else if (is_type_union_maybe_pointer(type)) { Type *v = type->Union.variants[0]; Type *pv = alloc_type_pointer(v); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 9333f13a4..017eeca2e 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1323,6 +1323,7 @@ gb_internal lbValue lb_emit_union_tag_value(lbProcedure *p, lbValue u) { gb_internal void lb_emit_store_union_variant_tag(lbProcedure *p, lbValue parent, Type *variant_type) { Type *t = type_deref(parent.type); + GB_ASSERT(is_type_union(t)); if (is_type_union_maybe_pointer(t) || type_size_of(t) == 0) { // No tag needed! diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index a54fde748..4716733cc 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -654,10 +654,9 @@ gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup lbValue count = lb_const_int(m, t_int, variant_count); vals[0] = llvm_const_slice(m, memory_types, count); - i64 tag_size = union_tag_size(t); - i64 tag_offset = align_formula(t->Union.variant_block_size, tag_size); - + i64 tag_size = union_tag_size(t); if (tag_size > 0) { + i64 tag_offset = align_formula(t->Union.variant_block_size, tag_size); vals[1] = lb_const_int(m, t_uintptr, tag_offset).value; vals[2] = lb_type_info(m, union_tag_type(t)).value; } else { From ca1d4374359907f4862f4958860f1106c76657da Mon Sep 17 00:00:00 2001 From: skytrias Date: Mon, 26 Jun 2023 21:28:54 +0200 Subject: [PATCH 087/144] add build flags restrictions --- vendor/fontstash/fontstash.odin | 1 + vendor/nanovg/gl/gl.odin | 1 + vendor/nanovg/nanovg.odin | 1 + 3 files changed, 3 insertions(+) diff --git a/vendor/fontstash/fontstash.odin b/vendor/fontstash/fontstash.odin index 64c1ce7cd..7d9926cb1 100644 --- a/vendor/fontstash/fontstash.odin +++ b/vendor/fontstash/fontstash.odin @@ -1,3 +1,4 @@ +//+build windows, linux, darwin package fontstash import "core:runtime" diff --git a/vendor/nanovg/gl/gl.odin b/vendor/nanovg/gl/gl.odin index 630e36dd4..34b9f99fb 100644 --- a/vendor/nanovg/gl/gl.odin +++ b/vendor/nanovg/gl/gl.odin @@ -1,3 +1,4 @@ +//+build windows, linux, darwin package nanovg_gl import "core:log" diff --git a/vendor/nanovg/nanovg.odin b/vendor/nanovg/nanovg.odin index 27054f049..f22f9c425 100644 --- a/vendor/nanovg/nanovg.odin +++ b/vendor/nanovg/nanovg.odin @@ -1,3 +1,4 @@ +//+build windows, linux, darwin package nanovg // TODO rename structs to old nanovg style! From a820246f64b4c84bd82714551ae46c0e61e5cbe3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 26 Jun 2023 23:00:39 +0100 Subject: [PATCH 088/144] Keep -vet happy --- vendor/fontstash/fontstash.odin | 6 +++--- vendor/nanovg/gl/gl.odin | 8 ++++---- vendor/nanovg/nanovg.odin | 16 ++++++++-------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/vendor/fontstash/fontstash.odin b/vendor/fontstash/fontstash.odin index 7d9926cb1..67b2c781c 100644 --- a/vendor/fontstash/fontstash.odin +++ b/vendor/fontstash/fontstash.odin @@ -146,7 +146,7 @@ Init :: proc(using ctx: ^FontContext, w, h: int, loc: QuadLocation) { } Destroy :: proc(using ctx: ^FontContext) { - for font in &fonts { + for font in fonts { if font.freeLoadedData { delete(font.loadedData) } @@ -166,7 +166,7 @@ Reset :: proc(using ctx: ^FontContext) { __dirtyRectReset(ctx) mem.zero_slice(textureData) - for font in &fonts { + for &font in fonts { __lutReset(&font) } @@ -722,7 +722,7 @@ ResetAtlas :: proc(ctx: ^FontContext, width, height: int, allocator := context.a ctx.dirtyRect[3] = 0 // reset fonts - for font in &ctx.fonts { + for &font in ctx.fonts { clear(&font.glyphs) __lutReset(&font) } diff --git a/vendor/nanovg/gl/gl.odin b/vendor/nanovg/gl/gl.odin index 34b9f99fb..50df6e1b5 100644 --- a/vendor/nanovg/gl/gl.odin +++ b/vendor/nanovg/gl/gl.odin @@ -255,7 +255,7 @@ __blendFuncSeparate :: proc(ctx: ^Context, blend: ^Blend) { } __allocTexture :: proc(ctx: ^Context) -> (tex: ^Texture) { - for texture in &ctx.textures { + for &texture in ctx.textures { if texture.id == 0 { tex = &texture break @@ -275,7 +275,7 @@ __allocTexture :: proc(ctx: ^Context) -> (tex: ^Texture) { } __findTexture :: proc(ctx: ^Context, id: int) -> ^Texture { - for texture in &ctx.textures { + for &texture in ctx.textures { if texture.id == id { return &texture } @@ -285,7 +285,7 @@ __findTexture :: proc(ctx: ^Context, id: int) -> ^Texture { } __deleteTexture :: proc(ctx: ^Context, id: int) -> bool { - for texture, i in &ctx.textures { + for &texture, i in ctx.textures { if texture.id == id { if texture.tex != 0 && (.NO_DELETE not_in texture.flags) { gl.DeleteTextures(1, &texture.tex) @@ -1302,7 +1302,7 @@ __renderDelete :: proc(uptr: rawptr) { gl.DeleteBuffers(1, &ctx.vertBuf) } - for texture in &ctx.textures { + for &texture in ctx.textures { if texture.tex != 0 && (.NO_DELETE not_in texture.flags) { gl.DeleteTextures(1, &texture.tex) } diff --git a/vendor/nanovg/nanovg.odin b/vendor/nanovg/nanovg.odin index f22f9c425..bc0335f6a 100644 --- a/vendor/nanovg/nanovg.odin +++ b/vendor/nanovg/nanovg.odin @@ -326,7 +326,7 @@ DeleteInternal :: proc(ctx: ^Context) { __deletePathCache(ctx.cache) fontstash.Destroy(&ctx.fs) - for image in &ctx.fontImages { + for &image in ctx.fontImages { if image != 0 { DeleteImage(ctx, image) } @@ -1874,7 +1874,7 @@ __calculateJoins :: proc( } // Calculate which joins needs extra vertices to append, and gather vertex count. - for path in &cache.paths { + for &path in cache.paths { pts := cache.points[path.first:] p0 := &pts[path.count-1] p1 := &pts[0] @@ -1968,7 +1968,7 @@ __expandStroke :: proc( // Calculate max vertex usage. cverts := 0 - for path in &cache.paths { + for &path in cache.paths { loop := path.closed // TODO check if f32 calculation necessary? @@ -2097,7 +2097,7 @@ __expandFill :: proc( // Calculate max vertex usage. cverts := 0 - for path in &cache.paths { + for &path in cache.paths { cverts += path.count + path.nbevel + 1 if fringe { @@ -2109,7 +2109,7 @@ __expandFill :: proc( verts := __allocTempVerts(ctx, cverts) dst_index: int - for path in &cache.paths { + for &path in cache.paths { pts := cache.points[path.first:] p0, p1: ^Point rw, lw, woff: f32 @@ -2542,7 +2542,7 @@ Fill :: proc(ctx: ^Context) { ctx.cache.paths[:], ) - for path in &ctx.cache.paths { + for &path in ctx.cache.paths { ctx.fillTriCount += len(path.fill) - 2 ctx.fillTriCount += len(path.stroke) - 2 ctx.drawCallCount += 2 @@ -2588,7 +2588,7 @@ Stroke :: proc(ctx: ^Context) { ctx.cache.paths[:], ) - for path in &ctx.cache.paths { + for &path in ctx.cache.paths { ctx.strokeTriCount += len(path.stroke) - 2 ctx.drawCallCount += 1 } @@ -2597,7 +2597,7 @@ Stroke :: proc(ctx: ^Context) { DebugDumpPathCache :: proc(ctx: ^Context) { fmt.printf("~~~~~~~~~~~~~Dumping %d cached paths\n", len(ctx.cache.paths)) - for path, i in &ctx.cache.paths { + for &path, i in ctx.cache.paths { fmt.printf(" - Path %d\n", i) if len(path.fill) != 0 { From 330b393e1604b9a41fb8751b6446f73091c9f85c Mon Sep 17 00:00:00 2001 From: Rickard Andersson Date: Tue, 27 Jun 2023 21:37:10 +0300 Subject: [PATCH 089/144] fix(os): use `setenv` instead of `putenv` `setenv` doesn't copy the value that is put, which means that the previous code had a bug where we free'd the temporary memory and the environment was accidentally cleared right after the function finished. --- core/os/os_linux.odin | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index e0b60fd36..aaff01165 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -458,6 +458,7 @@ foreign libc { @(link_name="execvp") _unix_execvp :: proc(path: cstring, argv: [^]cstring) -> int --- @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- @(link_name="putenv") _unix_putenv :: proc(cstring) -> c.int --- + @(link_name="setenv") _unix_setenv :: proc(key: cstring, value: cstring, overwrite: c.int) -> c.int --- @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr --- @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- @@ -894,7 +895,10 @@ get_env :: proc(key: string, allocator := context.allocator) -> (value: string) set_env :: proc(key, value: string) -> Errno { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() s := strings.concatenate({key, "=", value, "\x00"}, context.temp_allocator) - res := _unix_putenv(strings.unsafe_string_to_cstring(s)) + key_cstring := strings.unsafe_string_to_cstring(strings.concatenate({key, "\x00"}, context.temp_allocator)) + value_cstring := strings.unsafe_string_to_cstring(strings.concatenate({value, "\x00"}, context.temp_allocator)) + // NOTE(GoNZooo): `setenv` instead of `putenv` because it copies both key and value more commonly + res := _unix_setenv(key_cstring, value_cstring, 1) if res < 0 { return Errno(get_last_error()) } From 6ff0ce15e76019c28707a416f60852110b16d4fc Mon Sep 17 00:00:00 2001 From: Rickard Andersson Date: Tue, 27 Jun 2023 21:42:20 +0300 Subject: [PATCH 090/144] cleanup: remove leftover line --- core/os/os_linux.odin | 1 - 1 file changed, 1 deletion(-) diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index aaff01165..a2c07d98e 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -894,7 +894,6 @@ get_env :: proc(key: string, allocator := context.allocator) -> (value: string) set_env :: proc(key, value: string) -> Errno { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - s := strings.concatenate({key, "=", value, "\x00"}, context.temp_allocator) key_cstring := strings.unsafe_string_to_cstring(strings.concatenate({key, "\x00"}, context.temp_allocator)) value_cstring := strings.unsafe_string_to_cstring(strings.concatenate({value, "\x00"}, context.temp_allocator)) // NOTE(GoNZooo): `setenv` instead of `putenv` because it copies both key and value more commonly From d03d5d8f038ee5f964edd9e40441b9027c91f180 Mon Sep 17 00:00:00 2001 From: Rickard Andersson Date: Tue, 27 Jun 2023 21:46:00 +0300 Subject: [PATCH 091/144] style: use tabs :[ --- core/os/os_linux.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index a2c07d98e..0a3bdbb9e 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -894,9 +894,9 @@ get_env :: proc(key: string, allocator := context.allocator) -> (value: string) set_env :: proc(key, value: string) -> Errno { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - key_cstring := strings.unsafe_string_to_cstring(strings.concatenate({key, "\x00"}, context.temp_allocator)) - value_cstring := strings.unsafe_string_to_cstring(strings.concatenate({value, "\x00"}, context.temp_allocator)) - // NOTE(GoNZooo): `setenv` instead of `putenv` because it copies both key and value more commonly + key_cstring := strings.unsafe_string_to_cstring(strings.concatenate({key, "\x00"}, context.temp_allocator)) + value_cstring := strings.unsafe_string_to_cstring(strings.concatenate({value, "\x00"}, context.temp_allocator)) + // NOTE(GoNZooo): `setenv` instead of `putenv` because it copies both key and value more commonly res := _unix_setenv(key_cstring, value_cstring, 1) if res < 0 { return Errno(get_last_error()) From f048ad13b52d3c2be2fcd975f1039eb88604360f Mon Sep 17 00:00:00 2001 From: Rickard Andersson Date: Tue, 27 Jun 2023 21:48:53 +0300 Subject: [PATCH 092/144] fix(set_env): use `clone_to_cstring` instead of `unsafe_to_cstring` --- core/os/os_linux.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index 0a3bdbb9e..1a4c1fddb 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -894,8 +894,8 @@ get_env :: proc(key: string, allocator := context.allocator) -> (value: string) set_env :: proc(key, value: string) -> Errno { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - key_cstring := strings.unsafe_string_to_cstring(strings.concatenate({key, "\x00"}, context.temp_allocator)) - value_cstring := strings.unsafe_string_to_cstring(strings.concatenate({value, "\x00"}, context.temp_allocator)) + key_cstring := strings.clone_to_cstring(key, context.temp_allocator) + value_cstring := strings.clone_to_cstring(value, context.temp_allocator) // NOTE(GoNZooo): `setenv` instead of `putenv` because it copies both key and value more commonly res := _unix_setenv(key_cstring, value_cstring, 1) if res < 0 { From d352e2fa31a36e5666cfc1e99983a0e04b36be29 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Tue, 27 Jun 2023 23:30:33 +0200 Subject: [PATCH 093/144] only run nightly upload if workflow is ran on main repo --- .github/workflows/nightly.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 67d0396c1..0bfaa11e9 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -7,6 +7,7 @@ on: jobs: build_windows: + if: github.repository == 'odin-lang/Odin' runs-on: windows-2022 steps: - uses: actions/checkout@v1 @@ -37,6 +38,7 @@ jobs: name: windows_artifacts path: dist build_ubuntu: + if: github.repository == 'odin-lang/Odin' runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 @@ -61,6 +63,7 @@ jobs: name: ubuntu_artifacts path: dist build_macos: + if: github.repository == 'odin-lang/Odin' runs-on: macOS-latest steps: - uses: actions/checkout@v1 From d50a8447209ef4e79a03a2a006c70987448d7d65 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 28 Jun 2023 11:04:51 +0100 Subject: [PATCH 094/144] Replace `mem` with `runtime` in `core:slice` --- core/slice/ptr.odin | 20 ++++++++++---------- core/slice/slice.odin | 30 ++++++++++++++++++------------ 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/core/slice/ptr.odin b/core/slice/ptr.odin index e2f1c3e7b..b17a27dc8 100644 --- a/core/slice/ptr.odin +++ b/core/slice/ptr.odin @@ -1,7 +1,7 @@ package slice import "core:builtin" -import "core:mem" +import "core:runtime" ptr_add :: proc(p: $P/^$T, x: int) -> ^T { return ([^]T)(p)[x:] @@ -27,9 +27,9 @@ ptr_swap_non_overlapping :: proc(x, y: rawptr, len: int) { a := rawptr(uintptr(x) + uintptr(i)) b := rawptr(uintptr(y) + uintptr(i)) - mem.copy(t, a, BLOCK_SIZE) - mem.copy(a, b, BLOCK_SIZE) - mem.copy(b, t, BLOCK_SIZE) + runtime.mem_copy(t, a, BLOCK_SIZE) + runtime.mem_copy(a, b, BLOCK_SIZE) + runtime.mem_copy(b, t, BLOCK_SIZE) } if i < len { @@ -38,9 +38,9 @@ ptr_swap_non_overlapping :: proc(x, y: rawptr, len: int) { a := rawptr(uintptr(x) + uintptr(i)) b := rawptr(uintptr(y) + uintptr(i)) - mem.copy(t, a, rem) - mem.copy(a, b, rem) - mem.copy(b, t, rem) + runtime.mem_copy(t, a, rem) + runtime.mem_copy(a, b, rem) + runtime.mem_copy(b, t, rem) } } @@ -59,9 +59,9 @@ ptr_swap_overlapping :: proc(x, y: rawptr, len: int) { for n := len; n > 0; n -= N { m := builtin.min(n, N) - mem.copy(&buffer, a, m) - mem.copy(a, b, m) - mem.copy(b, &buffer, m) + runtime.mem_copy(&buffer, a, m) + runtime.mem_copy(a, b, m) + runtime.mem_copy(b, &buffer, m) a, b = a[N:], b[N:] } diff --git a/core/slice/slice.odin b/core/slice/slice.odin index 412c90fc8..9a810141d 100644 --- a/core/slice/slice.odin +++ b/core/slice/slice.odin @@ -3,12 +3,12 @@ package slice import "core:intrinsics" import "core:builtin" import "core:math/bits" -import "core:mem" +import "core:runtime" _ :: intrinsics _ :: builtin _ :: bits -_ :: mem +_ :: runtime /* Turn a pointer and a length into a slice. @@ -164,7 +164,7 @@ equal :: proc(a, b: $T/[]$E) -> bool where intrinsics.type_is_comparable(E) { return false } when intrinsics.type_is_simple_compare(E) { - return mem.compare_ptrs(raw_data(a), raw_data(b), len(a)*size_of(E)) == 0 + return runtime.memory_compare(raw_data(a), raw_data(b), len(a)*size_of(E)) == 0 } else { for i in 0.. bool where intrinsics.type_is_simple_comp if len(a) != len(b) { return false } - return mem.compare_ptrs(raw_data(a), raw_data(b), len(a)*size_of(E)) == 0 + return runtime.memory_compare(raw_data(a), raw_data(b), len(a)*size_of(E)) == 0 } /* @@ -220,6 +220,12 @@ has_suffix :: proc(array: $T/[]$E, needle: E) -> bool where intrinsics.type_is_c return false } +zero :: proc(array: $T/[]$E) #no_bounds_check { + if len(array) > 0 { + intrinsics.mem_zero(raw_data(array), size_of(E)*len(array)) + } +} + fill :: proc(array: $T/[]$E, value: E) #no_bounds_check { if len(array) <= 0 { return @@ -250,7 +256,7 @@ swap_with_slice :: proc(a, b: $T/[]$E, loc := #caller_location) { } @(require_results) -concatenate :: proc(a: []$T/[]$E, allocator := context.allocator) -> (res: T, err: mem.Allocator_Error) #optional_allocator_error { +concatenate :: proc(a: []$T/[]$E, allocator := context.allocator) -> (res: T, err: runtime.Allocator_Error) #optional_allocator_error { if len(a) == 0 { return } @@ -268,7 +274,7 @@ concatenate :: proc(a: []$T/[]$E, allocator := context.allocator) -> (res: T, er // copies a slice into a new slice @(require_results) -clone :: proc(a: $T/[]$E, allocator := context.allocator) -> ([]E, mem.Allocator_Error) #optional_allocator_error { +clone :: proc(a: $T/[]$E, allocator := context.allocator) -> ([]E, runtime.Allocator_Error) #optional_allocator_error { d, err := make([]E, len(a), allocator) copy(d[:], a) return d, err @@ -276,7 +282,7 @@ clone :: proc(a: $T/[]$E, allocator := context.allocator) -> ([]E, mem.Allocator // copies slice into a new dynamic array -clone_to_dynamic :: proc(a: $T/[]$E, allocator := context.allocator) -> ([dynamic]E, mem.Allocator_Error) #optional_allocator_error { +clone_to_dynamic :: proc(a: $T/[]$E, allocator := context.allocator) -> ([dynamic]E, runtime.Allocator_Error) #optional_allocator_error { d, err := make([dynamic]E, len(a), allocator) copy(d[:], a) return d, err @@ -286,12 +292,12 @@ to_dynamic :: clone_to_dynamic // Converts slice into a dynamic array without cloning or allocating memory @(require_results) into_dynamic :: proc(a: $T/[]$E) -> [dynamic]E { - s := transmute(mem.Raw_Slice)a - d := mem.Raw_Dynamic_Array{ + s := transmute(runtime.Raw_Slice)a + d := runtime.Raw_Dynamic_Array{ data = s.data, len = 0, cap = s.len, - allocator = mem.nil_allocator(), + allocator = runtime.nil_allocator(), } return transmute([dynamic]E)d } @@ -373,7 +379,7 @@ as_ptr :: proc(array: $T/[]$E) -> [^]E { @(require_results) -mapper :: proc(s: $S/[]$U, f: proc(U) -> $V, allocator := context.allocator) -> (r: []V, err: mem.Allocator_Error) #optional_allocator_error { +mapper :: proc(s: $S/[]$U, f: proc(U) -> $V, allocator := context.allocator) -> (r: []V, err: runtime.Allocator_Error) #optional_allocator_error { r = make([]V, len(s), allocator) or_return for v, i in s { r[i] = f(v) @@ -402,7 +408,7 @@ filter :: proc(s: $S/[]$U, f: proc(U) -> bool, allocator := context.allocator) - } @(require_results) -scanner :: proc (s: $S/[]$U, initializer: $V, f: proc(V, U) -> V, allocator := context.allocator) -> (res: []V, err: mem.Allocator_Error) #optional_allocator_error { +scanner :: proc (s: $S/[]$U, initializer: $V, f: proc(V, U) -> V, allocator := context.allocator) -> (res: []V, err: runtime.Allocator_Error) #optional_allocator_error { if len(s) == 0 { return } res = make([]V, len(s), allocator) or_return From 9e9f3c485bfd283cda92c788a921e521e9c0a66b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 28 Jun 2023 11:36:27 +0100 Subject: [PATCH 095/144] General code style clean up for `vendor:fontstash` --- vendor/fontstash/fontstash.odin | 495 +++++++++++++++----------------- 1 file changed, 230 insertions(+), 265 deletions(-) diff --git a/vendor/fontstash/fontstash.odin b/vendor/fontstash/fontstash.odin index 67b2c781c..93a48bc0d 100644 --- a/vendor/fontstash/fontstash.odin +++ b/vendor/fontstash/fontstash.odin @@ -7,6 +7,7 @@ import "core:os" import "core:mem" import "core:math" import "core:strings" +import "core:slice" import stbtt "vendor:stb/truetype" // This is a port from Fontstash into odin - specialized for nanovg @@ -23,13 +24,13 @@ import stbtt "vendor:stb/truetype" // leaves GPU vertex creation & texture management up to the user // texture atlas expands by default -INVALID :: -1 -MAX_STATES :: 20 -HASH_LUT_SIZE :: 256 -INIT_GLYPHS :: 256 +INVALID :: -1 +MAX_STATES :: 20 +HASH_LUT_SIZE :: 256 +INIT_GLYPHS :: 256 INIT_ATLAS_NODES :: 256 -MAX_FALLBACKS :: 20 -Glyph_Index :: i32 // in case you want to change the handle for glyph indices +MAX_FALLBACKS :: 20 +Glyph_Index :: i32 // in case you want to change the handle for glyph indices AlignHorizontal :: enum { LEFT, @@ -47,30 +48,30 @@ AlignVertical :: enum { Font :: struct { name: string, // allocated - info: stbtt.fontinfo, - loadedData: []byte, + info: stbtt.fontinfo, + loadedData: []byte, freeLoadedData: bool, // in case you dont want loadedData to be removed - ascender: f32, - descender: f32, + ascender: f32, + descender: f32, lineHeight: f32, glyphs: [dynamic]Glyph, - lut: [HASH_LUT_SIZE]int, + lut: [HASH_LUT_SIZE]int, - fallbacks: [MAX_FALLBACKS]int, + fallbacks: [MAX_FALLBACKS]int, nfallbacks: int, } Glyph :: struct { - codepoint: rune, - index: Glyph_Index, - next: int, - isize: i16, - blurSize: i16, + codepoint: rune, + index: Glyph_Index, + next: int, + isize: i16, + blurSize: i16, x0, y0, x1, y1: i16, - xoff, yoff: i16, - xadvance: i16, + xoff, yoff: i16, + xadvance: i16, } AtlasNode :: struct { @@ -78,8 +79,8 @@ AtlasNode :: struct { } Vertex :: struct #packed { - x, y: f32, - u, v: f32, + x, y: f32, + u, v: f32, color: [4]u8, } @@ -95,13 +96,13 @@ FontContext :: struct { nodes: [dynamic]AtlasNode, // actual pixels - textureData: []byte, // allocated using context.allocator + textureData: []byte, // allocated using context.allocator width, height: int, // 1 / texture_atlas_width, 1 / texture_atlas_height itw, ith: f32, // state - states: []State, + states: []State, state_count: int, // used states location: QuadLocation, @@ -118,24 +119,24 @@ FontContext :: struct { callbackUpdate: proc(data: rawptr, dirtyRect: [4]f32, textureData: rawptr), } -Init :: proc(using ctx: ^FontContext, w, h: int, loc: QuadLocation) { - userData = ctx - location = loc - fonts = make([dynamic]Font, 0, 8) +Init :: proc(ctx: ^FontContext, w, h: int, loc: QuadLocation) { + ctx.userData = ctx + ctx.location = loc + ctx.fonts = make([dynamic]Font, 0, 8) - itw = f32(1) / f32(w) - ith = f32(1) / f32(h) - textureData = make([]byte, w * h) + ctx.itw, ctx.ith = 1.0 / f32(w), 1.0 / f32(h) + + ctx.textureData = make([]byte, w * h) - width = w - height = h - nodes = make([dynamic]AtlasNode, 0, INIT_ATLAS_NODES) + ctx.width = w + ctx.height = h + ctx.nodes = make([dynamic]AtlasNode, 0, INIT_ATLAS_NODES) __dirtyRectReset(ctx) - states = make([]State, MAX_STATES) + ctx.states = make([]State, MAX_STATES) // NOTE NECESSARY - append(&nodes, AtlasNode { + append(&ctx.nodes, AtlasNode{ width = i16(w), }) @@ -145,8 +146,8 @@ Init :: proc(using ctx: ^FontContext, w, h: int, loc: QuadLocation) { ClearState(ctx) } -Destroy :: proc(using ctx: ^FontContext) { - for font in fonts { +Destroy :: proc(ctx: ^FontContext) { + for font in ctx.fonts { if font.freeLoadedData { delete(font.loadedData) } @@ -155,18 +156,18 @@ Destroy :: proc(using ctx: ^FontContext) { delete(font.glyphs) } - delete(states) - delete(textureData) - delete(fonts) - delete(nodes) + delete(ctx.states) + delete(ctx.textureData) + delete(ctx.fonts) + delete(ctx.nodes) } -Reset :: proc(using ctx: ^FontContext) { - __atlasReset(ctx, width, height) +Reset :: proc(ctx: ^FontContext) { + __atlasReset(ctx, ctx.width, ctx.height) __dirtyRectReset(ctx) - mem.zero_slice(textureData) + slice.zero(ctx.textureData) - for &font in fonts { + for &font in ctx.fonts { __lutReset(&font) } @@ -175,84 +176,67 @@ Reset :: proc(using ctx: ^FontContext) { ClearState(ctx) } -__atlasInsertNode :: proc(using ctx: ^FontContext, idx, x, y, w: int) { - // resize is alright here - resize(&nodes, len(nodes) + 1) - - // shift nodes up once to leave space at idx - for i := len(nodes) - 1; i > idx; i -= 1 { - nodes[i] = nodes[i - 1] - } - - // set new inserted one to properties - nodes[idx].x = i16(x) - nodes[idx].y = i16(y) - nodes[idx].width = i16(w) -} - -__atlasRemoveNode :: proc(using ctx: ^FontContext, idx: int) { - if len(nodes) == 0 { - return - } - - // remove node at index, shift elements down - for i in idx.. width { - __atlasInsertNode(ctx, len(nodes), width, 0, w - width) - } - - width = w - height = h -} - -__atlasReset :: proc(using ctx: ^FontContext, w, h: int) { - width = w - height = h - clear(&nodes) - - // init root node - append(&nodes, AtlasNode { +__atlasInsertNode :: proc(ctx: ^FontContext, idx: int, x, y, w: int) { + inject_at(&ctx.nodes, idx, AtlasNode{ + x = i16(x), + y = i16(y), width = i16(w), }) } -__AtlasAddSkylineLevel :: proc(using ctx: ^FontContext, idx, x, y, w, h: int) { +__atlasRemoveNode :: proc(ctx: ^FontContext, idx: int) { + if len(ctx.nodes) == 0 { + return + } + + ordered_remove(&ctx.nodes, idx) +} + +__atlasExpand :: proc(ctx: ^FontContext, w, h: int) { + if w > ctx.width { + __atlasInsertNode(ctx, len(ctx.nodes), ctx.width, 0, w - ctx.width) + } + + ctx.width, ctx.height = w, h +} + +__atlasReset :: proc(ctx: ^FontContext, w, h: int) { + ctx.width, ctx.height = w, h + clear(&ctx.nodes) + + // init root node + append(&ctx.nodes, AtlasNode{ + width = i16(w), + }) +} + +__AtlasAddSkylineLevel :: proc(using ctx: ^FontContext, idx: int, x, y, w, h: int) { // insert new node __atlasInsertNode(ctx, idx, x, y + h, w) // Delete skyline segments that fall under the shadow of the new segment. for i := idx + 1; i < len(nodes); i += 1 { - if nodes[i].x < nodes[i - 1].x + nodes[i - 1].width { - shrink := nodes[i-1].x + nodes[i-1].width - nodes[i].x - nodes[i].x += i16(shrink) - nodes[i].width -= i16(shrink) - - if nodes[i].width <= 0 { - __atlasRemoveNode(ctx, i) - i -= 1 - } else { - break - } - } else { + if nodes[i].x >= nodes[i-1].x + nodes[i-1].width { break } + shrink := nodes[i-1].x + nodes[i-1].width - nodes[i].x + nodes[i].x += i16(shrink) + nodes[i].width -= i16(shrink) + + if nodes[i].width > 0 { + break + } + __atlasRemoveNode(ctx, i) + i -= 1 } // Merge same height skyline segments that are next to each other. - for i := 0; i < len(nodes) - 1; i += 1 { - if nodes[i].y == nodes[i + 1].y { - nodes[i].width += nodes[i + 1].width - __atlasRemoveNode(ctx, i + 1) - i -= 1 + for i := 0; i < len(nodes) - 1; /**/ { + if nodes[i].y == nodes[i+1].y { + nodes[i].width += nodes[i+1].width + __atlasRemoveNode(ctx, i+1) + } else { + i += 1 } } } @@ -261,14 +245,14 @@ __AtlasRectFits :: proc(using ctx: ^FontContext, i, w, h: int) -> int { // Checks if there is enough space at the location of skyline span 'i', // and return the max height of all skyline spans under that at that location, // (think tetris block being dropped at that position). Or -1 if no space found. - x := int(nodes[i].x) - y := int(nodes[i].y) - + + i := i + x, y := int(nodes[i].x), int(nodes[i].y) + if x + w > width { return -1 } - i := i space_left := w for space_left > 0 { if i == len(nodes) { @@ -288,8 +272,7 @@ __AtlasRectFits :: proc(using ctx: ^FontContext, i, w, h: int) -> int { } __AtlasAddRect :: proc(using ctx: ^FontContext, rw, rh: int) -> (rx, ry: int, ok: bool) { - besth := height - bestw := width + bestw, besth := width, height besti, bestx, besty := -1, -1, -1 // Bottom left fit heuristic. @@ -313,18 +296,11 @@ __AtlasAddRect :: proc(using ctx: ^FontContext, rw, rh: int) -> (rx, ry: int, ok // Perform the actual packing. __AtlasAddSkylineLevel(ctx, besti, bestx, besty, rw, rh) - ok = true - rx = bestx - ry = besty - return + return bestx, besty, true } -__AtlasAddWhiteRect :: proc(ctx: ^FontContext, w, h: int) { - gx, gy, ok := __AtlasAddRect(ctx, w, h) - - if !ok { - return - } +__AtlasAddWhiteRect :: proc(ctx: ^FontContext, w, h: int) -> bool { + gx, gy := __AtlasAddRect(ctx, w, h) or_return // Rasterize dst := ctx.textureData[gx + gy * ctx.width:] @@ -336,10 +312,12 @@ __AtlasAddWhiteRect :: proc(ctx: ^FontContext, w, h: int) { dst = dst[ctx.width:] } - ctx.dirtyRect[0] = cast(f32) min(int(ctx.dirtyRect[0]), gx) - ctx.dirtyRect[1] = cast(f32) min(int(ctx.dirtyRect[1]), gy) - ctx.dirtyRect[2] = cast(f32) max(int(ctx.dirtyRect[2]), gx + w) - ctx.dirtyRect[3] = cast(f32) max(int(ctx.dirtyRect[3]), gy + h) + ctx.dirtyRect[0] = f32(min(int(ctx.dirtyRect[0]), gx)) + ctx.dirtyRect[1] = f32(min(int(ctx.dirtyRect[1]), gy)) + ctx.dirtyRect[2] = f32(max(int(ctx.dirtyRect[2]), gx + w)) + ctx.dirtyRect[3] = f32(max(int(ctx.dirtyRect[3]), gy + h)) + + return true } AddFontPath :: proc( @@ -359,25 +337,26 @@ AddFontPath :: proc( // push a font to the font stack // optionally init with ascii characters at a wanted size AddFontMem :: proc( - ctx: ^FontContext, - name: string, - data: []u8, + ctx: ^FontContext, + name: string, + data: []u8, freeLoadedData: bool, ) -> int { - append(&ctx.fonts, Font {}) + append(&ctx.fonts, Font{}) res := &ctx.fonts[len(ctx.fonts) - 1] - res.loadedData = data + res.loadedData = data res.freeLoadedData = freeLoadedData - res.name = strings.clone(name) + res.name = strings.clone(name) stbtt.InitFont(&res.info, &res.loadedData[0], 0) ascent, descent, line_gap: i32 + stbtt.GetFontVMetrics(&res.info, &ascent, &descent, &line_gap) fh := f32(ascent - descent) - res.ascender = f32(ascent) / fh - res.descender = f32(descent) / fh + res.ascender = f32(ascent) / fh + res.descender = f32(descent) / fh res.lineHeight = (fh + f32(line_gap)) / fh - res.glyphs = make([dynamic]Glyph, 0, INIT_GLYPHS) + res.glyphs = make([dynamic]Glyph, 0, INIT_GLYPHS) __lutReset(res) return len(ctx.fonts) - 1 @@ -417,9 +396,7 @@ GetFontByName :: proc(ctx: ^FontContext, name: string) -> int { __lutReset :: proc(font: ^Font) { // set lookup table - for i in 0.. u32 { @@ -434,23 +411,23 @@ __hashint :: proc(a: u32) -> u32 { } __renderGlyphBitmap :: proc( - font: ^Font, - output: []u8, - outWidth: i32, - outHeight: i32, - outStride: i32, - scaleX: f32, - scaleY: f32, + font: ^Font, + output: []u8, + outWidth: i32, + outHeight: i32, + outStride: i32, + scaleX: f32, + scaleY: f32, glyphIndex: Glyph_Index, ) { stbtt.MakeGlyphBitmap(&font.info, raw_data(output), outWidth, outHeight, outStride, scaleX, scaleY, glyphIndex) } __buildGlyphBitmap :: proc( - font: ^Font, + font: ^Font, glyphIndex: Glyph_Index, - pixelSize: f32, - scale: f32, + pixelSize: f32, + scale: f32, ) -> (advance, lsb, x0, y0, x1, y1: i32) { stbtt.GetGlyphHMetrics(&font.info, glyphIndex, &advance, &lsb) stbtt.GetGlyphBitmapBox(&font.info, glyphIndex, scale, scale, &x0, &y0, &x1, &y1) @@ -459,28 +436,26 @@ __buildGlyphBitmap :: proc( // get glyph and push to atlas if not exists __getGlyph :: proc( - ctx: ^FontContext, - font: ^Font, + ctx: ^FontContext, + font: ^Font, codepoint: rune, - isize: i16, - blur: i16 = 0, -) -> (res: ^Glyph) #no_bounds_check { + isize: i16, + blur: i16 = 0, +) -> (res: ^Glyph, ok: bool) #no_bounds_check { if isize < 2 { return } // find code point and size h := __hashint(u32(codepoint)) & (HASH_LUT_SIZE - 1) - i := font.lut[h] - for i != -1 { + for i := font.lut[h]; i != -1; /**/ { glyph := &font.glyphs[i] - if - glyph.codepoint == codepoint && - glyph.isize == isize && - glyph.blurSize == blur - { + if glyph.codepoint == codepoint && + glyph.isize == isize && + glyph.blurSize == blur { res = glyph + ok = true return } @@ -505,39 +480,34 @@ __getGlyph :: proc( } pixel_size := f32(isize) / 10 - blurSize := min(blur, 20) - padding := i16(blurSize + 2) // 2 minimum padding - scale := __getPixelHeightScale(render_font, pixel_size) + blurSize := min(blur, 20) + padding := i16(blurSize + 2) // 2 minimum padding + scale := __getPixelHeightScale(render_font, pixel_size) advance, _, x0, y0, x1, y1 := __buildGlyphBitmap(render_font, glyph_index, pixel_size, scale) gw := (x1 - x0) + i32(padding) * 2 gh := (y1 - y0) + i32(padding) * 2 // Find free spot for the rect in the atlas - gx, gy, ok := __AtlasAddRect(ctx, int(gw), int(gh)) - if !ok { + gx, gy, rect_ok := __AtlasAddRect(ctx, int(gw), int(gh)) + if !rect_ok { // try again with expanded ExpandAtlas(ctx, ctx.width * 2, ctx.height * 2) - gx, gy, ok = __AtlasAddRect(ctx, int(gw), int(gh)) - } - - // still not ok? - if !ok { - return + gx, gy = __AtlasAddRect(ctx, int(gw), int(gh)) or_return } // Init glyph. - append(&font.glyphs, Glyph { + append(&font.glyphs, Glyph{ codepoint = codepoint, - isize = isize, - blurSize = blurSize, - index = glyph_index, - x0 = i16(gx), - y0 = i16(gy), - x1 = i16(i32(gx) + gw), - y1 = i16(i32(gy) + gh), - xadvance = i16(scale * f32(advance) * 10), - xoff = i16(x0 - i32(padding)), - yoff = i16(y0 - i32(padding)), + isize = isize, + blurSize = blurSize, + index = glyph_index, + x0 = i16(gx), + y0 = i16(gy), + x1 = i16(i32(gx) + gw), + y1 = i16(i32(gy) + gh), + xadvance = i16(scale * f32(advance) * 10), + xoff = i16(x0 - i32(padding)), + yoff = i16(y0 - i32(padding)), // insert char to hash lookup. next = font.lut[h], @@ -575,11 +545,12 @@ __getGlyph :: proc( __blur(dst, int(gw), int(gh), ctx.width, blurSize) } - ctx.dirtyRect[0] = cast(f32) min(int(ctx.dirtyRect[0]), int(res.x0)) - ctx.dirtyRect[1] = cast(f32) min(int(ctx.dirtyRect[1]), int(res.y0)) - ctx.dirtyRect[2] = cast(f32) max(int(ctx.dirtyRect[2]), int(res.x1)) - ctx.dirtyRect[3] = cast(f32) max(int(ctx.dirtyRect[3]), int(res.y1)) + ctx.dirtyRect[0] = f32(min(int(ctx.dirtyRect[0]), int(res.x0))) + ctx.dirtyRect[1] = f32(min(int(ctx.dirtyRect[1]), int(res.y0))) + ctx.dirtyRect[2] = f32(max(int(ctx.dirtyRect[2]), int(res.x1))) + ctx.dirtyRect[3] = f32(max(int(ctx.dirtyRect[3]), int(res.y1))) + ok = true return } @@ -640,7 +611,7 @@ __blurRows :: proc(dst: []u8, w, h, dstStride, alpha: int) { } __blur :: proc(dst: []u8, w, h, dstStride: int, blurSize: i16) { - assert(blurSize != 0) + assert(blurSize > 0) // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity) sigma := f32(blurSize) * 0.57735 // 1 / sqrt(3) @@ -710,9 +681,10 @@ ExpandAtlas :: proc(ctx: ^FontContext, width, height: int, allocator := context. ResetAtlas :: proc(ctx: ^FontContext, width, height: int, allocator := context.allocator) -> bool { if width == ctx.width && height == ctx.height { // just clear - mem.zero_slice(ctx.textureData) + slice.zero(ctx.textureData) } else { // realloc + delete(ctx.textureData, allocator) ctx.textureData = make([]byte, width * height, allocator) } @@ -732,7 +704,7 @@ ResetAtlas :: proc(ctx: ^FontContext, width, height: int, allocator := context.a ctx.itw = 1.0 / f32(width) ctx.ith = 1.0 / f32(height) - __AtlasAddWhiteRect(ctx, 2, 2) + _ = __AtlasAddWhiteRect(ctx, 2, 2) return true } @@ -769,16 +741,16 @@ CodepointWidth :: proc( // get top and bottom line boundary LineBounds :: proc(ctx: ^FontContext, y: f32) -> (miny, maxy: f32) { state := __getState(ctx) - font := __getFont(ctx, state.font) + font := __getFont(ctx, state.font) isize := i16(state.size * 10.0) y := y y += __getVerticalAlign(ctx, font, state.av, isize) if ctx.location == .TOPLEFT { - miny = y - font.ascender * f32(isize) / 10 + miny = y - font.ascender * f32(isize) / 10 maxy = miny + font.lineHeight * f32(isize / 10) } else if ctx.location == .BOTTOMLEFT { - miny = y + font.ascender * f32(isize) / 10 + miny = y + font.ascender * f32(isize) / 10 maxy = miny - font.lineHeight * f32(isize / 10) } @@ -809,9 +781,9 @@ ValidateTexture :: proc(using ctx: ^FontContext, dirty: ^[4]f32) -> bool { // get alignment based on font __getVerticalAlign :: proc( - ctx: ^FontContext, - font: ^Font, - av: AlignVertical, + ctx: ^FontContext, + font: ^Font, + av: AlignVertical, pixelSize: i16, ) -> (res: f32) { switch ctx.location { @@ -842,7 +814,7 @@ UTF8_ACCEPT :: 0 UTF8_REJECT :: 1 @(private) -utf8d := [400]u8 { +utf8d := [400]u8{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f @@ -871,11 +843,11 @@ __decutf8 :: #force_inline proc(state: ^rune, codep: ^rune, b: byte) -> bool { // state used to share font options State :: struct { - font: int, - size: f32, - color: [4]u8, + font: int, + size: f32, + color: [4]u8, spacing: f32, - blur: f32, + blur: f32, ah: AlignHorizontal, av: AlignVertical, @@ -890,21 +862,21 @@ Quad :: struct { // text iteration with custom settings TextIter :: struct { x, y, nextx, nexty, scale, spacing: f32, - isize, iblur: i16, + isize, iblur: i16, - font: ^Font, + font: ^Font, previousGlyphIndex: Glyph_Index, // unicode iteration - utf8state: rune, // utf8 - codepoint: rune, - text: string, + utf8state: rune, // utf8 + codepoint: rune, + text: string, codepointCount: int, // byte indices - str: int, + str: int, next: int, - end: int, + end: int, } // push a state, copies the current one over to the next one @@ -930,13 +902,13 @@ PopState :: proc(using ctx: ^FontContext) { // clear current state ClearState :: proc(ctx: ^FontContext) { state := __getState(ctx) - state.size = 12 - state.color = 255 - state.blur = 0 + state.size = 12 + state.color = 255 + state.blur = 0 state.spacing = 0 - state.font = 0 - state.ah = .LEFT - state.av = .BASELINE + state.font = 0 + state.ah = .LEFT + state.av = .BASELINE } __getState :: #force_inline proc(ctx: ^FontContext) -> ^State #no_bounds_check { @@ -975,13 +947,13 @@ SetAlignVertical :: proc(ctx: ^FontContext, av: AlignVertical) { } __getQuad :: proc( - ctx: ^FontContext, + ctx: ^FontContext, font: ^Font, previousGlyphIndex: i32, - glyph: ^Glyph, + glyph: ^Glyph, - scale: f32, + scale: f32, spacing: f32, x, y: ^f32, @@ -1036,27 +1008,27 @@ __getQuad :: proc( // init text iter struct with settings TextIterInit :: proc( - ctx: ^FontContext, - x: f32, - y: f32, + ctx: ^FontContext, + x: f32, + y: f32, text: string, ) -> (res: TextIter) { + + x, y := x, y + state := __getState(ctx) - res.font = __getFont(ctx, state.font) + res.font = __getFont(ctx, state.font) res.isize = i16(f32(state.size) * 10) res.iblur = i16(state.blur) res.scale = __getPixelHeightScale(res.font, f32(res.isize) / 10) // align horizontally - x := x - y := y switch state.ah { - case .LEFT: {} - + case .LEFT: + /**/ case .CENTER: width := TextBounds(ctx, text, x, y, nil) x = math.round(x - width * 0.5) - case .RIGHT: width := TextBounds(ctx, text, x, y, nil) x -= width @@ -1066,24 +1038,22 @@ TextIterInit :: proc( y = math.round(y + __getVerticalAlign(ctx, res.font, state.av, res.isize)) // set positions - res.x = x - res.nextx = x - res.y = y - res.nexty = y + res.x, res.nextx = x, x + res.y, res.nexty = y, y res.previousGlyphIndex = -1 res.spacing = state.spacing - res.text = text + res.text = text - res.str = 0 + res.str = 0 res.next = 0 - res.end = len(text) + res.end = len(text) return } // step through each codepoint TextIterNext :: proc( - ctx: ^FontContext, + ctx: ^FontContext, iter: ^TextIter, quad: ^Quad, ) -> (ok: bool) { @@ -1097,13 +1067,12 @@ TextIterNext :: proc( iter.x = iter.nextx iter.y = iter.nexty iter.codepointCount += 1 - glyph := __getGlyph(ctx, iter.font, iter.codepoint, iter.isize, iter.iblur) - - if glyph != nil { + if glyph, ok := __getGlyph(ctx, iter.font, iter.codepoint, iter.isize, iter.iblur); ok { __getQuad(ctx, iter.font, iter.previousGlyphIndex, glyph, iter.scale, iter.spacing, &iter.nextx, &iter.nexty, quad) + iter.previousGlyphIndex = glyph.index + } else { + iter.previousGlyphIndex = -1 } - - iter.previousGlyphIndex = glyph == nil ? -1 : glyph.index ok = true break } @@ -1115,24 +1084,21 @@ TextIterNext :: proc( // width of a text line, optionally the full rect TextBounds :: proc( - ctx: ^FontContext, - text: string, - x: f32 = 0, - y: f32 = 0, + ctx: ^FontContext, + text: string, + x: f32 = 0, + y: f32 = 0, bounds: ^[4]f32 = nil, ) -> f32 { state := __getState(ctx) isize := i16(state.size * 10) iblur := i16(state.blur) - font := __getFont(ctx, state.font) + font := __getFont(ctx, state.font) // bunch of state - x := x - y := y - minx := x - maxx := x - miny := y - maxy := y + x, y := x, y + minx, maxx := x, x + miny, maxy := y, y start_x := x // iterate @@ -1143,9 +1109,7 @@ TextBounds :: proc( codepoint: rune for byte_offset in 0.. (ascender, descender, lineHeight: f32) { state := __getState(ctx) isize := i16(state.size * 10.0) - font := __getFont(ctx, state.font) - ascender = font.ascender * f32(isize / 10) - descender = font.descender * f32(isize / 10) + font := __getFont(ctx, state.font) + ascender = font.ascender * f32(isize / 10) + descender = font.descender * f32(isize / 10) lineHeight = font.lineHeight * f32(isize / 10) return } // reset to single state -BeginState :: proc(using ctx: ^FontContext) { - state_count = 0 +BeginState :: proc(ctx: ^FontContext) { + ctx.state_count = 0 PushState(ctx) ClearState(ctx) } @@ -1223,7 +1189,6 @@ EndState :: proc(using ctx: ^FontContext) { if callbackUpdate != nil { callbackUpdate(userData, dirtyRect, raw_data(textureData)) } - __dirtyRectReset(ctx) } } \ No newline at end of file From ebed66d4ceee206998500b604a664e72a3ba9539 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 28 Jun 2023 11:57:37 +0100 Subject: [PATCH 096/144] General code style clean up for `vendor:nanovg` --- vendor/nanovg/nanovg.odin | 864 ++++++++++++++++++-------------------- 1 file changed, 413 insertions(+), 451 deletions(-) diff --git a/vendor/nanovg/nanovg.odin b/vendor/nanovg/nanovg.odin index bc0335f6a..6b06e3534 100644 --- a/vendor/nanovg/nanovg.odin +++ b/vendor/nanovg/nanovg.odin @@ -10,21 +10,21 @@ import "core:fmt" import "../fontstash" import stbi "vendor:stb/image" -AlignVertical :: fontstash.AlignVertical +AlignVertical :: fontstash.AlignVertical AlignHorizontal :: fontstash.AlignHorizontal INIT_FONTIMAGE_SIZE :: 512 -MAX_FONTIMAGE_SIZE :: 2048 -MAX_FONTIMAGES :: 4 +MAX_FONTIMAGE_SIZE :: 2048 +MAX_FONTIMAGES :: 4 -MAX_STATES :: 32 +MAX_STATES :: 32 INIT_COMMANDS_SIZE :: 256 -INIT_POINTS_SIZE :: 128 -INIT_PATH_SIZE :: 16 -INIT_VERTS_SIZE :: 26 -KAPPA :: 0.5522847493 +INIT_POINTS_SIZE :: 128 +INIT_PATH_SIZE :: 16 +INIT_VERTS_SIZE :: 26 +KAPPA :: 0.5522847493 -Color :: [4]f32 +Color :: [4]f32 Matrix :: [6]f32 Vertex :: [4]f32 // x,y,u,v @@ -40,13 +40,13 @@ ImageFlag :: enum { ImageFlags :: bit_set[ImageFlag] Paint :: struct { - xform: Matrix, - extent: [2]f32, - radius: f32, - feather: f32, + xform: Matrix, + extent: [2]f32, + radius: f32, + feather: f32, innerColor: Color, outerColor: Color, - image: int, + image: int, } Winding :: enum { @@ -110,7 +110,7 @@ Texture :: enum { } ScissorT :: struct { - xform: Matrix, + xform: Matrix, extent: [2]f32, } @@ -131,83 +131,83 @@ PointFlag :: enum { PointFlags :: bit_set[PointFlag] Point :: struct { - x, y: f32, - dx, dy: f32, - len: f32, + x, y: f32, + dx, dy: f32, + len: f32, dmx, dmy: f32, - flags: PointFlags, + flags: PointFlags, } PathCache :: struct { points: [dynamic]Point, - paths: [dynamic]Path, - verts: [dynamic]Vertex, + paths: [dynamic]Path, + verts: [dynamic]Vertex, bounds: [4]f32, } Path :: struct { - first: int, - count: int, - closed: bool, - nbevel: int, - fill: []Vertex, - stroke: []Vertex, + first: int, + count: int, + closed: bool, + nbevel: int, + fill: []Vertex, + stroke: []Vertex, winding: Winding, - convex: bool, + convex: bool, } State :: struct { compositeOperation: CompositeOperationState, - shapeAntiAlias: bool, - fill: Paint, - stroke: Paint, - strokeWidth: f32, - miterLimit: f32, - lineJoin: LineCapType, - lineCap: LineCapType, - alpha: f32, - xform: Matrix, - scissor: ScissorT, + shapeAntiAlias: bool, + fill: Paint, + stroke: Paint, + strokeWidth: f32, + miterLimit: f32, + lineJoin: LineCapType, + lineCap: LineCapType, + alpha: f32, + xform: Matrix, + scissor: ScissorT, // font state - fontSize: f32, - letterSpacing: f32, - lineHeight: f32, - fontBlur: f32, + fontSize: f32, + letterSpacing: f32, + lineHeight: f32, + fontBlur: f32, alignHorizontal: AlignHorizontal, - alignVertical: AlignVertical, - fontId: int, + alignVertical: AlignVertical, + fontId: int, } Context :: struct { - params: Params, - commands: [dynamic]f32, - commandx, commandy: f32, - states: [MAX_STATES]State, - nstates: int, - cache: PathCache, - tessTol: f32, - distTol: f32, - fringeWidth: f32, + params: Params, + commands: [dynamic]f32, + commandx, commandy: f32, + states: [MAX_STATES]State, + nstates: int, + cache: PathCache, + tessTol: f32, + distTol: f32, + fringeWidth: f32, devicePxRatio: f32, // font - fs: fontstash.FontContext, - fontImages: [MAX_FONTIMAGES]int, + fs: fontstash.FontContext, + fontImages: [MAX_FONTIMAGES]int, fontImageIdx: int, // stats - drawCallCount: int, - fillTriCount: int, + drawCallCount: int, + fillTriCount: int, strokeTriCount: int, - textTriCount: int, + textTriCount: int, // flush texture textureDirty: bool, } Params :: struct { - userPtr: rawptr, + userPtr: rawptr, edgeAntiAlias: bool, // callbacks to fill out @@ -216,19 +216,19 @@ Params :: struct { // textures calls renderCreateTexture: proc( - uptr: rawptr, - type: Texture, - w, h: int, + uptr: rawptr, + type: Texture, + w, h: int, imageFlags: ImageFlags, - data: []byte, + data: []byte, ) -> int, renderDeleteTexture: proc(uptr: rawptr, image: int) -> bool, renderUpdateTexture: proc( - uptr: rawptr, + uptr: rawptr, image: int, - x, y: int, - w, h: int, - data: []byte, + x, y: int, + w, h: int, + data: []byte, ) -> bool, renderGetTextureSize: proc(uptr: rawptr, image: int, w, h: ^int) -> bool, @@ -237,37 +237,37 @@ Params :: struct { renderCancel: proc(uptr: rawptr), renderFlush: proc(uptr: rawptr), renderFill: proc( - uptr: rawptr, - paint: ^Paint, + uptr: rawptr, + paint: ^Paint, compositeOperation: CompositeOperationState, - scissor: ^ScissorT, - fringe: f32, - bounds: [4]f32, - paths: []Path, + scissor: ^ScissorT, + fringe: f32, + bounds: [4]f32, + paths: []Path, ), renderStroke: proc( - uptr: rawptr, - paint: ^Paint, + uptr: rawptr, + paint: ^Paint, compositeOperation: CompositeOperationState, - scissor: ^ScissorT, - fringe: f32, - strokeWidth: f32, - paths: []Path, + scissor: ^ScissorT, + fringe: f32, + strokeWidth: f32, + paths: []Path, ), renderTriangles: proc( - uptr: rawptr, - paint: ^Paint, + uptr: rawptr, + paint: ^Paint, compositeOperation: CompositeOperationState, - scissor: ^ScissorT, - verts: []Vertex, - fringe: f32, + scissor: ^ScissorT, + verts: []Vertex, + fringe: f32, ), } __allocPathCache :: proc(c: ^PathCache) { c.points = make([dynamic]Point, 0, INIT_POINTS_SIZE) - c.paths = make([dynamic]Path, 0, INIT_PATH_SIZE) - c.verts = make([dynamic]Vertex, 0, INIT_VERTS_SIZE) + c.paths = make([dynamic]Path, 0, INIT_PATH_SIZE) + c.verts = make([dynamic]Vertex, 0, INIT_VERTS_SIZE) } __deletePathCache :: proc(c: PathCache) { @@ -277,14 +277,14 @@ __deletePathCache :: proc(c: PathCache) { } __setDevicePxRatio :: proc(ctx: ^Context, ratio: f32) { - ctx.tessTol = 0.25 / ratio - ctx.distTol = 0.01 / ratio - ctx.fringeWidth = 1.0 / ratio + ctx.tessTol = 0.25 / ratio + ctx.distTol = 0.01 / ratio + ctx.fringeWidth = 1.0 / ratio ctx.devicePxRatio = ratio } __getState :: #force_inline proc(ctx: ^Context) -> ^State #no_bounds_check { - return &ctx.states[ctx.nstates - 1] + return &ctx.states[ctx.nstates-1] } CreateInternal :: proc(params: Params) -> (ctx: ^Context) { @@ -311,7 +311,7 @@ CreateInternal :: proc(params: Params) -> (ctx: ^Context) { // handle to the image needs to be set to the new generated texture ctx.fs.callbackResize = proc(data: rawptr, w, h: int) { - ctx := cast(^Context) data + ctx := (^Context)(data) ctx.fontImages[0] = ctx.params.renderCreateTexture(ctx.params.userPtr, .Alpha, w, h, {}, ctx.fs.textureData) } @@ -326,7 +326,7 @@ DeleteInternal :: proc(ctx: ^Context) { __deletePathCache(ctx.cache) fontstash.Destroy(&ctx.fs) - for &image in ctx.fontImages { + for image in ctx.fontImages { if image != 0 { DeleteImage(ctx, image) } @@ -350,9 +350,9 @@ DeleteInternal :: proc(ctx: ^Context) { devicePixelRatio to: frameBufferWidth / windowWidth. */ BeginFrame :: proc( - ctx: ^Context, - windowWidth: f32, - windowHeight: f32, + ctx: ^Context, + windowWidth: f32, + windowHeight: f32, devicePixelRatio: f32, ) { ctx.nstates = 0 @@ -371,9 +371,9 @@ BeginFrame :: proc( @(deferred_out=EndFrame) FrameScoped :: proc( - ctx: ^Context, - windowWidth: f32, - windowHeight: f32, + ctx: ^Context, + windowWidth: f32, + windowHeight: f32, devicePixelRatio: f32, ) -> ^Context { BeginFrame(ctx, windowWidth, windowHeight, devicePixelRatio) @@ -454,7 +454,7 @@ RGBA :: proc(r, g, b, a: u8) -> (res: Color) { LerpRGBA :: proc(c0, c1: Color, u: f32) -> (cint: Color) { clamped := clamp(u, 0.0, 1.0) oneminu := 1.0 - clamped - for i in 0..<4 { + for _, i in cint { cint[i] = c0[i] * oneminu + c1[i] * clamped } @@ -510,12 +510,9 @@ HSLA :: proc(hue, saturation, lightness: f32, a: u8) -> (col: Color) { // hex to 0xAARRGGBB color ColorHex :: proc(color: u32) -> (res: Color) { color := color - res.b = f32(0x000000FF & color) / 255 - color = color >> 8 - res.g = f32(0x000000FF & color) / 255 - color = color >> 8 - res.r = f32(0x000000FF & color) / 255 - color = color >> 8 + res.b = f32(0x000000FF & color) / 255; color >>= 8 + res.g = f32(0x000000FF & color) / 255; color >>= 8 + res.r = f32(0x000000FF & color) / 255; color >>= 8 res.a = f32(0x000000FF & color) / 255 return } @@ -634,7 +631,7 @@ TransformInverse :: proc(inv: ^Matrix, t: Matrix) -> bool { TransformPoint :: proc( dx: ^f32, dy: ^f32, - t: Matrix, + t: Matrix, sx: f32, sy: f32, ) { @@ -667,7 +664,7 @@ Save :: proc(ctx: ^Context) { // copy prior if ctx.nstates > 0 { - ctx.states[ctx.nstates] = ctx.states[ctx.nstates - 1] + ctx.states[ctx.nstates] = ctx.states[ctx.nstates-1] } ctx.nstates += 1 @@ -691,8 +688,8 @@ SaveScoped :: #force_inline proc(ctx: ^Context) { __setPaintColor :: proc(p: ^Paint, color: Color) { p^ = {} TransformIdentity(&p.xform) - p.radius = 0 - p.feather = 1 + p.radius = 0 + p.feather = 1 p.innerColor = color p.outerColor = color } @@ -706,25 +703,25 @@ Reset :: proc(ctx: ^Context) { __setPaintColor(&state.stroke, RGBA(0, 0, 0, 255)) state.compositeOperation = __compositeOperationState(.SOURCE_OVER) - state.shapeAntiAlias = true - state.strokeWidth = 1 - state.miterLimit = 10 - state.lineCap = .BUTT - state.lineJoin = .MITER - state.alpha = 1 + state.shapeAntiAlias = true + state.strokeWidth = 1 + state.miterLimit = 10 + state.lineCap = .BUTT + state.lineJoin = .MITER + state.alpha = 1 TransformIdentity(&state.xform) state.scissor.extent[0] = -1 state.scissor.extent[1] = -1 // font settings - state.fontSize = 16 - state.letterSpacing = 0 - state.lineHeight = 1 - state.fontBlur = 0 + state.fontSize = 16 + state.letterSpacing = 0 + state.lineHeight = 1 + state.fontBlur = 0 state.alignHorizontal = .LEFT - state.alignVertical = .BASELINE - state.fontId = 0 + state.alignVertical = .BASELINE + state.fontId = 0 } /////////////////////////////////////////////////////////// @@ -817,7 +814,7 @@ FillPaint :: proc(ctx: ^Context, paint: Paint) { Transform :: proc(ctx: ^Context, a, b, c, d, e, f: f32) { state := __getState(ctx) - TransformPremultiply(&state.xform, { a, b, c, d, e, f }) + TransformPremultiply(&state.xform, {a, b, c, d, e, f}) } // Resets current transform to a identity matrix. @@ -874,10 +871,10 @@ Scale :: proc(ctx: ^Context, x, y: f32) { There should be space for 6 floats in the return buffer for the values a-f. */ CurrentTransform :: proc(ctx: ^Context, xform: ^Matrix) { - state := __getState(ctx) if xform == nil { return } + state := __getState(ctx) xform^ = state.xform } @@ -901,7 +898,7 @@ CreateImagePath :: proc(ctx: ^Context, filename: cstring, imageFlags: ImageFlags return 0 } - data := mem.slice_ptr(img, int(w) * int(h) * int(n)) + data := img[:int(w) * int(h) * int(n)] image := CreateImageRGBA(ctx, int(w), int(h), imageFlags, data) stbi.image_free(img) return image @@ -919,13 +916,13 @@ CreateImageMem :: proc(ctx: ^Context, data: []byte, imageFlags: ImageFlags) -> i return 0 } - pixel_data := mem.slice_ptr(img, int(w) * int(h) * int(n)) - image := CreateImageRGBA(ctx, int(w), int(h), imageFlags, pixel_data) + data := img[:int(w) * int(h) * int(n)] + image := CreateImageRGBA(ctx, int(w), int(h), imageFlags, data) stbi.image_free(img) return image } -CreateImage :: proc { CreateImagePath, CreateImageMem } +CreateImage :: proc{CreateImagePath, CreateImageMem} // Creates image from specified image data. // Returns handle to the image. @@ -980,8 +977,8 @@ DeleteImage :: proc(ctx: ^Context, image: int) { LinearGradient :: proc( sx, sy: f32, ex, ey: f32, - icol: Color, - ocol: Color, + icol: Color, + ocol: Color, ) -> (p: Paint) { LARGE :: f32(1e5) @@ -1024,10 +1021,10 @@ LinearGradient :: proc( */ RadialGradient :: proc( cx, cy: f32, - inr: f32, - outr: f32, - icol: Color, - ocol: Color, + inr: f32, + outr: f32, + icol: Color, + ocol: Color, ) -> (p: Paint) { r := (inr+outr)*0.5 f := (outr-inr) @@ -1056,8 +1053,8 @@ RadialGradient :: proc( BoxGradient :: proc( x, y: f32, w, h: f32, - r: f32, - f: f32, + r: f32, + f: f32, icol: Color, ocol: Color, ) -> (p: Paint) { @@ -1084,10 +1081,10 @@ BoxGradient :: proc( */ ImagePattern :: proc( cx, cy: f32, - w, h: f32, - angle: f32, - image: int, - alpha: f32, + w, h: f32, + angle: f32, + image: int, + alpha: f32, ) -> (p: Paint) { TransformRotate(&p.xform, angle) p.xform[4] = cx @@ -1097,7 +1094,7 @@ ImagePattern :: proc( p.extent[1] = h p.image = image - p.innerColor = { 1,1,1,alpha } + p.innerColor = {1, 1, 1, alpha} p.outerColor = p.innerColor return @@ -1113,8 +1110,8 @@ ImagePattern :: proc( // Sets the current scissor rectangle. // The scissor rectangle is transformed by the current transform. Scissor :: proc( - ctx: ^Context, - x, y: f32, + ctx: ^Context, + x, y: f32, width, height: f32, ) { state := __getState(ctx) @@ -1139,11 +1136,11 @@ Scissor :: proc( transform space. The resulting shape is always rectangle. */ IntersectScissor :: proc( - ctx: ^Context, + ctx: ^Context, x, y, w, h: f32, ) { isect_rects :: proc( - dst: ^[4]f32, + dst: ^[4]f32, ax, ay, aw, ah: f32, bx, by, bw, bh: f32, ) { @@ -1158,8 +1155,6 @@ IntersectScissor :: proc( } state := __getState(ctx) - pxform: Matrix - invxorm: Matrix // If no previous scissor has been set, set the scissor as current scissor. if state.scissor.extent[0] < 0 { @@ -1167,9 +1162,11 @@ IntersectScissor :: proc( return } - pxform = state.scissor.xform + pxform := state.scissor.xform ex := state.scissor.extent[0] ey := state.scissor.extent[1] + + invxorm: Matrix TransformInverse(&invxorm, state.xform) TransformMultiply(&pxform, invxorm) tex := ex * abs(pxform[0]) + ey * abs(pxform[2]) @@ -1197,27 +1194,26 @@ ResetScissor :: proc(ctx: ^Context) { /////////////////////////////////////////////////////////// // state table instead of if else chains -OP_STATE_TABLE :: [CompositeOperation][2]BlendFactor { - .SOURCE_OVER = { .ONE, .ONE_MINUS_SRC_ALPHA }, - .SOURCE_IN = { .DST_ALPHA, .ZERO }, - .SOURCE_OUT = { .ONE_MINUS_DST_ALPHA, .ZERO }, - .ATOP = { .DST_ALPHA, .ONE_MINUS_SRC_ALPHA }, +OP_STATE_TABLE := [CompositeOperation][2]BlendFactor { + .SOURCE_OVER = {.ONE, .ONE_MINUS_SRC_ALPHA}, + .SOURCE_IN = {.DST_ALPHA, .ZERO}, + .SOURCE_OUT = {.ONE_MINUS_DST_ALPHA, .ZERO}, + .ATOP = {.DST_ALPHA, .ONE_MINUS_SRC_ALPHA}, - .DESTINATION_OVER = { .ONE_MINUS_DST_ALPHA, .ONE }, - .DESTINATION_IN = { .ZERO, .SRC_ALPHA }, - .DESTINATION_OUT = { .ZERO, .ONE_MINUS_SRC_ALPHA }, - .DESTINATION_ATOP = { .ONE_MINUS_DST_ALPHA, .SRC_ALPHA }, + .DESTINATION_OVER = {.ONE_MINUS_DST_ALPHA, .ONE}, + .DESTINATION_IN = {.ZERO, .SRC_ALPHA}, + .DESTINATION_OUT = {.ZERO, .ONE_MINUS_SRC_ALPHA}, + .DESTINATION_ATOP = {.ONE_MINUS_DST_ALPHA, .SRC_ALPHA}, - .LIGHTER = { .ONE, .ONE }, - .COPY = { .ONE, .ZERO }, - .XOR = { .ONE_MINUS_DST_ALPHA, .ONE_MINUS_SRC_ALPHA }, + .LIGHTER = {.ONE, .ONE}, + .COPY = {.ONE, .ZERO}, + .XOR = {.ONE_MINUS_DST_ALPHA, .ONE_MINUS_SRC_ALPHA}, } __compositeOperationState :: proc(op: CompositeOperation) -> (res: CompositeOperationState) { - table := OP_STATE_TABLE - factors := table[op] - res.srcRGB = factors.x - res.dstRGB = factors.y + factors := OP_STATE_TABLE[op] + res.srcRGB = factors.x + res.dstRGB = factors.y res.srcAlpha = factors.x res.dstAlpha = factors.y return @@ -1236,20 +1232,19 @@ GlobalCompositeBlendFunc :: proc(ctx: ^Context, sfactor, dfactor: BlendFactor) { // Sets the composite operation with custom pixel arithmetic for RGB and alpha components separately. The parameters should be one of NVGblendFactor. GlobalCompositeBlendFuncSeparate :: proc( - ctx: ^Context, - srcRGB: BlendFactor, - dstRGB: BlendFactor, + ctx: ^Context, + srcRGB: BlendFactor, + dstRGB: BlendFactor, srcAlpha: BlendFactor, dstAlpha: BlendFactor, ) { - op := CompositeOperationState { + state := __getState(ctx) + state.compositeOperation = CompositeOperationState{ srcRGB, dstRGB, srcAlpha, dstAlpha, } - state := __getState(ctx) - state.compositeOperation = op } /////////////////////////////////////////////////////////// @@ -1277,46 +1272,38 @@ __distPtSeg :: proc(x, y, px, py, qx, qy: f32) -> f32 { if d > 0 { t /= d } - - if t < 0 { - t = 0 - } else if t > 1 { - t = 1 - } + t = clamp(t, 0, 1) dx = px + t * pqx - x dy = py + t * pqy - y return dx * dx + dy * dy } -__appendCommands :: proc(ctx: ^Context, values: []f32) { +__appendCommands :: proc(ctx: ^Context, values: ..f32) { state := __getState(ctx) if Commands(values[0]) != .CLOSE && Commands(values[0]) != .WINDING { - ctx.commandx = values[len(values) - 2] - ctx.commandy = values[len(values) - 1] + ctx.commandx = values[len(values)-2] + ctx.commandy = values[len(values)-1] } - - i := 0 - for i < len(values) { + for i := 0; i < len(values); /**/ { cmd := Commands(values[i]) switch cmd { case .MOVE_TO, .LINE_TO: - TransformPoint(&values[i + 1], &values[i + 2], state.xform, values[i + 1], values[i + 2]) + TransformPoint(&values[i+1], &values[i+2], state.xform, values[i+1], values[i+2]) i += 3 - case .BEZIER_TO: - TransformPoint(&values[i + 1], &values[i + 2], state.xform, values[i + 1], values[i + 2]) - TransformPoint(&values[i + 3], &values[i + 4], state.xform, values[i + 3], values[i + 4]) - TransformPoint(&values[i + 5], &values[i + 6], state.xform, values[i + 5], values[i + 6]) + TransformPoint(&values[i+1], &values[i+2], state.xform, values[i+1], values[i+2]) + TransformPoint(&values[i+3], &values[i+4], state.xform, values[i+3], values[i+4]) + TransformPoint(&values[i+5], &values[i+6], state.xform, values[i+5], values[i+6]) i += 7 - - case .CLOSE: i += 1 - case .WINDING: i += 2 - - // default - case: i += 1 + case .CLOSE: + i += 1 + case .WINDING: + i += 2 + case: + i += 1 } } @@ -1331,22 +1318,22 @@ __clearPathCache :: proc(ctx: ^Context) { __lastPath :: proc(ctx: ^Context) -> ^Path { if len(ctx.cache.paths) > 0 { - return &ctx.cache.paths[len(ctx.cache.paths) - 1] + return &ctx.cache.paths[len(ctx.cache.paths)-1] } return nil } __addPath :: proc(ctx: ^Context) { - append(&ctx.cache.paths, Path { - first = len(ctx.cache.points), + append(&ctx.cache.paths, Path{ + first = len(ctx.cache.points), winding = .CCW, }) } __lastPoint :: proc(ctx: ^Context) -> ^Point { if len(ctx.cache.paths) > 0 { - return &ctx.cache.points[len(ctx.cache.points) - 1] + return &ctx.cache.points[len(ctx.cache.points)-1] } return nil @@ -1368,7 +1355,7 @@ __addPoint :: proc(ctx: ^Context, x, y: f32, flags: PointFlags) { } } - append(&ctx.cache.points, Point { + append(&ctx.cache.points, Point{ x = x, y = y, flags = flags, @@ -1415,7 +1402,7 @@ __polyArea :: proc(points: []Point) -> f32 { for i := 2; i < len(points); i += 1 { a := &points[0] - b := &points[i - 1] + b := &points[i-1] c := &points[i] area += __triarea2(a.x, a.y, b.x, b.y, c.x, c.y) } @@ -1448,24 +1435,24 @@ __normalize :: proc(x, y: ^f32) -> f32 { } __tesselateBezier :: proc( - ctx: ^Context, + ctx: ^Context, x1, y1: f32, x2, y2: f32, x3, y3: f32, x4, y4: f32, - level: int, - flags: PointFlags, + level: int, + flags: PointFlags, ) { if level > 10 { return } - x12 := (x1 + x2) * 0.5 - y12 := (y1 + y2) * 0.5 - x23 := (x2 + x3) * 0.5 - y23 := (y2 + y3) * 0.5 - x34 := (x3 + x4) * 0.5 - y34 := (y3 + y4) * 0.5 + x12 := (x1 + x2) * 0.5 + y12 := (y1 + y2) * 0.5 + x23 := (x2 + x3) * 0.5 + y23 := (y2 + y3) * 0.5 + x34 := (x3 + x4) * 0.5 + y34 := (y3 + y4) * 0.5 x123 := (x12 + x23) * 0.5 y123 := (y12 + y23) * 0.5 @@ -1479,8 +1466,8 @@ __tesselateBezier :: proc( return } - x234 := (x23 + x34) * 0.5 - y234 := (y23 + y34) * 0.5 + x234 := (x23 + x34) * 0.5 + y234 := (y23 + y34) * 0.5 x1234 := (x123 + x234) * 0.5 y1234 := (y123 + y234) * 0.5 @@ -1504,22 +1491,20 @@ __flattenPaths :: proc(ctx: ^Context) { case .MOVE_TO: __addPath(ctx) p := ctx.commands[i + 1:] - __addPoint(ctx, p[0], p[1], { .CORNER }) + __addPoint(ctx, p[0], p[1], {.CORNER}) i += 3 case .LINE_TO: p := ctx.commands[i + 1:] - __addPoint(ctx, p[0], p[1], { .CORNER }) + __addPoint(ctx, p[0], p[1], {.CORNER}) i += 3 case .BEZIER_TO: - last := __lastPoint(ctx) - - if last != nil { + if last := __lastPoint(ctx); last != nil { cp1 := ctx.commands[i + 1:] cp2 := ctx.commands[i + 3:] p := ctx.commands[i + 5:] - __tesselateBezier(ctx, last.x,last.y, cp1[0],cp1[1], cp2[0],cp2[1], p[0],p[1], 0, { .CORNER }) + __tesselateBezier(ctx, last.x,last.y, cp1[0],cp1[1], cp2[0],cp2[1], p[0],p[1], 0, {.CORNER}) } i += 7 @@ -1542,8 +1527,7 @@ __flattenPaths :: proc(ctx: ^Context) { cache.bounds[3] = -1e6 // Calculate the direction and length of line segments. - for j in 0.. 0.000001) { + if dmr2 > 0.000001 { scale := 1.0 / dmr2 - if (scale > 600.0) { + if scale > 600.0 { scale = 600.0 } p1.dmx *= scale @@ -1901,25 +1875,25 @@ __calculateJoins :: proc( } // Clear flags, but keep the corner. - p1.flags = (.CORNER in p1.flags) ? { .CORNER } : {} + p1.flags = {.CORNER} if .CORNER in p1.flags else nil // Keep track of left turns. __cross = p1.dx * p0.dy - p0.dx * p1.dy if __cross > 0.0 { nleft += 1 - incl(&p1.flags, PointFlag.LEFT) + p1.flags += {.LEFT} } // Calculate if we should use bevel or miter for inner join. limit = max(1.01, min(p0.len, p1.len) * iw) if (dmr2 * limit * limit) < 1.0 { - incl(&p1.flags, PointFlag.INNER_BEVEL) + p1.flags += {.INNER_BEVEL} } // Check to see if the corner needs to be beveled. if .CORNER in p1.flags { if (dmr2 * miterLimit*miterLimit) < 1.0 || lineJoin == .BEVEL || lineJoin == .ROUND { - incl(&p1.flags, PointFlag.BEVEL) + p1.flags += {.BEVEL} } } @@ -1942,11 +1916,11 @@ __allocTempVerts :: proc(ctx: ^Context, nverts: int) -> []Vertex { } __expandStroke :: proc( - ctx: ^Context, - w: f32, - fringe: f32, - lineCap: LineCapType, - lineJoin: LineCapType, + ctx: ^Context, + w: f32, + fringe: f32, + lineCap: LineCapType, + lineJoin: LineCapType, miterLimit: f32, ) -> bool { cache := &ctx.cache @@ -1968,7 +1942,7 @@ __expandStroke :: proc( // Calculate max vertex usage. cverts := 0 - for &path in cache.paths { + for path in cache.paths { loop := path.closed // TODO check if f32 calculation necessary? @@ -1991,8 +1965,7 @@ __expandStroke :: proc( verts := __allocTempVerts(ctx, cverts) dst_index: int - for i in 0.. bool { cache := &ctx.cache @@ -2097,7 +2070,7 @@ __expandFill :: proc( // Calculate max vertex usage. cverts := 0 - for &path in cache.paths { + for path in cache.paths { cverts += path.count + path.nbevel + 1 if fringe { @@ -2125,7 +2098,7 @@ __expandFill :: proc( p0 = &pts[path.count-1] p1 = &pts[0] - for j in 0.. f32 { font := fontstash.__getFont(fs, state.fontId) isize := i16(fstate.size * 10) iblur := i16(fstate.blur) - glyph := fontstash.__getGlyph(fs, font, codepoint, isize, iblur) + glyph, _ := fontstash.__getGlyph(fs, font, codepoint, isize, iblur) fscale := fontstash.__getPixelHeightScale(font, f32(isize) / 10) // transform x / y @@ -2910,12 +2872,12 @@ TextIcon :: proc(ctx: ^Context, xpos, ypos: f32, codepoint: rune) -> f32 { TransformPoint(&c[6], &c[7], state.xform, q.x0 * invscale, q.y1 * invscale) // Create triangles - verts[0] = { c[0], c[1], q.s0, q.t0 } - verts[1] = { c[4], c[5], q.s1, q.t1 } - verts[2] = { c[2], c[3], q.s1, q.t0 } - verts[3] = { c[0], c[1], q.s0, q.t0 } - verts[4] = { c[6], c[7], q.s0, q.t1 } - verts[5] = { c[4], c[5], q.s1, q.t1 } + verts[0] = {c[0], c[1], q.s0, q.t0} + verts[1] = {c[4], c[5], q.s1, q.t1} + verts[2] = {c[2], c[3], q.s1, q.t0} + verts[3] = {c[0], c[1], q.s0, q.t0} + verts[4] = {c[6], c[7], q.s0, q.t1} + verts[5] = {c[4], c[5], q.s1, q.t1} ctx.textureDirty = true __renderText(ctx, verts[:]) @@ -2986,12 +2948,12 @@ Text :: proc(ctx: ^Context, x, y: f32, text: string) -> f32 { // Create triangles if nverts + 6 <= cverts { - verts[nverts] = { c[0], c[1], q.s0, q.t0 } - verts[nverts + 1] = { c[4], c[5], q.s1, q.t1 } - verts[nverts + 2] = { c[2], c[3], q.s1, q.t0 } - verts[nverts + 3] = { c[0], c[1], q.s0, q.t0 } - verts[nverts + 4] = { c[6], c[7], q.s0, q.t1 } - verts[nverts + 5] = { c[4], c[5], q.s1, q.t1 } + verts[nverts+0] = {c[0], c[1], q.s0, q.t0} + verts[nverts+1] = {c[4], c[5], q.s1, q.t1} + verts[nverts+2] = {c[2], c[3], q.s1, q.t0} + verts[nverts+3] = {c[0], c[1], q.s0, q.t0} + verts[nverts+4] = {c[6], c[7], q.s0, q.t1} + verts[nverts+5] = {c[4], c[5], q.s1, q.t1} nverts += 6 } } @@ -3033,17 +2995,17 @@ TextMetrics :: proc(ctx: ^Context) -> (ascender, descender, lineHeight: f32) { // Returns the horizontal advance of the measured text (i.e. where the next character should drawn). // Measured values are returned in local coordinate space. TextBounds :: proc( - ctx: ^Context, - x, y: f32, - input: string, + ctx: ^Context, + x, y: f32, + input: string, bounds: ^[4]f32 = nil, ) -> (advance: f32) { - state := __getState(ctx) - scale := __getFontScale(state) * ctx.devicePxRatio + state := __getState(ctx) + scale := __getFontScale(state) * ctx.devicePxRatio invscale := f32(1.0) / scale if state.fontId == -1 { - return {} + return 0 } fs := &ctx.fs @@ -3055,11 +3017,11 @@ TextBounds :: proc( fontstash.SetFont(fs, state.fontId) width := fontstash.TextBounds(fs, input, x * scale, y * scale, bounds) - - // Use line bounds for height. - one, two := fontstash.LineBounds(fs, y * scale) if bounds != nil { + // Use line bounds for height. + one, two := fontstash.LineBounds(fs, y * scale) + bounds[1] = one bounds[3] = two bounds[0] *= invscale @@ -3073,10 +3035,10 @@ TextBounds :: proc( // text row with relative byte offsets into a string Text_Row :: struct { - start: int, - end: int, - next: int, - width: f32, + start: int, + end: int, + next: int, + width: f32, minx, maxx: f32, } @@ -3091,10 +3053,10 @@ Codepoint_Type :: enum { // White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. // Words longer than the max width are slit at nearest character (i.e. no hyphenation). TextBox :: proc( - ctx: ^Context, - x, y: f32, + ctx: ^Context, + x, y: f32, break_row_width: f32, - input: string, + input: string, ) { state := __getState(ctx) rows: [2]Text_Row @@ -3112,8 +3074,7 @@ TextBox :: proc( y := y input := input for nrows, input_last in TextBreakLines(ctx, &input, break_row_width, &rows_mod) { - for i in 0.. (nrows: int, last: string, ok: bool) { state := __getState(ctx) scale := __getFontScale(state) * ctx.devicePxRatio @@ -3144,7 +3105,7 @@ TextBreakLines :: proc( word_start_x, word_min_x: f32 break_width, break_max_x: f32 - type := Codepoint_Type.Space + type := Codepoint_Type.Space ptype := Codepoint_Type.Space pcodepoint: rune @@ -3160,9 +3121,10 @@ TextBreakLines :: proc( fontstash.SetAlignVertical(fs, state.alignVertical) fontstash.SetFont(fs, state.fontId) - break_x := break_row_width * scale - iter := fontstash.TextIterInit(fs, 0, 0, text^) + break_x := break_row_width * scale + iter := fontstash.TextIterInit(fs, 0, 0, text^) prev_iter := iter + q: fontstash.Quad stopped_early: bool @@ -3179,36 +3141,37 @@ TextBreakLines :: proc( type = .Space case '\n': - type = pcodepoint == 13 ? .Space : .Newline + type = .Space if pcodepoint == 13 else .Newline case '\r': - type = pcodepoint == 10 ? .Space : .Newline + type = .Space if pcodepoint == 10 else .Newline case 0x0085: // NEL type = .Newline case: - if (iter.codepoint >= 0x4E00 && iter.codepoint <= 0x9FFF) || - (iter.codepoint >= 0x3000 && iter.codepoint <= 0x30FF) || - (iter.codepoint >= 0xFF00 && iter.codepoint <= 0xFFEF) || - (iter.codepoint >= 0x1100 && iter.codepoint <= 0x11FF) || - (iter.codepoint >= 0x3130 && iter.codepoint <= 0x318F) || - (iter.codepoint >= 0xAC00 && iter.codepoint <= 0xD7AF) { + switch iter.codepoint { + case 0x4E00..=0x9FFF, + 0x3000..=0x30FF, + 0xFF00..=0xFFEF, + 0x1100..=0x11FF, + 0x3130..=0x318F, + 0xAC00..=0xD7AF: type = .CJK - } else { + case: type = .Char } } if type == .Newline { // Always handle new lines. - rows[nrows].start = row_start != -1 ? row_start : iter.str - rows[nrows].end = row_end != -1 ? row_end : iter.str + rows[nrows].start = row_start if row_start != -1 else iter.str + rows[nrows].end = row_end if row_end != -1 else iter.str rows[nrows].width = row_width * invscale - rows[nrows].minx = row_min_x * invscale - rows[nrows].maxx = row_max_x * invscale - rows[nrows].next = iter.next + rows[nrows].minx = row_min_x * invscale + rows[nrows].maxx = row_max_x * invscale + rows[nrows].next = iter.next nrows += 1 if nrows >= max_rows { @@ -3270,7 +3233,7 @@ TextBreakLines :: proc( // Break to new line when a character is beyond break width. if (type == .Char || type == .CJK) && next_width > break_x { // The run length is too long, need to break to new line. - if (break_end == row_start) { + if break_end == row_start { // The current word is longer than the row length, just break it from here. rows[nrows].start = row_start rows[nrows].end = iter.str @@ -3341,7 +3304,7 @@ TextBreakLines :: proc( // NOTE a bit hacky, row.start / row.end need to work with last string range last = text^ // advance early - next := rows[nrows - 1].next + next := rows[nrows-1].next text^ = text[next:] // terminate the for loop on non ok ok = nrows != 0 @@ -3353,11 +3316,11 @@ TextBreakLines :: proc( // if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] // Measured values are returned in local coordinate space. TextBoxBounds :: proc( - ctx: ^Context, - x, y: f32, + ctx: ^Context, + x, y: f32, breakRowWidth: f32, - input: string, - bounds: ^[4]f32, + input: string, + bounds: ^[4]f32, ) { state := __getState(ctx) rows: [2]Text_Row @@ -3398,15 +3361,14 @@ TextBoxBounds :: proc( y := y for nrows, input_last in TextBreakLines(ctx, &input, breakRowWidth, &rows_mod) { - for i in 0.. int { state := __getState(ctx) From 20e954a864ecd2e9da8d5df23739f1b83c59eb9a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 28 Jun 2023 12:16:49 +0100 Subject: [PATCH 097/144] Add `math.hypot` --- core/math/math.odin | 68 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/core/math/math.odin b/core/math/math.odin index 05177378f..70ffc1a35 100644 --- a/core/math/math.odin +++ b/core/math/math.odin @@ -2158,6 +2158,74 @@ signbit :: proc{ } +@(require_results) +hypot_f16 :: proc "contextless" (x, y: f16) -> (r: f16) { + switch { + case is_nan(x) || is_nan(y): + r = nan_f16() + case x == y: + r = x + case x == 0: + r = copy_sign_f16(1, y) + case (y > x) == (x > 0): + r = transmute(f16)(transmute(u16)x + 1) + case: + r = transmute(f16)(transmute(u16)x - 1) + } + return +} +@(require_results) +hypot_f32 :: proc "contextless" (x, y: f32) -> (r: f32) { + switch { + case is_nan(x) || is_nan(y): + r = nan_f32() + case x == y: + r = x + case x == 0: + r = copy_sign_f32(1, y) + case (y > x) == (x > 0): + r = transmute(f32)(transmute(u32)x + 1) + case: + r = transmute(f32)(transmute(u32)x - 1) + } + return +} +@(require_results) +hypot_f64 :: proc "contextless" (x, y: f64) -> (r: f64) { + switch { + case is_nan(x) || is_nan(y): + r = nan_f64() + case x == y: + r = x + case x == 0: + r = copy_sign_f64(1, y) + case (y > x) == (x > 0): + r = transmute(f64)(transmute(u64)x + 1) + case: + r = transmute(f64)(transmute(u64)x - 1) + } + return +} +@(require_results) hypot_f16le :: proc "contextless" (x, y: f16le) -> (r: f16le) { return f16le(hypot_f16(f16(x), f16(y))) } +@(require_results) hypot_f16be :: proc "contextless" (x, y: f16be) -> (r: f16be) { return f16be(hypot_f16(f16(x), f16(y))) } +@(require_results) hypot_f32le :: proc "contextless" (x, y: f32le) -> (r: f32le) { return f32le(hypot_f32(f32(x), f32(y))) } +@(require_results) hypot_f32be :: proc "contextless" (x, y: f32be) -> (r: f32be) { return f32be(hypot_f32(f32(x), f32(y))) } +@(require_results) hypot_f64le :: proc "contextless" (x, y: f64le) -> (r: f64le) { return f64le(hypot_f64(f64(x), f64(y))) } +@(require_results) hypot_f64be :: proc "contextless" (x, y: f64be) -> (r: f64be) { return f64be(hypot_f64(f64(x), f64(y))) } + +// hypot returns Sqrt(p*p + q*q), taking care to avoid unnecessary overflow and underflow. +// +// Special cases: +// hypot(±Inf, q) = +Inf +// hypot(p, ±Inf) = +Inf +// hypot(NaN, q) = NaN +// hypot(p, NaN) = NaN +hypot :: proc{ + hypot_f16, hypot_f16le, hypot_f16be, + hypot_f32, hypot_f32le, hypot_f32be, + hypot_f64, hypot_f64le, hypot_f64be, +} + F16_DIG :: 3 F16_EPSILON :: 0.00097656 F16_GUARD :: 0 From 866a9fdf192a5dcd54ae77ba797273e14899164a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 28 Jun 2023 12:19:45 +0100 Subject: [PATCH 098/144] Actually add math.hypot --- core/math/math.odin | 72 ++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/core/math/math.odin b/core/math/math.odin index 70ffc1a35..6f7a36bab 100644 --- a/core/math/math.odin +++ b/core/math/math.odin @@ -2160,51 +2160,57 @@ signbit :: proc{ @(require_results) hypot_f16 :: proc "contextless" (x, y: f16) -> (r: f16) { + p, q := abs(x), abs(y) switch { - case is_nan(x) || is_nan(y): - r = nan_f16() - case x == y: - r = x - case x == 0: - r = copy_sign_f16(1, y) - case (y > x) == (x > 0): - r = transmute(f16)(transmute(u16)x + 1) - case: - r = transmute(f16)(transmute(u16)x - 1) + case is_inf(p, 1) || is_inf(q, 1): + return inf_f16(1) + case is_nan(p) || is_nan(q): + return nan_f16() } - return + if p < q { + p, q = q, p + } + if p == 0 { + return 0 + } + q = q / p + return p * sqrt(1+q*q) } @(require_results) hypot_f32 :: proc "contextless" (x, y: f32) -> (r: f32) { + p, q := abs(x), abs(y) switch { - case is_nan(x) || is_nan(y): - r = nan_f32() - case x == y: - r = x - case x == 0: - r = copy_sign_f32(1, y) - case (y > x) == (x > 0): - r = transmute(f32)(transmute(u32)x + 1) - case: - r = transmute(f32)(transmute(u32)x - 1) + case is_inf(p, 1) || is_inf(q, 1): + return inf_f32(1) + case is_nan(p) || is_nan(q): + return nan_f32() } - return + if p < q { + p, q = q, p + } + if p == 0 { + return 0 + } + q = q / p + return p * sqrt(1+q*q) } @(require_results) hypot_f64 :: proc "contextless" (x, y: f64) -> (r: f64) { + p, q := abs(x), abs(y) switch { - case is_nan(x) || is_nan(y): - r = nan_f64() - case x == y: - r = x - case x == 0: - r = copy_sign_f64(1, y) - case (y > x) == (x > 0): - r = transmute(f64)(transmute(u64)x + 1) - case: - r = transmute(f64)(transmute(u64)x - 1) + case is_inf(p, 1) || is_inf(q, 1): + return inf_f64(1) + case is_nan(p) || is_nan(q): + return nan_f64() } - return + if p < q { + p, q = q, p + } + if p == 0 { + return 0 + } + q = q / p + return p * sqrt(1+q*q) } @(require_results) hypot_f16le :: proc "contextless" (x, y: f16le) -> (r: f16le) { return f16le(hypot_f16(f16(x), f16(y))) } @(require_results) hypot_f16be :: proc "contextless" (x, y: f16be) -> (r: f16be) { return f16be(hypot_f16(f16(x), f16(y))) } From abca3ceac8cdf4583e640b5a980b35e4bace07de Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 28 Jun 2023 12:19:57 +0100 Subject: [PATCH 099/144] Keep `-vet` happy --- examples/all/all_experimental.odin | 2 -- vendor/fontstash/fontstash.odin | 2 +- vendor/nanovg/nanovg.odin | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/all/all_experimental.odin b/examples/all/all_experimental.odin index 9679e0ca4..cd60c269c 100644 --- a/examples/all/all_experimental.odin +++ b/examples/all/all_experimental.odin @@ -3,8 +3,6 @@ package all import c_tokenizer "core:c/frontend/tokenizer" import c_preprocessor "core:c/frontend/preprocessor" -import virtual "core:mem/virtual" _ :: c_tokenizer _ :: c_preprocessor -_ :: virtual diff --git a/vendor/fontstash/fontstash.odin b/vendor/fontstash/fontstash.odin index 93a48bc0d..edf9e12db 100644 --- a/vendor/fontstash/fontstash.odin +++ b/vendor/fontstash/fontstash.odin @@ -1067,7 +1067,7 @@ TextIterNext :: proc( iter.x = iter.nextx iter.y = iter.nexty iter.codepointCount += 1 - if glyph, ok := __getGlyph(ctx, iter.font, iter.codepoint, iter.isize, iter.iblur); ok { + if glyph, glyph_ok := __getGlyph(ctx, iter.font, iter.codepoint, iter.isize, iter.iblur); glyph_ok { __getQuad(ctx, iter.font, iter.previousGlyphIndex, glyph, iter.scale, iter.spacing, &iter.nextx, &iter.nexty, quad) iter.previousGlyphIndex = glyph.index } else { diff --git a/vendor/nanovg/nanovg.odin b/vendor/nanovg/nanovg.odin index 6b06e3534..8d30a407d 100644 --- a/vendor/nanovg/nanovg.odin +++ b/vendor/nanovg/nanovg.odin @@ -916,8 +916,8 @@ CreateImageMem :: proc(ctx: ^Context, data: []byte, imageFlags: ImageFlags) -> i return 0 } - data := img[:int(w) * int(h) * int(n)] - image := CreateImageRGBA(ctx, int(w), int(h), imageFlags, data) + pixel_data := img[:int(w) * int(h) * int(n)] + image := CreateImageRGBA(ctx, int(w), int(h), imageFlags, pixel_data) stbi.image_free(img) return image } From 0180a4fcd429ca6345d28ef4608d4445ecd00f55 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 28 Jun 2023 12:57:09 +0100 Subject: [PATCH 100/144] Add `math.sincos` --- core/math/math_sincos.odin | 308 +++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100644 core/math/math_sincos.odin diff --git a/core/math/math_sincos.odin b/core/math/math_sincos.odin new file mode 100644 index 000000000..578876ac5 --- /dev/null +++ b/core/math/math_sincos.odin @@ -0,0 +1,308 @@ +package math + +import "core:math/bits" + +// The original C code, the long comment, and the constants +// below were from http://netlib.sandia.gov/cephes/cmath/sin.c, +// available from http://www.netlib.org/cephes/cmath.tgz. +// The go code is a simplified version of the original C. +// +// sin.c +// +// Circular sine +// +// SYNOPSIS: +// +// double x, y, sin(); +// y = sin( x ); +// +// DESCRIPTION: +// +// Range reduction is into intervals of pi/4. The reduction error is nearly +// eliminated by contriving an extended precision modular arithmetic. +// +// Two polynomial approximating functions are employed. +// Between 0 and pi/4 the sine is approximated by +// x + x**3 P(x**2). +// Between pi/4 and pi/2 the cosine is represented as +// 1 - x**2 Q(x**2). +// +// ACCURACY: +// +// Relative error: +// arithmetic domain # trials peak rms +// DEC 0, 10 150000 3.0e-17 7.8e-18 +// IEEE -1.07e9,+1.07e9 130000 2.1e-16 5.4e-17 +// +// Partial loss of accuracy begins to occur at x = 2**30 = 1.074e9. The loss +// is not gradual, but jumps suddenly to about 1 part in 10e7. Results may +// be meaningless for x > 2**49 = 5.6e14. +// +// cos.c +// +// Circular cosine +// +// SYNOPSIS: +// +// double x, y, cos(); +// y = cos( x ); +// +// DESCRIPTION: +// +// Range reduction is into intervals of pi/4. The reduction error is nearly +// eliminated by contriving an extended precision modular arithmetic. +// +// Two polynomial approximating functions are employed. +// Between 0 and pi/4 the cosine is approximated by +// 1 - x**2 Q(x**2). +// Between pi/4 and pi/2 the sine is represented as +// x + x**3 P(x**2). +// +// ACCURACY: +// +// Relative error: +// arithmetic domain # trials peak rms +// IEEE -1.07e9,+1.07e9 130000 2.1e-16 5.4e-17 +// DEC 0,+1.07e9 17000 3.0e-17 7.2e-18 +// +// Cephes Math Library Release 2.8: June, 2000 +// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier +// +// The readme file at http://netlib.sandia.gov/cephes/ says: +// Some software in this archive may be from the book _Methods and +// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster +// International, 1989) or from the Cephes Mathematical Library, a +// commercial product. In either event, it is copyrighted by the author. +// What you see here may be used freely but it comes with no support or +// guarantee. +// +// The two known misprints in the book are repaired here in the +// source listings for the gamma function and the incomplete beta +// integral. +// +// Stephen L. Moshier +// moshier@na-net.ornl.gov + +sincos :: proc{ + sincos_f16, sincos_f16le, sincos_f16be, + sincos_f32, sincos_f32le, sincos_f32be, + sincos_f64, sincos_f64le, sincos_f64be, +} + +sincos_f16 :: proc "contextless" (x: f16) -> (sin, cos: f16) #no_bounds_check { + s, c := sincos_f64(f64(x)) + return f16(s), f16(c) +} +sincos_f16le :: proc "contextless" (x: f16le) -> (sin, cos: f16le) #no_bounds_check { + s, c := sincos_f64(f64(x)) + return f16le(s), f16le(c) +} +sincos_f16be :: proc "contextless" (x: f16be) -> (sin, cos: f16be) #no_bounds_check { + s, c := sincos_f64(f64(x)) + return f16be(s), f16be(c) +} + +sincos_f32 :: proc "contextless" (x: f32) -> (sin, cos: f32) #no_bounds_check { + s, c := sincos_f64(f64(x)) + return f32(s), f32(c) +} +sincos_f32le :: proc "contextless" (x: f32le) -> (sin, cos: f32le) #no_bounds_check { + s, c := sincos_f64(f64(x)) + return f32le(s), f32le(c) +} +sincos_f32be :: proc "contextless" (x: f32be) -> (sin, cos: f32be) #no_bounds_check { + s, c := sincos_f64(f64(x)) + return f32be(s), f32be(c) +} + +sincos_f64le :: proc "contextless" (x: f64le) -> (sin, cos: f64le) #no_bounds_check { + s, c := sincos_f64(f64(x)) + return f64le(s), f64le(c) +} +sincos_f64be :: proc "contextless" (x: f64be) -> (sin, cos: f64be) #no_bounds_check { + s, c := sincos_f64(f64(x)) + return f64be(s), f64be(c) +} + +sincos_f64 :: proc "contextless" (x: f64) -> (sin, cos: f64) #no_bounds_check { + x := x + + PI4A :: 0h3fe921fb40000000 // 7.85398125648498535156e-1 PI/4 split into three parts + PI4B :: 0h3e64442d00000000 // 3.77489470793079817668e-8 + PI4C :: 0h3ce8469898cc5170 // 2.69515142907905952645e-15 + + // special cases + switch { + case x == 0: + return x, 1 // return ±0.0, 1.0 + case is_nan(x) || is_inf(x, 0): + return nan_f64(), nan_f64() + } + + // make argument positive + sin_sign, cos_sign := false, false + if x < 0 { + x = -x + sin_sign = true + } + + j: u64 + y, z: f64 + if x >= REDUCE_THRESHOLD { + j, z = _trig_reduce_f64(x) + } else { + j = u64(x * (4 / PI)) // integer part of x/(PI/4), as integer for tests on the phase angle + y = f64(j) // integer part of x/(PI/4), as float + + if j&1 == 1 { // map zeros to origin + j += 1 + y += 1 + } + j &= 7 // octant modulo TAU radians (360 degrees) + z = ((x - y*PI4A) - y*PI4B) - y*PI4C // Extended precision modular arithmetic + } + if j > 3 { // reflect in x axis + j -= 4 + sin_sign, cos_sign = !sin_sign, !cos_sign + } + if j > 1 { + cos_sign = !cos_sign + } + + zz := z * z + + cos = 1.0 - 0.5*zz + zz*zz*((((((_cos[0]*zz)+_cos[1])*zz+_cos[2])*zz+_cos[3])*zz+_cos[4])*zz+_cos[5]) + sin = z + z*zz*((((((_sin[0]*zz)+_sin[1])*zz+_sin[2])*zz+_sin[3])*zz+_sin[4])*zz+_sin[5]) + + if j == 1 || j == 2 { + sin, cos = cos, sin + } + if cos_sign { + cos = -cos + } + if sin_sign { + sin = -sin + } + return +} + +// sin coefficients +@(private="file") +_sin := [?]f64{ + 0h3de5d8fd1fd19ccd, // 1.58962301576546568060e-10 + 0hbe5ae5e5a9291f5d, // -2.50507477628578072866e-8 + 0h3ec71de3567d48a1, // 2.75573136213857245213e-6 + 0hbf2a01a019bfdf03, // -1.98412698295895385996e-4 + 0h3f8111111110f7d0, // 8.33333333332211858878e-3 + 0hbfc5555555555548, // -1.66666666666666307295e-1 +} + +// cos coefficients +@(private="file") +_cos := [?]f64{ + 0hbda8fa49a0861a9b, // -1.13585365213876817300e-11, + 0h3e21ee9d7b4e3f05, // 2.08757008419747316778e-9, + 0hbe927e4f7eac4bc6, // -2.75573141792967388112e-7, + 0h3efa01a019c844f5, // 2.48015872888517045348e-5, + 0hbf56c16c16c14f91, // -1.38888888888730564116e-3, + 0h3fa555555555554b, // 4.16666666666665929218e-2, +} + +// REDUCE_THRESHOLD is the maximum value of x where the reduction using Pi/4 +// in 3 f64 parts still gives accurate results. This threshold +// is set by y*C being representable as a f64 without error +// where y is given by y = floor(x * (4 / Pi)) and C is the leading partial +// terms of 4/Pi. Since the leading terms (PI4A and PI4B in sin.go) have 30 +// and 32 trailing zero bits, y should have less than 30 significant bits. +// +// y < 1<<30 -> floor(x*4/Pi) < 1<<30 -> x < (1<<30 - 1) * Pi/4 +// +// So, conservatively we can take x < 1<<29. +// Above this threshold Payne-Hanek range reduction must be used. +@(private="file") +REDUCE_THRESHOLD :: 1 << 29 + +// _trig_reduce_f64 implements Payne-Hanek range reduction by Pi/4 +// for x > 0. It returns the integer part mod 8 (j) and +// the fractional part (z) of x / (Pi/4). +// The implementation is based on: +// "ARGUMENT REDUCTION FOR HUGE ARGUMENTS: Good to the Last Bit" +// K. C. Ng et al, March 24, 1992 +// The simulated multi-precision calculation of x*B uses 64-bit integer arithmetic. +_trig_reduce_f64 :: proc "contextless" (x: f64) -> (j: u64, z: f64) #no_bounds_check { + // bd_pi4 is the binary digits of 4/pi as a u64 array, + // that is, 4/pi = Sum bd_pi4[i]*2^(-64*i) + // 19 64-bit digits and the leading one bit give 1217 bits + // of precision to handle the largest possible f64 exponent. + @static bd_pi4 := [?]u64{ + 0x0000000000000001, + 0x45f306dc9c882a53, + 0xf84eafa3ea69bb81, + 0xb6c52b3278872083, + 0xfca2c757bd778ac3, + 0x6e48dc74849ba5c0, + 0x0c925dd413a32439, + 0xfc3bd63962534e7d, + 0xd1046bea5d768909, + 0xd338e04d68befc82, + 0x7323ac7306a673e9, + 0x3908bf177bf25076, + 0x3ff12fffbc0b301f, + 0xde5e2316b414da3e, + 0xda6cfd9e4f96136e, + 0x9e8c7ecd3cbfd45a, + 0xea4f758fd7cbe2f6, + 0x7a0e73ef14a525d4, + 0xd7f6bf623f1aba10, + 0xac06608df8f6d757, + } + + PI4 :: PI / 4 + if x < PI4 { + return 0, x + } + + MASK :: 0x7FF + SHIFT :: 64 - 11 - 1 + BIAS :: 1023 + + // Extract out the integer and exponent such that, + // x = ix * 2 ** exp. + ix := transmute(u64)x + exp := int(ix>>SHIFT&MASK) - BIAS - SHIFT + ix &~= MASK << SHIFT + ix |= 1 << SHIFT + // Use the exponent to extract the 3 appropriate u64 digits from bd_pi4, + // B ~ (z0, z1, z2), such that the product leading digit has the exponent -61. + // Note, exp >= -53 since x >= PI4 and exp < 971 for maximum f64. + digit, bitshift := uint(exp+61)/64, uint(exp+61)%64 + z0 := (bd_pi4[digit] << bitshift) | (bd_pi4[digit+1] >> (64 - bitshift)) + z1 := (bd_pi4[digit+1] << bitshift) | (bd_pi4[digit+2] >> (64 - bitshift)) + z2 := (bd_pi4[digit+2] << bitshift) | (bd_pi4[digit+3] >> (64 - bitshift)) + // Multiply mantissa by the digits and extract the upper two digits (hi, lo). + z2hi, _ := bits.mul(z2, ix) + z1hi, z1lo := bits.mul(z1, ix) + z0lo := z0 * ix + lo, c := bits.add(z1lo, z2hi, 0) + hi, _ := bits.add(z0lo, z1hi, c) + // The top 3 bits are j. + j = hi >> 61 + // Extract the fraction and find its magnitude. + hi = hi<<3 | lo>>61 + lz := uint(bits.leading_zeros(hi)) + e := u64(BIAS - (lz + 1)) + // Clear implicit mantissa bit and shift into place. + hi = (hi << (lz + 1)) | (lo >> (64 - (lz + 1))) + hi >>= 64 - SHIFT + // Include the exponent and convert to a float. + hi |= e << SHIFT + z = transmute(f64)hi + // Map zeros to origin. + if j&1 == 1 { + j += 1 + j &= 7 + z -= 1 + } + // Multiply the fractional part by pi/4. + return j, z * PI4 +} From a2b3c72647d4356175dbe5e6635fa0275933de4c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 28 Jun 2023 13:18:36 +0100 Subject: [PATCH 101/144] Improve accuracy of `abs` or `complex*` types --- core/runtime/internal.odin | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index 71ad9386a..ad8a40acf 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -566,16 +566,37 @@ max_f64 :: #force_inline proc "contextless" (a, b: f64) -> f64 { } abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 { - r, i := real(x), imag(x) - return f16(intrinsics.sqrt(f32(r*r + i*i))) + p, q := abs(real(x)), abs(imag(x)) + if p < q { + p, q = q, p + } + if p == 0 { + return 0 + } + q = q / p + return p * f16(intrinsics.sqrt(f32(1 + q*q))) } abs_complex64 :: #force_inline proc "contextless" (x: complex64) -> f32 { - r, i := real(x), imag(x) - return intrinsics.sqrt(r*r + i*i) + p, q := abs(real(x)), abs(imag(x)) + if p < q { + p, q = q, p + } + if p == 0 { + return 0 + } + q = q / p + return p * intrinsics.sqrt(1 + q*q) } abs_complex128 :: #force_inline proc "contextless" (x: complex128) -> f64 { - r, i := real(x), imag(x) - return intrinsics.sqrt(r*r + i*i) + p, q := abs(real(x)), abs(imag(x)) + if p < q { + p, q = q, p + } + if p == 0 { + return 0 + } + q = q / p + return p * intrinsics.sqrt(1 + q*q) } abs_quaternion64 :: #force_inline proc "contextless" (x: quaternion64) -> f16 { r, i, j, k := real(x), imag(x), jmag(x), kmag(x) From 1ecb4aa9aaa7344fe03a5e253fe19d0ca6abedd3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 28 Jun 2023 13:20:23 +0100 Subject: [PATCH 102/144] Begin work on `core:math/cmplx` `complex*` types only at the moment, `quaternion*` types coming later --- core/math/cmplx/cmplx.odin | 513 +++++++++++++++++++++++++++++ core/math/cmplx/cmplx_invtrig.odin | 273 +++++++++++++++ core/math/cmplx/cmplx_trig.odin | 409 +++++++++++++++++++++++ 3 files changed, 1195 insertions(+) create mode 100644 core/math/cmplx/cmplx.odin create mode 100644 core/math/cmplx/cmplx_invtrig.odin create mode 100644 core/math/cmplx/cmplx_trig.odin diff --git a/core/math/cmplx/cmplx.odin b/core/math/cmplx/cmplx.odin new file mode 100644 index 000000000..c029be30c --- /dev/null +++ b/core/math/cmplx/cmplx.odin @@ -0,0 +1,513 @@ +package math_cmplx + +import "core:builtin" +import "core:math" + +// The original C code, the long comment, and the constants +// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. +// The go code is a simplified version of the original C. +// +// Cephes Math Library Release 2.8: June, 2000 +// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier +// +// The readme file at http://netlib.sandia.gov/cephes/ says: +// Some software in this archive may be from the book _Methods and +// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster +// International, 1989) or from the Cephes Mathematical Library, a +// commercial product. In either event, it is copyrighted by the author. +// What you see here may be used freely but it comes with no support or +// guarantee. +// +// The two known misprints in the book are repaired here in the +// source listings for the gamma function and the incomplete beta +// integral. +// +// Stephen L. Moshier +// moshier@na-net.ornl.gov + +abs :: builtin.abs +conj :: builtin.conj +real :: builtin.real +imag :: builtin.imag +jmag :: builtin.jmag +kmag :: builtin.kmag + + +sin :: proc{ + sin_complex128, +} +cos :: proc{ + cos_complex128, +} +tan :: proc{ + tan_complex128, +} +cot :: proc{ + cot_complex128, +} + + +sinh :: proc{ + sinh_complex128, +} +cosh :: proc{ + cosh_complex128, +} +tanh :: proc{ + tanh_complex128, +} + + + +// sqrt returns the square root of x. +// The result r is chosen so that real(r) ≥ 0 and imag(r) has the same sign as imag(x). +sqrt :: proc{ + sqrt_complex32, + sqrt_complex64, + sqrt_complex128, +} +ln :: proc{ + ln_complex32, + ln_complex64, + ln_complex128, +} +log10 :: proc{ + log10_complex32, + log10_complex64, + log10_complex128, +} + +exp :: proc{ + exp_complex32, + exp_complex64, + exp_complex128, +} + +pow :: proc{ + pow_complex32, + pow_complex64, + pow_complex128, +} + +phase :: proc{ + phase_complex32, + phase_complex64, + phase_complex128, +} + +polar :: proc{ + polar_complex32, + polar_complex64, + polar_complex128, +} + +is_inf :: proc{ + is_inf_complex32, + is_inf_complex64, + is_inf_complex128, +} + +is_nan :: proc{ + is_nan_complex32, + is_nan_complex64, + is_nan_complex128, +} + + + +// sqrt_complex32 returns the square root of x. +// The result r is chosen so that real(r) ≥ 0 and imag(r) has the same sign as imag(x). +sqrt_complex32 :: proc "contextless" (x: complex32) -> complex32 { + return complex32(sqrt_complex128(complex128(x))) +} + +// sqrt_complex64 returns the square root of x. +// The result r is chosen so that real(r) ≥ 0 and imag(r) has the same sign as imag(x). +sqrt_complex64 :: proc "contextless" (x: complex64) -> complex64 { + return complex64(sqrt_complex128(complex128(x))) +} + + +// sqrt_complex128 returns the square root of x. +// The result r is chosen so that real(r) ≥ 0 and imag(r) has the same sign as imag(x). +sqrt_complex128 :: proc "contextless" (x: complex128) -> complex128 { + // The original C code, the long comment, and the constants + // below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. + // The go code is a simplified version of the original C. + // + // Cephes Math Library Release 2.8: June, 2000 + // Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier + // + // The readme file at http://netlib.sandia.gov/cephes/ says: + // Some software in this archive may be from the book _Methods and + // Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster + // International, 1989) or from the Cephes Mathematical Library, a + // commercial product. In either event, it is copyrighted by the author. + // What you see here may be used freely but it comes with no support or + // guarantee. + // + // The two known misprints in the book are repaired here in the + // source listings for the gamma function and the incomplete beta + // integral. + // + // Stephen L. Moshier + // moshier@na-net.ornl.gov + + // Complex square root + // + // DESCRIPTION: + // + // If z = x + iy, r = |z|, then + // + // 1/2 + // Re w = [ (r + x)/2 ] , + // + // 1/2 + // Im w = [ (r - x)/2 ] . + // + // Cancellation error in r-x or r+x is avoided by using the + // identity 2 Re w Im w = y. + // + // Note that -w is also a square root of z. The root chosen + // is always in the right half plane and Im w has the same sign as y. + // + // ACCURACY: + // + // Relative error: + // arithmetic domain # trials peak rms + // DEC -10,+10 25000 3.2e-17 9.6e-18 + // IEEE -10,+10 1,000,000 2.9e-16 6.1e-17 + + if imag(x) == 0 { + // Ensure that imag(r) has the same sign as imag(x) for imag(x) == signed zero. + if real(x) == 0 { + return complex(0, imag(x)) + } + if real(x) < 0 { + return complex(0, math.copy_sign(math.sqrt(-real(x)), imag(x))) + } + return complex(math.sqrt(real(x)), imag(x)) + } else if math.is_inf(imag(x), 0) { + return complex(math.inf_f64(1.0), imag(x)) + } + if real(x) == 0 { + if imag(x) < 0 { + r := math.sqrt(-0.5 * imag(x)) + return complex(r, -r) + } + r := math.sqrt(0.5 * imag(x)) + return complex(r, r) + } + a := real(x) + b := imag(x) + scale: f64 + // Rescale to avoid internal overflow or underflow. + if abs(a) > 4 || abs(b) > 4 { + a *= 0.25 + b *= 0.25 + scale = 2 + } else { + a *= 1.8014398509481984e16 // 2**54 + b *= 1.8014398509481984e16 + scale = 7.450580596923828125e-9 // 2**-27 + } + r := math.hypot(a, b) + t: f64 + if a > 0 { + t = math.sqrt(0.5*r + 0.5*a) + r = scale * abs((0.5*b)/t) + t *= scale + } else { + r = math.sqrt(0.5*r - 0.5*a) + t = scale * abs((0.5*b)/r) + r *= scale + } + if b < 0 { + return complex(t, -r) + } + return complex(t, r) +} + +ln_complex32 :: proc "contextless" (x: complex32) -> complex32 { + return complex(math.ln(abs(x)), phase(x)) +} +ln_complex64 :: proc "contextless" (x: complex64) -> complex64 { + return complex(math.ln(abs(x)), phase(x)) +} +ln_complex128 :: proc "contextless" (x: complex128) -> complex128 { + return complex(math.ln(abs(x)), phase(x)) +} + + +exp_complex32 :: proc "contextless" (x: complex32) -> complex32 { + switch re, im := real(x), imag(x); { + case math.is_inf(re, 0): + switch { + case re > 0 && im == 0: + return x + case math.is_inf(im, 0) || math.is_nan(im): + if re < 0 { + return complex(0, math.copy_sign(0, im)) + } else { + return complex(math.inf_f64(1.0), math.nan_f64()) + } + } + case math.is_nan(re): + if im == 0 { + return complex(math.nan_f16(), im) + } + } + r := math.exp(real(x)) + s, c := math.sincos(imag(x)) + return complex(r*c, r*s) +} +exp_complex64 :: proc "contextless" (x: complex64) -> complex64 { + switch re, im := real(x), imag(x); { + case math.is_inf(re, 0): + switch { + case re > 0 && im == 0: + return x + case math.is_inf(im, 0) || math.is_nan(im): + if re < 0 { + return complex(0, math.copy_sign(0, im)) + } else { + return complex(math.inf_f64(1.0), math.nan_f64()) + } + } + case math.is_nan(re): + if im == 0 { + return complex(math.nan_f32(), im) + } + } + r := math.exp(real(x)) + s, c := math.sincos(imag(x)) + return complex(r*c, r*s) +} +exp_complex128 :: proc "contextless" (x: complex128) -> complex128 { + switch re, im := real(x), imag(x); { + case math.is_inf(re, 0): + switch { + case re > 0 && im == 0: + return x + case math.is_inf(im, 0) || math.is_nan(im): + if re < 0 { + return complex(0, math.copy_sign(0, im)) + } else { + return complex(math.inf_f64(1.0), math.nan_f64()) + } + } + case math.is_nan(re): + if im == 0 { + return complex(math.nan_f64(), im) + } + } + r := math.exp(real(x)) + s, c := math.sincos(imag(x)) + return complex(r*c, r*s) +} + + +pow_complex32 :: proc "contextless" (x, y: complex32) -> complex32 { + if x == 0 { // Guaranteed also true for x == -0. + if is_nan(y) { + return nan_complex32() + } + r, i := real(y), imag(y) + switch { + case r == 0: + return 1 + case r < 0: + if i == 0 { + return complex(math.inf_f16(1), 0) + } + return inf_complex32() + case r > 0: + return 0 + } + unreachable() + } + modulus := abs(x) + if modulus == 0 { + return complex(0, 0) + } + r := math.pow(modulus, real(y)) + arg := phase(x) + theta := real(y) * arg + if imag(y) != 0 { + r *= math.exp(-imag(y) * arg) + theta += imag(y) * math.ln(modulus) + } + s, c := math.sincos(theta) + return complex(r*c, r*s) +} +pow_complex64 :: proc "contextless" (x, y: complex64) -> complex64 { + if x == 0 { // Guaranteed also true for x == -0. + if is_nan(y) { + return nan_complex64() + } + r, i := real(y), imag(y) + switch { + case r == 0: + return 1 + case r < 0: + if i == 0 { + return complex(math.inf_f32(1), 0) + } + return inf_complex64() + case r > 0: + return 0 + } + unreachable() + } + modulus := abs(x) + if modulus == 0 { + return complex(0, 0) + } + r := math.pow(modulus, real(y)) + arg := phase(x) + theta := real(y) * arg + if imag(y) != 0 { + r *= math.exp(-imag(y) * arg) + theta += imag(y) * math.ln(modulus) + } + s, c := math.sincos(theta) + return complex(r*c, r*s) +} +pow_complex128 :: proc "contextless" (x, y: complex128) -> complex128 { + if x == 0 { // Guaranteed also true for x == -0. + if is_nan(y) { + return nan_complex128() + } + r, i := real(y), imag(y) + switch { + case r == 0: + return 1 + case r < 0: + if i == 0 { + return complex(math.inf_f64(1), 0) + } + return inf_complex128() + case r > 0: + return 0 + } + unreachable() + } + modulus := abs(x) + if modulus == 0 { + return complex(0, 0) + } + r := math.pow(modulus, real(y)) + arg := phase(x) + theta := real(y) * arg + if imag(y) != 0 { + r *= math.exp(-imag(y) * arg) + theta += imag(y) * math.ln(modulus) + } + s, c := math.sincos(theta) + return complex(r*c, r*s) +} + + + +log10_complex32 :: proc "contextless" (x: complex32) -> complex32 { + return math.LN10*ln(x) +} +log10_complex64 :: proc "contextless" (x: complex64) -> complex64 { + return math.LN10*ln(x) +} +log10_complex128 :: proc "contextless" (x: complex128) -> complex128 { + return math.LN10*ln(x) +} + + +phase_complex32 :: proc "contextless" (x: complex32) -> f16 { + return math.atan2(imag(x), real(x)) +} +phase_complex64 :: proc "contextless" (x: complex64) -> f32 { + return math.atan2(imag(x), real(x)) +} +phase_complex128 :: proc "contextless" (x: complex128) -> f64 { + return math.atan2(imag(x), real(x)) +} + + +rect_complex32 :: proc "contextless" (r, θ: f16) -> complex32 { + s, c := math.sincos(θ) + return complex(r*c, r*s) +} +rect_complex64 :: proc "contextless" (r, θ: f32) -> complex64 { + s, c := math.sincos(θ) + return complex(r*c, r*s) +} +rect_complex128 :: proc "contextless" (r, θ: f64) -> complex128 { + s, c := math.sincos(θ) + return complex(r*c, r*s) +} + +polar_complex32 :: proc "contextless" (x: complex32) -> (r, θ: f16) { + return abs(x), phase(x) +} +polar_complex64 :: proc "contextless" (x: complex64) -> (r, θ: f32) { + return abs(x), phase(x) +} +polar_complex128 :: proc "contextless" (x: complex128) -> (r, θ: f64) { + return abs(x), phase(x) +} + + + + +nan_complex32 :: proc "contextless" () -> complex32 { + return complex(math.nan_f16(), math.nan_f16()) +} +nan_complex64 :: proc "contextless" () -> complex64 { + return complex(math.nan_f32(), math.nan_f32()) +} +nan_complex128 :: proc "contextless" () -> complex128 { + return complex(math.nan_f64(), math.nan_f64()) +} + + +inf_complex32 :: proc "contextless" () -> complex32 { + inf := math.inf_f16(1) + return complex(inf, inf) +} +inf_complex64 :: proc "contextless" () -> complex64 { + inf := math.inf_f32(1) + return complex(inf, inf) +} +inf_complex128 :: proc "contextless" () -> complex128 { + inf := math.inf_f64(1) + return complex(inf, inf) +} + + +is_inf_complex32 :: proc "contextless" (x: complex32) -> bool { + return math.is_inf(real(x), 0) || math.is_inf(imag(x), 0) +} +is_inf_complex64 :: proc "contextless" (x: complex64) -> bool { + return math.is_inf(real(x), 0) || math.is_inf(imag(x), 0) +} +is_inf_complex128 :: proc "contextless" (x: complex128) -> bool { + return math.is_inf(real(x), 0) || math.is_inf(imag(x), 0) +} + + +is_nan_complex32 :: proc "contextless" (x: complex32) -> bool { + if math.is_inf(real(x), 0) || math.is_inf(imag(x), 0) { + return false + } + return math.is_nan(real(x)) || math.is_nan(imag(x)) +} +is_nan_complex64 :: proc "contextless" (x: complex64) -> bool { + if math.is_inf(real(x), 0) || math.is_inf(imag(x), 0) { + return false + } + return math.is_nan(real(x)) || math.is_nan(imag(x)) +} +is_nan_complex128 :: proc "contextless" (x: complex128) -> bool { + if math.is_inf(real(x), 0) || math.is_inf(imag(x), 0) { + return false + } + return math.is_nan(real(x)) || math.is_nan(imag(x)) +} diff --git a/core/math/cmplx/cmplx_invtrig.odin b/core/math/cmplx/cmplx_invtrig.odin new file mode 100644 index 000000000..a746a370f --- /dev/null +++ b/core/math/cmplx/cmplx_invtrig.odin @@ -0,0 +1,273 @@ +package math_cmplx + +import "core:builtin" +import "core:math" + +// The original C code, the long comment, and the constants +// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. +// The go code is a simplified version of the original C. +// +// Cephes Math Library Release 2.8: June, 2000 +// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier +// +// The readme file at http://netlib.sandia.gov/cephes/ says: +// Some software in this archive may be from the book _Methods and +// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster +// International, 1989) or from the Cephes Mathematical Library, a +// commercial product. In either event, it is copyrighted by the author. +// What you see here may be used freely but it comes with no support or +// guarantee. +// +// The two known misprints in the book are repaired here in the +// source listings for the gamma function and the incomplete beta +// integral. +// +// Stephen L. Moshier +// moshier@na-net.ornl.gov + +acos :: proc{ + acos_complex32, + acos_complex64, + acos_complex128, +} +acosh :: proc{ + acosh_complex32, + acosh_complex64, + acosh_complex128, +} + +asin :: proc{ + asin_complex32, + asin_complex64, + asin_complex128, +} +asinh :: proc{ + asinh_complex32, + asinh_complex64, + asinh_complex128, +} + +atan :: proc{ + atan_complex32, + atan_complex64, + atan_complex128, +} + +atanh :: proc{ + atanh_complex32, + atanh_complex64, + atanh_complex128, +} + + +acos_complex32 :: proc "contextless" (x: complex32) -> complex32 { + w := asin(x) + return complex(math.PI/2 - real(w), -imag(w)) +} +acos_complex64 :: proc "contextless" (x: complex64) -> complex64 { + w := asin(x) + return complex(math.PI/2 - real(w), -imag(w)) +} +acos_complex128 :: proc "contextless" (x: complex128) -> complex128 { + w := asin(x) + return complex(math.PI/2 - real(w), -imag(w)) +} + + +acosh_complex32 :: proc "contextless" (x: complex32) -> complex32 { + if x == 0 { + return complex(0, math.copy_sign(math.PI/2, imag(x))) + } + w := acos(x) + if imag(w) <= 0 { + return complex(-imag(w), real(w)) + } + return complex(imag(w), -real(w)) +} +acosh_complex64 :: proc "contextless" (x: complex64) -> complex64 { + if x == 0 { + return complex(0, math.copy_sign(math.PI/2, imag(x))) + } + w := acos(x) + if imag(w) <= 0 { + return complex(-imag(w), real(w)) + } + return complex(imag(w), -real(w)) +} +acosh_complex128 :: proc "contextless" (x: complex128) -> complex128 { + if x == 0 { + return complex(0, math.copy_sign(math.PI/2, imag(x))) + } + w := acos(x) + if imag(w) <= 0 { + return complex(-imag(w), real(w)) + } + return complex(imag(w), -real(w)) +} + +asin_complex32 :: proc "contextless" (x: complex32) -> complex32 { + return complex32(asin_complex128(complex128(x))) +} +asin_complex64 :: proc "contextless" (x: complex64) -> complex64 { + return complex64(asin_complex128(complex128(x))) +} +asin_complex128 :: proc "contextless" (x: complex128) -> complex128 { + switch re, im := real(x), imag(x); { + case im == 0 && abs(re) <= 1: + return complex(math.asin(re), im) + case re == 0 && abs(im) <= 1: + return complex(re, math.asinh(im)) + case math.is_nan(im): + switch { + case re == 0: + return complex(re, math.nan_f64()) + case math.is_inf(re, 0): + return complex(math.nan_f64(), re) + case: + return nan_complex128() + } + case math.is_inf(im, 0): + switch { + case math.is_nan(re): + return x + case math.is_inf(re, 0): + return complex(math.copy_sign(math.PI/4, re), im) + case: + return complex(math.copy_sign(0, re), im) + } + case math.is_inf(re, 0): + return complex(math.copy_sign(math.PI/2, re), math.copy_sign(re, im)) + } + ct := complex(-imag(x), real(x)) // i * x + xx := x * x + x1 := complex(1-real(xx), -imag(xx)) // 1 - x*x + x2 := sqrt(x1) // x2 = sqrt(1 - x*x) + w := ln(ct + x2) + return complex(imag(w), -real(w)) // -i * w +} + +asinh_complex32 :: proc "contextless" (x: complex32) -> complex32 { + return complex32(asinh_complex128(complex128(x))) +} +asinh_complex64 :: proc "contextless" (x: complex64) -> complex64 { + return complex64(asinh_complex128(complex128(x))) +} +asinh_complex128 :: proc "contextless" (x: complex128) -> complex128 { + switch re, im := real(x), imag(x); { + case im == 0 && abs(re) <= 1: + return complex(math.asinh(re), im) + case re == 0 && abs(im) <= 1: + return complex(re, math.asin(im)) + case math.is_inf(re, 0): + switch { + case math.is_inf(im, 0): + return complex(re, math.copy_sign(math.PI/4, im)) + case math.is_nan(im): + return x + case: + return complex(re, math.copy_sign(0.0, im)) + } + case math.is_nan(re): + switch { + case im == 0: + return x + case math.is_inf(im, 0): + return complex(im, re) + case: + return nan_complex128() + } + case math.is_inf(im, 0): + return complex(math.copy_sign(im, re), math.copy_sign(math.PI/2, im)) + } + xx := x * x + x1 := complex(1+real(xx), imag(xx)) // 1 + x*x + return ln(x + sqrt(x1)) // log(x + sqrt(1 + x*x)) +} + + +atan_complex32 :: proc "contextless" (x: complex32) -> complex32 { + return complex32(atan_complex128(complex128(x))) +} +atan_complex64 :: proc "contextless" (x: complex64) -> complex64 { + return complex64(atan_complex128(complex128(x))) +} +atan_complex128 :: proc "contextless" (x: complex128) -> complex128 { + // Complex circular arc tangent + // + // DESCRIPTION: + // + // If + // z = x + iy, + // + // then + // 1 ( 2x ) + // Re w = - arctan(-----------) + k PI + // 2 ( 2 2) + // (1 - x - y ) + // + // ( 2 2) + // 1 (x + (y+1) ) + // Im w = - log(------------) + // 4 ( 2 2) + // (x + (y-1) ) + // + // Where k is an arbitrary integer. + // + // catan(z) = -i catanh(iz). + // + // ACCURACY: + // + // Relative error: + // arithmetic domain # trials peak rms + // DEC -10,+10 5900 1.3e-16 7.8e-18 + // IEEE -10,+10 30000 2.3e-15 8.5e-17 + // The check catan( ctan(z) ) = z, with |x| and |y| < PI/2, + // had peak relative error 1.5e-16, rms relative error + // 2.9e-17. See also clog(). + + switch re, im := real(x), imag(x); { + case im == 0: + return complex(math.atan(re), im) + case re == 0 && abs(im) <= 1: + return complex(re, math.atanh(im)) + case math.is_inf(im, 0) || math.is_inf(re, 0): + if math.is_nan(re) { + return complex(math.nan_f64(), math.copy_sign(0, im)) + } + return complex(math.copy_sign(math.PI/2, re), math.copy_sign(0, im)) + case math.is_nan(re) || math.is_nan(im): + return nan_complex128() + } + x2 := real(x) * real(x) + a := 1 - x2 - imag(x)*imag(x) + if a == 0 { + return nan_complex128() + } + t := 0.5 * math.atan2(2*real(x), a) + w := _reduce_pi_f64(t) + + t = imag(x) - 1 + b := x2 + t*t + if b == 0 { + return nan_complex128() + } + t = imag(x) + 1 + c := (x2 + t*t) / b + return complex(w, 0.25*math.ln(c)) +} + +atanh_complex32 :: proc "contextless" (x: complex32) -> complex32 { + z := complex(-imag(x), real(x)) // z = i * x + z = atan(z) + return complex(imag(z), -real(z)) // z = -i * z +} +atanh_complex64 :: proc "contextless" (x: complex64) -> complex64 { + z := complex(-imag(x), real(x)) // z = i * x + z = atan(z) + return complex(imag(z), -real(z)) // z = -i * z +} +atanh_complex128 :: proc "contextless" (x: complex128) -> complex128 { + z := complex(-imag(x), real(x)) // z = i * x + z = atan(z) + return complex(imag(z), -real(z)) // z = -i * z +} \ No newline at end of file diff --git a/core/math/cmplx/cmplx_trig.odin b/core/math/cmplx/cmplx_trig.odin new file mode 100644 index 000000000..7ca404fab --- /dev/null +++ b/core/math/cmplx/cmplx_trig.odin @@ -0,0 +1,409 @@ +package math_cmplx + +import "core:math" +import "core:math/bits" + +// The original C code, the long comment, and the constants +// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. +// The go code is a simplified version of the original C. +// +// Cephes Math Library Release 2.8: June, 2000 +// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier +// +// The readme file at http://netlib.sandia.gov/cephes/ says: +// Some software in this archive may be from the book _Methods and +// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster +// International, 1989) or from the Cephes Mathematical Library, a +// commercial product. In either event, it is copyrighted by the author. +// What you see here may be used freely but it comes with no support or +// guarantee. +// +// The two known misprints in the book are repaired here in the +// source listings for the gamma function and the incomplete beta +// integral. +// +// Stephen L. Moshier +// moshier@na-net.ornl.gov + +sin_complex128 :: proc "contextless" (x: complex128) -> complex128 { + // Complex circular sine + // + // DESCRIPTION: + // + // If + // z = x + iy, + // + // then + // + // w = sin x cosh y + i cos x sinh y. + // + // csin(z) = -i csinh(iz). + // + // ACCURACY: + // + // Relative error: + // arithmetic domain # trials peak rms + // DEC -10,+10 8400 5.3e-17 1.3e-17 + // IEEE -10,+10 30000 3.8e-16 1.0e-16 + // Also tested by csin(casin(z)) = z. + + switch re, im := real(x), imag(x); { + case im == 0 && (math.is_inf(re, 0) || math.is_nan(re)): + return complex(math.nan_f64(), im) + case math.is_inf(im, 0): + switch { + case re == 0: + return x + case math.is_inf(re, 0) || math.is_nan(re): + return complex(math.nan_f64(), im) + } + case re == 0 && math.is_nan(im): + return x + } + s, c := math.sincos(real(x)) + sh, ch := _sinhcosh_f64(imag(x)) + return complex(s*ch, c*sh) +} + +cos_complex128 :: proc "contextless" (x: complex128) -> complex128 { + // Complex circular cosine + // + // DESCRIPTION: + // + // If + // z = x + iy, + // + // then + // + // w = cos x cosh y - i sin x sinh y. + // + // ACCURACY: + // + // Relative error: + // arithmetic domain # trials peak rms + // DEC -10,+10 8400 4.5e-17 1.3e-17 + // IEEE -10,+10 30000 3.8e-16 1.0e-16 + + switch re, im := real(x), imag(x); { + case im == 0 && (math.is_inf(re, 0) || math.is_nan(re)): + return complex(math.nan_f64(), -im*math.copy_sign(0, re)) + case math.is_inf(im, 0): + switch { + case re == 0: + return complex(math.inf_f64(1), -re*math.copy_sign(0, im)) + case math.is_inf(re, 0) || math.is_nan(re): + return complex(math.inf_f64(1), math.nan_f64()) + } + case re == 0 && math.is_nan(im): + return complex(math.nan_f64(), 0) + } + s, c := math.sincos(real(x)) + sh, ch := _sinhcosh_f64(imag(x)) + return complex(c*ch, -s*sh) +} + +sinh_complex128 :: proc "contextless" (x: complex128) -> complex128 { + // Complex hyperbolic sine + // + // DESCRIPTION: + // + // csinh z = (cexp(z) - cexp(-z))/2 + // = sinh x * cos y + i cosh x * sin y . + // + // ACCURACY: + // + // Relative error: + // arithmetic domain # trials peak rms + // IEEE -10,+10 30000 3.1e-16 8.2e-17 + + switch re, im := real(x), imag(x); { + case re == 0 && (math.is_inf(im, 0) || math.is_nan(im)): + return complex(re, math.nan_f64()) + case math.is_inf(re, 0): + switch { + case im == 0: + return complex(re, im) + case math.is_inf(im, 0) || math.is_nan(im): + return complex(re, math.nan_f64()) + } + case im == 0 && math.is_nan(re): + return complex(math.nan_f64(), im) + } + s, c := math.sincos(imag(x)) + sh, ch := _sinhcosh_f64(real(x)) + return complex(c*sh, s*ch) +} + +cosh_complex128 :: proc "contextless" (x: complex128) -> complex128 { + // Complex hyperbolic cosine + // + // DESCRIPTION: + // + // ccosh(z) = cosh x cos y + i sinh x sin y . + // + // ACCURACY: + // + // Relative error: + // arithmetic domain # trials peak rms + // IEEE -10,+10 30000 2.9e-16 8.1e-17 + + switch re, im := real(x), imag(x); { + case re == 0 && (math.is_inf(im, 0) || math.is_nan(im)): + return complex(math.nan_f64(), re*math.copy_sign(0, im)) + case math.is_inf(re, 0): + switch { + case im == 0: + return complex(math.inf_f64(1), im*math.copy_sign(0, re)) + case math.is_inf(im, 0) || math.is_nan(im): + return complex(math.inf_f64(1), math.nan_f64()) + } + case im == 0 && math.is_nan(re): + return complex(math.nan_f64(), im) + } + s, c := math.sincos(imag(x)) + sh, ch := _sinhcosh_f64(real(x)) + return complex(c*ch, s*sh) +} + +tan_complex128 :: proc "contextless" (x: complex128) -> complex128 { + // Complex circular tangent + // + // DESCRIPTION: + // + // If + // z = x + iy, + // + // then + // + // sin 2x + i sinh 2y + // w = --------------------. + // cos 2x + cosh 2y + // + // On the real axis the denominator is zero at odd multiples + // of PI/2. The denominator is evaluated by its Taylor + // series near these points. + // + // ctan(z) = -i ctanh(iz). + // + // ACCURACY: + // + // Relative error: + // arithmetic domain # trials peak rms + // DEC -10,+10 5200 7.1e-17 1.6e-17 + // IEEE -10,+10 30000 7.2e-16 1.2e-16 + // Also tested by ctan * ccot = 1 and catan(ctan(z)) = z. + + switch re, im := real(x), imag(x); { + case math.is_inf(im, 0): + switch { + case math.is_inf(re, 0) || math.is_nan(re): + return complex(math.copy_sign(0, re), math.copy_sign(1, im)) + } + return complex(math.copy_sign(0, math.sin(2*re)), math.copy_sign(1, im)) + case re == 0 && math.is_nan(im): + return x + } + d := math.cos(2*real(x)) + math.cosh(2*imag(x)) + if abs(d) < 0.25 { + d = _tan_series_f64(x) + } + if d == 0 { + return inf_complex128() + } + return complex(math.sin(2*real(x))/d, math.sinh(2*imag(x))/d) +} + +tanh_complex128 :: proc "contextless" (x: complex128) -> complex128 { + switch re, im := real(x), imag(x); { + case math.is_inf(re, 0): + switch { + case math.is_inf(im, 0) || math.is_nan(im): + return complex(math.copy_sign(1, re), math.copy_sign(0, im)) + } + return complex(math.copy_sign(1, re), math.copy_sign(0, math.sin(2*im))) + case im == 0 && math.is_nan(re): + return x + } + d := math.cosh(2*real(x)) + math.cos(2*imag(x)) + if d == 0 { + return inf_complex128() + } + return complex(math.sinh(2*real(x))/d, math.sin(2*imag(x))/d) +} + +cot_complex128 :: proc "contextless" (x: complex128) -> complex128 { + d := math.cosh(2*imag(x)) - math.cos(2*real(x)) + if abs(d) < 0.25 { + d = _tan_series_f64(x) + } + if d == 0 { + return inf_complex128() + } + return complex(math.sin(2*real(x))/d, -math.sinh(2*imag(x))/d) +} + + +@(private="file") +_sinhcosh_f64 :: proc "contextless" (x: f64) -> (sh, ch: f64) { + if abs(x) <= 0.5 { + return math.sinh(x), math.cosh(x) + } + e := math.exp(x) + ei := 0.5 / e + e *= 0.5 + return e - ei, e + ei +} + + +// taylor series of cosh(2y) - cos(2x) +@(private) +_tan_series_f64 :: proc "contextless" (z: complex128) -> f64 { + MACH_EPSILON :: 1.0 / (1 << 53) + + x := abs(2 * real(z)) + y := abs(2 * imag(z)) + x = _reduce_pi_f64(x) + x, y = x * x, y * y + x2, y2 := 1.0, 1.0 + f, rn, d := 1.0, 0.0, 0.0 + + for { + rn += 1 + f *= rn + rn += 1 + f *= rn + x2 *= x + y2 *= y + t := y2 + x2 + t /= f + d += t + + rn += 1 + f *= rn + rn += 1 + f *= rn + x2 *= x + y2 *= y + t = y2 - x2 + t /= f + d += t + if !(abs(t/d) > MACH_EPSILON) { // don't use <=, because of floating point nonsense and NaN + break + } + } + return d +} + +// _reduce_pi_f64 reduces the input argument x to the range (-PI/2, PI/2]. +// x must be greater than or equal to 0. For small arguments it +// uses Cody-Waite reduction in 3 f64 parts based on: +// "Elementary Function Evaluation: Algorithms and Implementation" +// Jean-Michel Muller, 1997. +// For very large arguments it uses Payne-Hanek range reduction based on: +// "ARGUMENT REDUCTION FOR HUGE ARGUMENTS: Good to the Last Bit" +@(private) +_reduce_pi_f64 :: proc "contextless" (x: f64) -> f64 #no_bounds_check { + x := x + + // REDUCE_THRESHOLD is the maximum value of x where the reduction using + // Cody-Waite reduction still gives accurate results. This threshold + // is set by t*PIn being representable as a f64 without error + // where t is given by t = floor(x * (1 / PI)) and PIn are the leading partial + // terms of PI. Since the leading terms, PI1 and PI2 below, have 30 and 32 + // trailing zero bits respectively, t should have less than 30 significant bits. + // t < 1<<30 -> floor(x*(1/PI)+0.5) < 1<<30 -> x < (1<<30-1) * PI - 0.5 + // So, conservatively we can take x < 1<<30. + REDUCE_THRESHOLD :: f64(1 << 30) + + if abs(x) < REDUCE_THRESHOLD { + // Use Cody-Waite reduction in three parts. + // PI1, PI2 and PI3 comprise an extended precision value of PI + // such that PI ~= PI1 + PI2 + PI3. The parts are chosen so + // that PI1 and PI2 have an approximately equal number of trailing + // zero bits. This ensures that t*PI1 and t*PI2 are exact for + // large integer values of t. The full precision PI3 ensures the + // approximation of PI is accurate to 102 bits to handle cancellation + // during subtraction. + PI1 :: 0h400921fb40000000 // 3.141592502593994 + PI2 :: 0h3e84442d00000000 // 1.5099578831723193e-07 + PI3 :: 0h3d08469898cc5170 // 1.0780605716316238e-14 + + t := x / math.PI + t += 0.5 + t = f64(i64(t)) // i64(t) = the multiple + return ((x - t*PI1) - t*PI2) - t*PI3 + } + // Must apply Payne-Hanek range reduction + MASK :: 0x7FF + SHIFT :: 64 - 11 - 1 + BIAS :: 1023 + FRAC_MASK :: 1<>SHIFT&MASK) - BIAS - SHIFT + ix &= FRAC_MASK + ix |= 1 << SHIFT + + // bdpi is the binary digits of 1/PI as a u64 array, + // that is, 1/PI = SUM bdpi[i]*2^(-64*i). + // 19 64-bit digits give 1216 bits of precision + // to handle the largest possible f64 exponent. + @static bdpi := [?]u64{ + 0x0000000000000000, + 0x517cc1b727220a94, + 0xfe13abe8fa9a6ee0, + 0x6db14acc9e21c820, + 0xff28b1d5ef5de2b0, + 0xdb92371d2126e970, + 0x0324977504e8c90e, + 0x7f0ef58e5894d39f, + 0x74411afa975da242, + 0x74ce38135a2fbf20, + 0x9cc8eb1cc1a99cfa, + 0x4e422fc5defc941d, + 0x8ffc4bffef02cc07, + 0xf79788c5ad05368f, + 0xb69b3f6793e584db, + 0xa7a31fb34f2ff516, + 0xba93dd63f5f2f8bd, + 0x9e839cfbc5294975, + 0x35fdafd88fc6ae84, + 0x2b0198237e3db5d5, + } + + // Use the exponent to extract the 3 appropriate u64 digits from bdpi, + // B ~ (z0, z1, z2), such that the product leading digit has the exponent -64. + // Note, exp >= 50 since x >= REDUCE_THRESHOLD and exp < 971 for maximum f64. + digit, bitshift := uint(exp+64)/64, uint(exp+64)%64 + z0 := (bdpi[digit] << bitshift) | (bdpi[digit+1] >> (64 - bitshift)) + z1 := (bdpi[digit+1] << bitshift) | (bdpi[digit+2] >> (64 - bitshift)) + z2 := (bdpi[digit+2] << bitshift) | (bdpi[digit+3] >> (64 - bitshift)) + + // Multiply mantissa by the digits and extract the upper two digits (hi, lo). + z2hi, _ := bits.mul(z2, ix) + z1hi, z1lo := bits.mul(z1, ix) + z0lo := z0 * ix + lo, c := bits.add(z1lo, z2hi, 0) + hi, _ := bits.add(z0lo, z1hi, c) + + // Find the magnitude of the fraction. + lz := uint(bits.leading_zeros(hi)) + e := u64(BIAS - (lz + 1)) + + // Clear implicit mantissa bit and shift into place. + hi = (hi << (lz + 1)) | (lo >> (64 - (lz + 1))) + hi >>= 64 - SHIFT + + // Include the exponent and convert to a float. + hi |= e << SHIFT + x = transmute(f64)(hi) + + // map to (-PI/2, PI/2] + if x > 0.5 { + x -= 1 + } + return math.PI * x +} + From 5e99ff6769ba6463ca68f0e5c6f83ad35208b622 Mon Sep 17 00:00:00 2001 From: DanielGavin Date: Sun, 2 Jul 2023 18:24:37 +0200 Subject: [PATCH 103/144] Add support FlagBits2 --- .../vulkan/_gen/create_vulkan_odin_wrapper.py | 99 ++++++++ vendor/vulkan/enums.odin | 218 ++++++++++++++++++ vendor/vulkan/structs.odin | 6 - 3 files changed, 317 insertions(+), 6 deletions(-) diff --git a/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py b/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py index 26bfc0a82..942f212dc 100644 --- a/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py +++ b/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py @@ -429,6 +429,100 @@ def parse_enums(f): f.write("{} :: distinct bit_set[{}; Flags]\n".format(flag.ljust(max_len), flag_name)) f.write("{} :: enum u32 {{}}\n".format(flag_name.ljust(max_len))) +def parse_fake_enums(f): + data = re.findall(r"static const Vk(\w+FlagBits2) VK_(\w+?) = (\w+);", src, re.S) + + data.sort(key=lambda x: x[0]) + + + + fake_enums = {} + + for type_name, name, value in data: + if type_name in fake_enums: + fake_enums[type_name].append((name,value)) + else: + fake_enums[type_name] = [(name, value)] + + + + for name in fake_enums.keys(): + flags_name = name.replace("FlagBits", "Flags") + enum_name = name.replace("FlagBits", "Flag") + f.write("{} :: distinct bit_set[{}; Flags64]\n".format(flags_name, enum_name)) + f.write("{} :: enum Flags64 {{\n".format(name.replace("FlagBits", "Flag"))) + + prefix = to_snake_case(name).upper() + suffix = None + for ext in ext_suffixes: + prefix_new = remove_suffix(prefix, "_"+ext) + assert suffix is None + if prefix_new != prefix: + suffix = "_"+ext + prefix = prefix_new + break + + + prefix = prefix.replace("_FLAG_BITS2", "_2") + prefix += "_" + + ff = [] + + groups = [] + flags = {} + + names_and_values = fake_enums[name] + + for name, value in names_and_values: + value = value.replace("ULL", "") + n = fix_enum_name(name, prefix, suffix, True) + try: + v = fix_enum_value(value, prefix, suffix, True) + except FlagError as e: + v = int(str(e)) + groups.append((n, v)) + continue + except IgnoreFlagError as e: + groups.append((n, 0)) + continue + + if n == v: + continue + try: + flags[int(v)] = n + except ValueError as e: + pass + + if v == "NONE": + continue + + ff.append((n, v)) + + max_flag_value = max([int(v) for n, v in ff if is_int(v)] + [0]) + max_group_value = max([int(v) for n, v in groups if is_int(v)] + [0]) + if max_flag_value < max_group_value: + if (1< Date: Sun, 2 Jul 2023 18:28:37 +0200 Subject: [PATCH 104/144] Remove whitespaces --- vendor/vulkan/_gen/create_vulkan_odin_wrapper.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py b/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py index 942f212dc..c3497aa8f 100644 --- a/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py +++ b/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py @@ -434,8 +434,6 @@ def parse_fake_enums(f): data.sort(key=lambda x: x[0]) - - fake_enums = {} for type_name, name, value in data: @@ -444,8 +442,6 @@ def parse_fake_enums(f): else: fake_enums[type_name] = [(name, value)] - - for name in fake_enums.keys(): flags_name = name.replace("FlagBits", "Flags") enum_name = name.replace("FlagBits", "Flag") @@ -521,10 +517,6 @@ def parse_fake_enums(f): f.write("}\n\n") - - - - def parse_structs(f): data = re.findall(r"typedef (struct|union) Vk(\w+?) {(.+?)} \w+?;", src, re.S) data += re.findall(r"typedef (struct|union) Std(\w+?) {(.+?)} \w+?;", src, re.S) From 23bf7973fa571088cb5080d78d04a798a2cc1e7c Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sun, 2 Jul 2023 19:56:30 +0200 Subject: [PATCH 105/144] detect unix color support --- src/main.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 2198b4536..d66ec5846 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2547,6 +2547,10 @@ gb_internal int strip_semicolons(Parser *parser) { return cast(int)failed; } +#if defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX) +#include +#endif + gb_internal void init_terminal(void) { build_context.has_ansi_terminal_colours = false; #if defined(GB_SYSTEM_WINDOWS) @@ -2558,6 +2562,18 @@ gb_internal void init_terminal(void) { build_context.has_ansi_terminal_colours = true; } } +#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX) + FILE* file = popen("tput colors", "r"); + if (file) { + char buffer[20]; + if (fgets(&buffer[0], 20, file)) { + u64 colors = gb_str_to_u64(buffer, nullptr, 10); + if (colors >= 8) { + build_context.has_ansi_terminal_colours = true; + } + } + pclose(file); + } #endif if (!build_context.has_ansi_terminal_colours) { From f4e87c9720fada536c756110f900c4317fc1c1ce Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sun, 2 Jul 2023 20:20:10 +0200 Subject: [PATCH 106/144] ignore stderr in tput call --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index d66ec5846..80a6c819c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2563,7 +2563,7 @@ gb_internal void init_terminal(void) { } } #elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX) - FILE* file = popen("tput colors", "r"); + FILE* file = popen("tput colors 2>/dev/null", "r"); if (file) { char buffer[20]; if (fgets(&buffer[0], 20, file)) { From f68a3639b4ce2ba8fe90f6907e162ff0a5bbadc7 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sun, 2 Jul 2023 21:11:19 +0200 Subject: [PATCH 107/144] use more common detection method and add FORCE_COLOR and NO_COLOR support --- src/main.cpp | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 80a6c819c..0bd45b1eb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2547,12 +2547,24 @@ gb_internal int strip_semicolons(Parser *parser) { return cast(int)failed; } -#if defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX) -#include -#endif - gb_internal void init_terminal(void) { build_context.has_ansi_terminal_colours = false; + + gbAllocator a = heap_allocator(); + + char const *no_color = gb_get_env("NO_COLOR", a); + defer (gb_free(a, cast(void *)no_color)); + if (no_color != nullptr) { + return; + } + + char const *force_color = gb_get_env("FORCE_COLOR", a); + defer (gb_free(a, cast(void *)force_color)); + if (force_color != nullptr) { + build_context.has_ansi_terminal_colours = true; + return; + } + #if defined(GB_SYSTEM_WINDOWS) HANDLE hnd = GetStdHandle(STD_ERROR_HANDLE); DWORD mode = 0; @@ -2563,21 +2575,15 @@ gb_internal void init_terminal(void) { } } #elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX) - FILE* file = popen("tput colors 2>/dev/null", "r"); - if (file) { - char buffer[20]; - if (fgets(&buffer[0], 20, file)) { - u64 colors = gb_str_to_u64(buffer, nullptr, 10); - if (colors >= 8) { - build_context.has_ansi_terminal_colours = true; - } - } - pclose(file); + char const *term_ = gb_get_env("TERM", a); + defer (gb_free(a, cast(void *)term_)); + String term = make_string_c(term_); + if (!str_eq(term, str_lit("dumb")) && isatty(STDERR_FILENO)) { + build_context.has_ansi_terminal_colours = true; } #endif if (!build_context.has_ansi_terminal_colours) { - gbAllocator a = heap_allocator(); char const *odin_terminal_ = gb_get_env("ODIN_TERMINAL", a); defer (gb_free(a, cast(void *)odin_terminal_)); String odin_terminal = make_string_c(odin_terminal_); From 49058620f090062e1e1a7b3158b22d17fe3e3dfa Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sun, 2 Jul 2023 21:25:48 +0200 Subject: [PATCH 108/144] fix spacing --- src/main.cpp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 0bd45b1eb..db2702b19 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2550,20 +2550,20 @@ gb_internal int strip_semicolons(Parser *parser) { gb_internal void init_terminal(void) { build_context.has_ansi_terminal_colours = false; - gbAllocator a = heap_allocator(); + gbAllocator a = heap_allocator(); - char const *no_color = gb_get_env("NO_COLOR", a); - defer (gb_free(a, cast(void *)no_color)); - if (no_color != nullptr) { - return; - } + char const *no_color = gb_get_env("NO_COLOR", a); + defer (gb_free(a, cast(void *)no_color)); + if (no_color != nullptr) { + return; + } - char const *force_color = gb_get_env("FORCE_COLOR", a); - defer (gb_free(a, cast(void *)force_color)); - if (force_color != nullptr) { - build_context.has_ansi_terminal_colours = true; - return; - } + char const *force_color = gb_get_env("FORCE_COLOR", a); + defer (gb_free(a, cast(void *)force_color)); + if (force_color != nullptr) { + build_context.has_ansi_terminal_colours = true; + return; + } #if defined(GB_SYSTEM_WINDOWS) HANDLE hnd = GetStdHandle(STD_ERROR_HANDLE); @@ -2575,12 +2575,12 @@ gb_internal void init_terminal(void) { } } #elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX) - char const *term_ = gb_get_env("TERM", a); - defer (gb_free(a, cast(void *)term_)); - String term = make_string_c(term_); - if (!str_eq(term, str_lit("dumb")) && isatty(STDERR_FILENO)) { - build_context.has_ansi_terminal_colours = true; - } + char const *term_ = gb_get_env("TERM", a); + defer (gb_free(a, cast(void *)term_)); + String term = make_string_c(term_); + if (!str_eq(term, str_lit("dumb")) && isatty(STDERR_FILENO)) { + build_context.has_ansi_terminal_colours = true; + } #endif if (!build_context.has_ansi_terminal_colours) { From fc81008ab5e8568eb7a76f6311919a44b9e4d8a4 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sun, 2 Jul 2023 22:18:25 +0200 Subject: [PATCH 109/144] Fix #2615: can't iterate untyped string --- src/check_stmt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 964ff1842..a15977b7d 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1528,7 +1528,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) Type *t = base_type(type_deref(operand.type)); switch (t->kind) { case Type_Basic: - if (t->Basic.kind == Basic_string) { + if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) { is_possibly_addressable = false; array_add(&vals, t_rune); array_add(&vals, t_int); From a3e2d90f4cc591b7c2a3957aba4dc4a56451d372 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sun, 2 Jul 2023 22:55:28 +0200 Subject: [PATCH 110/144] add test --- tests/issues/run.bat | 1 + tests/issues/run.sh | 1 + tests/issues/test_issue_2615.odin | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+) create mode 100644 tests/issues/test_issue_2615.odin diff --git a/tests/issues/run.bat b/tests/issues/run.bat index bf49bc85b..105d474e3 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -13,6 +13,7 @@ set COMMON=-collection:tests=..\.. ..\..\..\odin test ..\test_issue_2087.odin %COMMON% -file || exit /b ..\..\..\odin build ..\test_issue_2113.odin %COMMON% -file -debug || exit /b ..\..\..\odin test ..\test_issue_2466.odin %COMMON% -file || exit /b +..\..\..\odin test ..\test_issue_2615.odin %COMMON% -file || exit /b @echo off diff --git a/tests/issues/run.sh b/tests/issues/run.sh index b0c43572f..c4c53e7e1 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -16,6 +16,7 @@ $ODIN test ../test_issue_2056.odin $COMMON -file $ODIN test ../test_issue_2087.odin $COMMON -file $ODIN build ../test_issue_2113.odin $COMMON -file -debug $ODIN test ../test_issue_2466.odin $COMMON -file +$ODIN test ../test_issue_2615.odin $COMMON -file if [[ $($ODIN build ../test_issue_2395.odin $COMMON -file 2>&1 >/dev/null | grep -c "$NO_NIL_ERR") -eq 2 ]] ; then echo "SUCCESSFUL 1/1" else diff --git a/tests/issues/test_issue_2615.odin b/tests/issues/test_issue_2615.odin new file mode 100644 index 000000000..229e5c35b --- /dev/null +++ b/tests/issues/test_issue_2615.odin @@ -0,0 +1,19 @@ +// Tests issue https://github.com/odin-lang/Odin/issues/2615 +// Cannot iterate over string literals +package test_issues + +import "core:testing" + +@(test) +test_cannot_iterate_over_string_literal :: proc(t: ^testing.T) { + for c, i in "fo世" { + switch i { + case 0: + testing.expect_value(t, c, 'f') + case 1: + testing.expect_value(t, c, 'o') + case 2: + testing.expect_value(t, c, '世') + } + } +} From 7cdf37eaf6ec9b4f2f4adf527eea55c7bc716472 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Mon, 3 Jul 2023 01:22:36 +0200 Subject: [PATCH 111/144] exit with code 1 on `odin test` failure --- src/llvm_backend.cpp | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 12abe7b16..6a4046d8d 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1825,25 +1825,45 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star TEMPORARY_ALLOCATOR_GUARD(); auto args = array_make(temporary_allocator(), 1); args[0] = lb_addr_load(p, all_tests_slice); - lb_emit_call(p, runner, args); + lbValue result = lb_emit_call(p, runner, args); + + lbBlock *block_success = lb_create_block(p, "success"); + lbBlock *block_failure = lb_create_block(p, "failure"); + + lbValue result_success = lb_emit_comp(p, Token_CmpEq, result, lb_const_bool(m, t_bool, true)); + lb_emit_if(p, result_success, block_success, block_failure); + + lbValue exit_runner = lb_find_package_value(m, str_lit("os"), str_lit("exit")); + + lb_start_block(p, block_success); + { + auto exit_args = array_make(temporary_allocator(), 1); + exit_args[0] = lb_const_int(m, t_int, 0); + lb_emit_call(p, exit_runner, exit_args); + } + + lb_start_block(p, block_failure); + { + auto exit_args = array_make(temporary_allocator(), 1); + exit_args[0] = lb_const_int(m, t_int, 1); + lb_emit_call(p, exit_runner, exit_args); + } } else { if (m->info->entry_point != nullptr) { lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point); lb_emit_call(p, entry_point, {}, ProcInlining_no_inline); } - } + if (call_cleanup) { + lbValue cleanup_runtime_value = {cleanup_runtime->value, cleanup_runtime->type}; + lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none); + } - if (call_cleanup) { - lbValue cleanup_runtime_value = {cleanup_runtime->value, cleanup_runtime->type}; - lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none); - } - - - if (is_dll_main) { - LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 1, false)); - } else { - LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false)); + if (is_dll_main) { + LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 1, false)); + } else { + LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false)); + } } lb_end_procedure_body(p); From 38d71e668d03040632e6e19f592cd63f7c5b4bd4 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Mon, 3 Jul 2023 19:56:56 +0200 Subject: [PATCH 112/144] add `ADDRESS_FAMILY` to darwin (currently only available in linux) --- core/os/os_darwin.odin | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin index ab05756ae..d8ba40fd0 100644 --- a/core/os/os_darwin.odin +++ b/core/os/os_darwin.odin @@ -314,15 +314,16 @@ Dirent :: struct { Dir :: distinct rawptr // DIR* +ADDRESS_FAMILY :: c.char SOCKADDR :: struct #packed { len: c.char, - family: c.char, + family: ADDRESS_FAMILY, sa_data: [14]c.char, } SOCKADDR_STORAGE_LH :: struct #packed { len: c.char, - family: c.char, + family: ADDRESS_FAMILY, __ss_pad1: [6]c.char, __ss_align: i64, __ss_pad2: [112]c.char, @@ -330,7 +331,7 @@ SOCKADDR_STORAGE_LH :: struct #packed { sockaddr_in :: struct #packed { sin_len: c.char, - sin_family: c.char, + sin_family: ADDRESS_FAMILY, sin_port: u16be, sin_addr: in_addr, sin_zero: [8]c.char, @@ -338,7 +339,7 @@ sockaddr_in :: struct #packed { sockaddr_in6 :: struct #packed { sin6_len: c.char, - sin6_family: c.char, + sin6_family: ADDRESS_FAMILY, sin6_port: u16be, sin6_flowinfo: c.uint, sin6_addr: in6_addr, From 1ff6212ffa32c42be86ee80292401f860435b443 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 7 Jul 2023 22:28:31 +0100 Subject: [PATCH 113/144] Always call `lb_run_remove_dead_instruction_pass` to fix `-debug` issues --- src/llvm_backend_general.cpp | 5 +++-- src/llvm_backend_opt.cpp | 10 ---------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 017eeca2e..676069120 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -3004,8 +3004,9 @@ gb_internal lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block); char const *name = ""; - if (e != nullptr) { - // name = alloc_cstring(permanent_allocator(), e->token.string); + if (e != nullptr && e->token.string.len > 0 && e->token.string != "_") { + // NOTE(bill): for debugging purposes only + name = alloc_cstring(permanent_allocator(), e->token.string); } LLVMTypeRef llvm_type = lb_type(p->module, type); diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 141ee88c7..54e667a0b 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -375,16 +375,6 @@ gb_internal void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedur return; } LLVMRunFunctionPassManager(fpm, p->value); - switch (pass_manager_kind) { - case lbFunctionPassManager_none: - return; - case lbFunctionPassManager_default: - case lbFunctionPassManager_default_without_memcpy: - if (build_context.optimization_level < 0) { - return; - } - break; - } // NOTE(bill): LLVMAddDCEPass doesn't seem to be exported in the official DLL's for LLVM // which means we cannot rely upon it // This is also useful for read the .ll for debug purposes because a lot of instructions From 8f4c59b0801cc5ffa9ea10f72c29a94245f0bbb1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 7 Jul 2023 22:29:53 +0100 Subject: [PATCH 114/144] Add `core:math/cmpl` to all_main.odin --- examples/all/all_main.odin | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index 60850c806..c6b727e42 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -87,6 +87,7 @@ import hlm "core:math/linalg/hlsl" import noise "core:math/noise" import rand "core:math/rand" import ease "core:math/ease" +import cmplx "core:math/cmplx" import mem "core:mem" import virtual "core:mem/virtual" @@ -206,6 +207,7 @@ _ :: hlm _ :: noise _ :: rand _ :: ease +_ :: cmplx _ :: mem _ :: virtual _ :: ast From 3758be55f562cadae17845bfdda1df0431b2a5df Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 7 Jul 2023 22:56:20 +0100 Subject: [PATCH 115/144] Fix #2630 --- src/check_expr.cpp | 30 ++++++++++++++++++++++-------- src/checker.cpp | 11 ----------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7b3ddfc73..77c351cce 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -664,6 +664,11 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand if (check_representable_as_constant(c, operand->value, dst, nullptr)) { if (is_type_typed(dst) && src->kind == Type_Basic) { switch (src->Basic.kind) { + case Basic_UntypedBool: + if (is_type_boolean(dst)) { + return 1; + } + break; case Basic_UntypedRune: if (is_type_integer(dst) || is_type_rune(dst)) { return 1; @@ -704,46 +709,55 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand return -1; } if (src->kind == Type_Basic) { + Type *d = base_array_type(dst); i64 score = -1; switch (src->Basic.kind) { + case Basic_UntypedBool: + if (is_type_boolean(d)) { + score = 1; + } + break; case Basic_UntypedRune: - if (is_type_integer(dst) || is_type_rune(dst)) { + if (is_type_integer(d) || is_type_rune(d)) { score = 1; } break; case Basic_UntypedInteger: - if (is_type_integer(dst) || is_type_rune(dst)) { + if (is_type_integer(d) || is_type_rune(d)) { score = 1; } break; case Basic_UntypedString: - if (is_type_string(dst)) { + if (is_type_string(d)) { score = 1; } break; case Basic_UntypedFloat: - if (is_type_float(dst)) { + if (is_type_float(d)) { score = 1; } break; case Basic_UntypedComplex: - if (is_type_complex(dst)) { + if (is_type_complex(d)) { score = 1; } - if (is_type_quaternion(dst)) { + if (is_type_quaternion(d)) { score = 2; } break; case Basic_UntypedQuaternion: - if (is_type_quaternion(dst)) { + if (is_type_quaternion(d)) { score = 1; } break; } if (score > 0) { - if (is_type_typed(dst)) { + if (is_type_typed(d)) { score += 1; } + if (d != dst) { + score += 6; + } } return score; } diff --git a/src/checker.cpp b/src/checker.cpp index 354cdadd3..396a04d7a 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -285,17 +285,6 @@ gb_internal Scope *create_scope_from_package(CheckerContext *c, AstPackage *pkg) } gb_internal void destroy_scope(Scope *scope) { - for (auto const &entry : scope->elements) { - Entity *e = entry.value; - if (e->kind == Entity_Variable) { - if (!(e->flags & EntityFlag_Used)) { -#if 0 - warning(e->token, "Unused variable '%.*s'", LIT(e->token.string)); -#endif - } - } - } - for (Scope *child = scope->head_child; child != nullptr; child = child->next) { destroy_scope(child); } From 759f846b2b56844b3f1a7e04a0adf78fec9379d0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 7 Jul 2023 23:03:46 +0100 Subject: [PATCH 116/144] Fix `[2]union{T}` comparison against `nil` --- src/llvm_backend_expr.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 68ad9e6e0..b250a32e1 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2276,7 +2276,10 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left GB_ASSERT(gb_is_between(op_kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1)); lbValue nil_check = {}; - if (is_type_untyped_nil(left.type)) { + + if (is_type_array_like(left.type) || is_type_array_like(right.type)) { + // don't do `nil` check if it is array-like + } else if (is_type_untyped_nil(left.type)) { nil_check = lb_emit_comp_against_nil(p, op_kind, right); } else if (is_type_untyped_nil(right.type)) { nil_check = lb_emit_comp_against_nil(p, op_kind, left); @@ -2332,7 +2335,7 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left lbValue res = lb_emit_comp(p, op_kind, val, lb_const_nil(p->module, val.type)); return lb_emit_conv(p, res, t_bool); } - if (is_type_array(a) || is_type_enumerated_array(a)) { + if (is_type_array_like(a)) { Type *tl = base_type(a); lbValue lhs = lb_address_from_load_or_generate_local(p, left); lbValue rhs = lb_address_from_load_or_generate_local(p, right); From bf848637aa73ed1fd31484cca3802e1cd0c14f93 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 7 Jul 2023 23:06:15 +0100 Subject: [PATCH 117/144] Fix #2629 --- src/llvm_backend_expr.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index b250a32e1..c30170553 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1395,13 +1395,17 @@ gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { return lb_emit_conv(p, cmp, type); } else if (lb_is_empty_string_constant(be->right)) { // `x == ""` or `x != ""` - lbValue len = lb_string_len(p, lb_build_expr(p, be->left)); + lbValue s = lb_build_expr(p, be->left); + s = lb_emit_conv(p, s, t_string); + lbValue len = lb_string_len(p, s); lbValue cmp = lb_emit_comp(p, be->op.kind, len, lb_const_int(p->module, t_int, 0)); Type *type = default_type(tv.type); return lb_emit_conv(p, cmp, type); } else if (lb_is_empty_string_constant(be->left)) { // `"" == x` or `"" != x` - lbValue len = lb_string_len(p, lb_build_expr(p, be->right)); + lbValue s = lb_build_expr(p, be->right); + s = lb_emit_conv(p, s, t_string); + lbValue len = lb_string_len(p, s); lbValue cmp = lb_emit_comp(p, be->op.kind, len, lb_const_int(p->module, t_int, 0)); Type *type = default_type(tv.type); return lb_emit_conv(p, cmp, type); From ea20b644cc82401c3e6e76c55cb8aac7ed655e56 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 7 Jul 2023 23:07:41 +0100 Subject: [PATCH 118/144] Fix #2624 --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 77c351cce..4e39b83bd 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1719,7 +1719,7 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) { break; case Token_Not: - if (!is_type_boolean(type)) { + if (!is_type_boolean(type) || is_type_array_like(o->type)) { ERROR_BLOCK(); str = expr_to_string(o->expr); error(op, "Operator '%.*s' is only allowed on boolean expressions", LIT(op.string)); From 6495f2cf989351470b92e6cab3881c7cb85a6a5c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 7 Jul 2023 23:09:46 +0100 Subject: [PATCH 119/144] Fix #2593 --- src/check_expr.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 4e39b83bd..b662c231f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4774,7 +4774,10 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod if (entity == nullptr && selector->kind == Ast_Ident) { String field_name = selector->Ident.token.string; - if (is_type_dynamic_array(type_deref(operand->type))) { + Type *t = type_deref(operand->type); + if (t == nullptr) { + error(operand->expr, "Cannot use a selector expression on 0-value expression"); + } else if (is_type_dynamic_array(t)) { init_mem_allocator(c->checker); } sel = lookup_field(operand->type, field_name, operand->mode == Addressing_Type); From 17562e476f9b2ab4177fbe301df66b98f588413e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 7 Jul 2023 23:13:37 +0100 Subject: [PATCH 120/144] Remove math usage of raw LLVM intrinsic prototypes --- core/math/math_basic.odin | 98 ++++++++++++++++++++++++++++++++------- 1 file changed, 82 insertions(+), 16 deletions(-) diff --git a/core/math/math_basic.odin b/core/math/math_basic.odin index 785c43b10..95e0a93ec 100644 --- a/core/math/math_basic.odin +++ b/core/math/math_basic.odin @@ -3,44 +3,110 @@ package math import "core:intrinsics" -@(default_calling_convention="none") +@(default_calling_convention="none", private="file") foreign _ { @(link_name="llvm.sin.f16", require_results) - sin_f16 :: proc(θ: f16) -> f16 --- + _sin_f16 :: proc(θ: f16) -> f16 --- @(link_name="llvm.sin.f32", require_results) - sin_f32 :: proc(θ: f32) -> f32 --- + _sin_f32 :: proc(θ: f32) -> f32 --- @(link_name="llvm.sin.f64", require_results) - sin_f64 :: proc(θ: f64) -> f64 --- + _sin_f64 :: proc(θ: f64) -> f64 --- @(link_name="llvm.cos.f16", require_results) - cos_f16 :: proc(θ: f16) -> f16 --- + _cos_f16 :: proc(θ: f16) -> f16 --- @(link_name="llvm.cos.f32", require_results) - cos_f32 :: proc(θ: f32) -> f32 --- + _cos_f32 :: proc(θ: f32) -> f32 --- @(link_name="llvm.cos.f64", require_results) - cos_f64 :: proc(θ: f64) -> f64 --- + _cos_f64 :: proc(θ: f64) -> f64 --- @(link_name="llvm.pow.f16", require_results) - pow_f16 :: proc(x, power: f16) -> f16 --- + _pow_f16 :: proc(x, power: f16) -> f16 --- @(link_name="llvm.pow.f32", require_results) - pow_f32 :: proc(x, power: f32) -> f32 --- + _pow_f32 :: proc(x, power: f32) -> f32 --- @(link_name="llvm.pow.f64", require_results) - pow_f64 :: proc(x, power: f64) -> f64 --- + _pow_f64 :: proc(x, power: f64) -> f64 --- @(link_name="llvm.fmuladd.f16", require_results) - fmuladd_f16 :: proc(a, b, c: f16) -> f16 --- + _fmuladd_f16 :: proc(a, b, c: f16) -> f16 --- @(link_name="llvm.fmuladd.f32", require_results) - fmuladd_f32 :: proc(a, b, c: f32) -> f32 --- + _fmuladd_f32 :: proc(a, b, c: f32) -> f32 --- @(link_name="llvm.fmuladd.f64", require_results) - fmuladd_f64 :: proc(a, b, c: f64) -> f64 --- + _fmuladd_f64 :: proc(a, b, c: f64) -> f64 --- @(link_name="llvm.exp.f16", require_results) - exp_f16 :: proc(x: f16) -> f16 --- + _exp_f16 :: proc(x: f16) -> f16 --- @(link_name="llvm.exp.f32", require_results) - exp_f32 :: proc(x: f32) -> f32 --- + _exp_f32 :: proc(x: f32) -> f32 --- @(link_name="llvm.exp.f64", require_results) - exp_f64 :: proc(x: f64) -> f64 --- + _exp_f64 :: proc(x: f64) -> f64 --- } +@(require_results) +sin_f16 :: proc "contextless" (θ: f16) -> f16 { + return _sin_f16(θ) +} +@(require_results) +sin_f32 :: proc "contextless" (θ: f32) -> f32 { + return _sin_f32(θ) +} +@(require_results) +sin_f64 :: proc "contextless" (θ: f64) -> f64 { + return _sin_f64(θ) +} + +@(require_results) +cos_f16 :: proc "contextless" (θ: f16) -> f16 { + return _cos_f16(θ) +} +@(require_results) +cos_f32 :: proc "contextless" (θ: f32) -> f32 { + return _cos_f32(θ) +} +@(require_results) +cos_f64 :: proc "contextless" (θ: f64) -> f64 { + return _cos_f64(θ) +} + +@(require_results) +pow_f16 :: proc "contextless" (x, power: f16) -> f16 { + return _pow_f16(x, power) +} +@(require_results) +pow_f32 :: proc "contextless" (x, power: f32) -> f32 { + return _pow_f32(x, power) +} +@(require_results) +pow_f64 :: proc "contextless" (x, power: f64) -> f64 { + return _pow_f64(x, power) +} + +@(require_results) +fmuladd_f16 :: proc "contextless" (a, b, c: f16) -> f16 { + return _fmuladd_f16(a, b, c) +} +@(require_results) +fmuladd_f32 :: proc "contextless" (a, b, c: f32) -> f32 { + return _fmuladd_f32(a, b, c) +} +@(require_results) +fmuladd_f64 :: proc "contextless" (a, b, c: f64) -> f64 { + return _fmuladd_f64(a, b, c) +} + +@(require_results) +exp_f16 :: proc "contextless" (x: f16) -> f16 { + return _exp_f16(x) +} +@(require_results) +exp_f32 :: proc "contextless" (x: f32) -> f32 { + return _exp_f32(x) +} +@(require_results) +exp_f64 :: proc "contextless" (x: f64) -> f64 { + return _exp_f64(x) +} + + @(require_results) sqrt_f16 :: proc "contextless" (x: f16) -> f16 { return intrinsics.sqrt(x) From aa38889704f3518878aeb82cb03d63c8b592ac0e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 7 Jul 2023 23:23:47 +0100 Subject: [PATCH 121/144] Fix issue with pointer casting internal llvm intrinsics --- src/llvm_backend_const.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index efe1e4d45..2a121ff5d 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -483,7 +483,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo GB_ASSERT(res.value != nullptr); GB_ASSERT(LLVMGetValueKind(res.value) == LLVMFunctionValueKind); - res.value = LLVMConstPointerCast(res.value, lb_type(m, res.type)); + if (LLVMGetIntrinsicID(res.value) == 0) { + // NOTE(bill): do not cast intrinsics as they are not really procedures that can be casted + res.value = LLVMConstPointerCast(res.value, lb_type(m, res.type)); + } return res; } From 62031c24a2afeb9dd15a85e3e15a78860900be6a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 7 Jul 2023 23:35:16 +0100 Subject: [PATCH 122/144] Add extra mutex guards around module value access --- src/llvm_backend_general.cpp | 3 +++ src/llvm_backend_stmt.cpp | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 676069120..f0bbaafb7 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2730,7 +2730,10 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) } else { array_add(&m->missing_procedures_to_check, missing_proc); } + + rw_mutex_shared_lock(&m->values_mutex); found = map_get(&m->values, e); + rw_mutex_shared_unlock(&m->values_mutex); if (found) { return *found; } diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index ae6adb0e2..60420402a 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -1878,7 +1878,9 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice const &return } else if (return_count == 1) { Entity *e = tuple->variables[0]; if (res_count == 0) { + rw_mutex_shared_lock(&p->module->values_mutex); lbValue found = map_must_get(&p->module->values, e); + rw_mutex_shared_unlock(&p->module->values_mutex); res = lb_emit_load(p, found); } else { res = lb_build_expr(p, return_results[0]); @@ -1887,7 +1889,9 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice const &return if (p->type->Proc.has_named_results) { // NOTE(bill): store the named values before returning if (e->token.string != "") { + rw_mutex_shared_lock(&p->module->values_mutex); lbValue found = map_must_get(&p->module->values, e); + rw_mutex_shared_unlock(&p->module->values_mutex); lb_emit_store(p, found, lb_emit_conv(p, res, e->type)); } } @@ -1903,7 +1907,9 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice const &return } else { for (isize res_index = 0; res_index < return_count; res_index++) { Entity *e = tuple->variables[res_index]; + rw_mutex_shared_lock(&p->module->values_mutex); lbValue found = map_must_get(&p->module->values, e); + rw_mutex_shared_unlock(&p->module->values_mutex); lbValue res = lb_emit_load(p, found); array_add(&results, res); } @@ -1925,7 +1931,9 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice const &return if (e->token.string == "") { continue; } + rw_mutex_shared_lock(&p->module->values_mutex); named_results[i] = map_must_get(&p->module->values, e); + rw_mutex_shared_unlock(&p->module->values_mutex); values[i] = lb_emit_conv(p, results[i], e->type); } From 3072479c3c3c4818b0a41dc2aed288e8b3ec0582 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 7 Jul 2023 23:52:56 +0100 Subject: [PATCH 123/144] Generalize name mangling rule to have a singular definition for a name separator --- src/llvm_backend.hpp | 2 ++ src/llvm_backend_general.cpp | 6 +++--- src/llvm_backend_proc.cpp | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 3aa13b488..ce01485ff 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -346,6 +346,8 @@ struct lbProcedure { }; +#define ABI_PKG_NAME_SEPARATOR "." + #if !ODIN_LLVM_MINIMUM_VERSION_14 #define LLVMConstGEP2(Ty__, ConstantVal__, ConstantIndices__, NumIndices__) LLVMConstGEP(ConstantVal__, ConstantIndices__, NumIndices__) diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index f0bbaafb7..ad8a1816a 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1403,7 +1403,7 @@ gb_internal String lb_mangle_name(lbModule *m, Entity *e) { char *new_name = gb_alloc_array(permanent_allocator(), char, max_len); isize new_name_len = gb_snprintf( new_name, max_len, - "%.*s.%.*s", LIT(pkgn), LIT(name) + "%.*s" ABI_PKG_NAME_SEPARATOR "%.*s", LIT(pkgn), LIT(name) ); if (require_suffix_id) { char *str = new_name + new_name_len-1; @@ -1453,7 +1453,7 @@ gb_internal String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedur isize name_len = p->name.len + 1 + ts_name.len + 1 + 10 + 1; char *name_text = gb_alloc_array(permanent_allocator(), char, name_len); u32 guid = 1+p->module->nested_type_name_guid.fetch_add(1); - name_len = gb_snprintf(name_text, name_len, "%.*s.%.*s-%u", LIT(p->name), LIT(ts_name), guid); + name_len = gb_snprintf(name_text, name_len, "%.*s" ABI_PKG_NAME_SEPARATOR "%.*s-%u", LIT(p->name), LIT(ts_name), guid); String name = make_string(cast(u8 *)name_text, name_len-1); e->TypeName.ir_mangled_name = name; @@ -1463,7 +1463,7 @@ gb_internal String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedur isize name_len = 9 + 1 + ts_name.len + 1 + 10 + 1; char *name_text = gb_alloc_array(permanent_allocator(), char, name_len); static std::atomic guid; - name_len = gb_snprintf(name_text, name_len, "_internal.%.*s-%u", LIT(ts_name), 1+guid.fetch_add(1)); + name_len = gb_snprintf(name_text, name_len, "_internal" ABI_PKG_NAME_SEPARATOR "%.*s-%u", LIT(ts_name), 1+guid.fetch_add(1)); String name = make_string(cast(u8 *)name_text, name_len-1); e->TypeName.ir_mangled_name = name; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 42ca90828..c27c55337 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -757,7 +757,7 @@ gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e) char *name_text = gb_alloc_array(permanent_allocator(), char, name_len); i32 guid = cast(i32)p->children.count; - name_len = gb_snprintf(name_text, name_len, "%.*s.%.*s-%d", LIT(p->name), LIT(pd_name), guid); + name_len = gb_snprintf(name_text, name_len, "%.*s" ABI_PKG_NAME_SEPARATOR "%.*s-%d", LIT(p->name), LIT(pd_name), guid); String name = make_string(cast(u8 *)name_text, name_len-1); e->Procedure.link_name = name; From 7b89f258189f817bcc177bbc4426c48f3ccd9127 Mon Sep 17 00:00:00 2001 From: ramn Date: Sat, 8 Jul 2023 23:46:51 +0200 Subject: [PATCH 124/144] Fix #2637 where testing.expect_value can't compare nils --- core/testing/testing.odin | 5 +++-- tests/issues/run.bat | 1 + tests/issues/run.sh | 1 + tests/issues/test_issue_2637.odin | 13 +++++++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 tests/issues/test_issue_2637.odin diff --git a/core/testing/testing.odin b/core/testing/testing.odin index 0ceb58e33..1d738bbd1 100644 --- a/core/testing/testing.odin +++ b/core/testing/testing.odin @@ -4,6 +4,7 @@ import "core:fmt" import "core:io" import "core:time" import "core:intrinsics" +import "core:reflect" // IMPORTANT NOTE: Compiler requires this layout Test_Signature :: proc(^T) @@ -89,7 +90,7 @@ expect :: proc(t: ^T, ok: bool, msg: string = "", loc := #caller_location) -> bo return ok } expect_value :: proc(t: ^T, value, expected: $T, loc := #caller_location) -> bool where intrinsics.type_is_comparable(T) { - ok := value == expected + ok := value == expected || reflect.is_nil(value) && reflect.is_nil(expected) if !ok { errorf(t, "expected %v, got %v", expected, value, loc=loc) } @@ -100,4 +101,4 @@ expect_value :: proc(t: ^T, value, expected: $T, loc := #caller_location) -> boo set_fail_timeout :: proc(t: ^T, duration: time.Duration, loc := #caller_location) { _fail_timeout(t, duration, loc) -} \ No newline at end of file +} diff --git a/tests/issues/run.bat b/tests/issues/run.bat index 105d474e3..63d722e09 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -14,6 +14,7 @@ set COMMON=-collection:tests=..\.. ..\..\..\odin build ..\test_issue_2113.odin %COMMON% -file -debug || exit /b ..\..\..\odin test ..\test_issue_2466.odin %COMMON% -file || exit /b ..\..\..\odin test ..\test_issue_2615.odin %COMMON% -file || exit /b +..\..\..\odin test ..\test_issue_2637.odin %COMMON% -file || exit /b @echo off diff --git a/tests/issues/run.sh b/tests/issues/run.sh index c4c53e7e1..7d2101dc6 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -17,6 +17,7 @@ $ODIN test ../test_issue_2087.odin $COMMON -file $ODIN build ../test_issue_2113.odin $COMMON -file -debug $ODIN test ../test_issue_2466.odin $COMMON -file $ODIN test ../test_issue_2615.odin $COMMON -file +$ODIN test ../test_issue_2637.odin $COMMON -file if [[ $($ODIN build ../test_issue_2395.odin $COMMON -file 2>&1 >/dev/null | grep -c "$NO_NIL_ERR") -eq 2 ]] ; then echo "SUCCESSFUL 1/1" else diff --git a/tests/issues/test_issue_2637.odin b/tests/issues/test_issue_2637.odin new file mode 100644 index 000000000..c170fc444 --- /dev/null +++ b/tests/issues/test_issue_2637.odin @@ -0,0 +1,13 @@ +// Tests issue #2637 https://github.com/odin-lang/Odin/issues/2637 +package test_issues + +import "core:testing" + +Foo :: Maybe(string) + +@(test) +test_expect_value_succeeds_with_nil :: proc(t: ^testing.T) { + x: Foo + testing.expect(t, x == nil) // Succeeds + testing.expect_value(t, x, nil) // Fails, "expected nil, got nil" +} From 5f53d815d113e06bf4612592506db8564d3dc88b Mon Sep 17 00:00:00 2001 From: ramn Date: Sun, 9 Jul 2023 00:15:01 +0200 Subject: [PATCH 125/144] fix: make -vet not complain --- core/testing/testing.odin | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/testing/testing.odin b/core/testing/testing.odin index 1d738bbd1..1ba05315c 100644 --- a/core/testing/testing.odin +++ b/core/testing/testing.odin @@ -6,6 +6,8 @@ import "core:time" import "core:intrinsics" import "core:reflect" +_ :: reflect // alias reflect to nothing to force visibility for -vet + // IMPORTANT NOTE: Compiler requires this layout Test_Signature :: proc(^T) From 363b701925ffdbbab6255934e5076c516fa57ce6 Mon Sep 17 00:00:00 2001 From: Zac Nowicki Date: Sun, 9 Jul 2023 08:29:28 -0400 Subject: [PATCH 126/144] Improve Mutex struct documentation --- core/sync/primitives.odin | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/core/sync/primitives.odin b/core/sync/primitives.odin index b8bcfad70..fdb86d835 100644 --- a/core/sync/primitives.odin +++ b/core/sync/primitives.odin @@ -7,10 +7,21 @@ current_thread_id :: proc "contextless" () -> int { return _current_thread_id() } -// A Mutex is a mutual exclusion lock -// The zero value for a Mutex is an unlocked mutex +// A Mutex is a [mutual exclusion lock](https://en.wikipedia.org/wiki/Mutual_exclusion). +// It can be used to prevent more than one thread from executing the same piece of code, +// and thus prevent access to same piece of memory by multiple threads, at the same time. // -// A Mutex must not be copied after first use +// A Mutex's zero value represents an initial, *unlocked* state. +// +// If another thread tries to take the lock while another thread holds it, it will pause +// until the lock is released. Code or memory that is "surrounded" by a mutex lock is said +// to be "guarded by a mutex". +// +// A Mutex must not be copied after first use (e.g., after locking it the first time). +// This is because, in order to coordinate with other threads, all threads must watch +// the same memory address to know when the lock has been released. Trying to use a +// copy of the lock at a different memory address will result in broken and unsafe +// behavior. For this reason, Mutexes are marked as `#no_copy`. Mutex :: struct #no_copy { impl: _Mutex, } From f0f8177a19e6975c309e5e3e720d8848e6819c96 Mon Sep 17 00:00:00 2001 From: ryuukk <44361234+ryuukk@users.noreply.github.com> Date: Mon, 10 Jul 2023 05:56:26 +0200 Subject: [PATCH 127/144] Add definitions for network byte order conversion procs --- core/sys/windows/ws2_32.odin | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/sys/windows/ws2_32.odin b/core/sys/windows/ws2_32.odin index 631ef4241..c8bec05a6 100644 --- a/core/sys/windows/ws2_32.odin +++ b/core/sys/windows/ws2_32.odin @@ -206,4 +206,12 @@ foreign ws2_32 { optval: ^c_char, optlen: ^c_int, ) -> c_int --- + // https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-ntohl + ntohl :: proc(netlong: c_ulong) -> c_ulong --- + // https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-ntohs + ntohs :: proc(netshort: c_ushort) -> c_ushort --- + // https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htonl + htonl :: proc(hostlong: c_ulong) -> c_ulong --- + // https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htons + htons :: proc(hostshort: c_ushort) -> c_ushort --- } From ccd5685cee52e09b49e6dbb9a3e1f122ef8ef632 Mon Sep 17 00:00:00 2001 From: ryuukk <44361234+ryuukk@users.noreply.github.com> Date: Mon, 10 Jul 2023 05:57:12 +0200 Subject: [PATCH 128/144] Fix doc links --- core/sys/windows/ws2_32.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/sys/windows/ws2_32.odin b/core/sys/windows/ws2_32.odin index c8bec05a6..c09b714ca 100644 --- a/core/sys/windows/ws2_32.odin +++ b/core/sys/windows/ws2_32.odin @@ -206,12 +206,12 @@ foreign ws2_32 { optval: ^c_char, optlen: ^c_int, ) -> c_int --- - // https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-ntohl + // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-ntohl) ntohl :: proc(netlong: c_ulong) -> c_ulong --- - // https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-ntohs + // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-ntohs) ntohs :: proc(netshort: c_ushort) -> c_ushort --- - // https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htonl + // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htonl) htonl :: proc(hostlong: c_ulong) -> c_ulong --- - // https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htons + // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htons) htons :: proc(hostshort: c_ushort) -> c_ushort --- } From bc43a8d38dcd4ad778ebf405308e4dc9058c0834 Mon Sep 17 00:00:00 2001 From: ryuukk <44361234+ryuukk@users.noreply.github.com> Date: Mon, 10 Jul 2023 06:02:38 +0200 Subject: [PATCH 129/144] Add CopyFileW --- core/sys/windows/kernel32.odin | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index beed3a7e5..fcd9e55ed 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -159,6 +159,11 @@ foreign kernel32 { WaitForSingleObject :: proc(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD --- Sleep :: proc(dwMilliseconds: DWORD) --- GetProcessId :: proc(handle: HANDLE) -> DWORD --- + CopyFileW :: proc( + lpExistingFileName: LPCWSTR, + lpNewFileName: LPCWSTR, + bFailIfExists: BOOL, + ) -> BOOL --- CopyFileExW :: proc( lpExistingFileName: LPCWSTR, lpNewFileName: LPCWSTR, From f141078073ef086dfb21f9c3c3de4d43205de69f Mon Sep 17 00:00:00 2001 From: Pix Date: Mon, 10 Jul 2023 22:24:16 +0800 Subject: [PATCH 130/144] GetGestureDetecetd should return a Gestures bit_set not a Gesture. --- vendor/raylib/raylib.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/raylib/raylib.odin b/vendor/raylib/raylib.odin index 2bedf77c4..4236839f0 100644 --- a/vendor/raylib/raylib.odin +++ b/vendor/raylib/raylib.odin @@ -1133,7 +1133,7 @@ foreign lib { SetGesturesEnabled :: proc(flags: Gestures) --- // Enable a set of gestures using flags IsGestureDetected :: proc(gesture: Gesture) -> bool --- // Check if a gesture have been detected - GetGestureDetected :: proc() -> Gesture --- // Get latest detected gesture + GetGestureDetected :: proc() -> Gestures --- // Get latest detected gesture GetGestureHoldDuration :: proc() -> f32 --- // Get gesture hold time in milliseconds GetGestureDragVector :: proc() -> Vector2 --- // Get gesture drag vector GetGestureDragAngle :: proc() -> f32 --- // Get gesture drag angle From 20e5e95ff85cd0d590fa152aec3d8942c0d665e2 Mon Sep 17 00:00:00 2001 From: ryuukk <44361234+ryuukk@users.noreply.github.com> Date: Tue, 11 Jul 2023 03:45:08 +0200 Subject: [PATCH 131/144] Deprecate htons, htonl, ntohl, ntohs --- core/sys/windows/ws2_32.odin | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/sys/windows/ws2_32.odin b/core/sys/windows/ws2_32.odin index c09b714ca..6e5c37345 100644 --- a/core/sys/windows/ws2_32.odin +++ b/core/sys/windows/ws2_32.odin @@ -211,7 +211,9 @@ foreign ws2_32 { // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-ntohs) ntohs :: proc(netshort: c_ushort) -> c_ushort --- // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htonl) + @(deprecated("Use endian specific integers instead, https://odin-lang.org/docs/overview/#basic-types")) htonl :: proc(hostlong: c_ulong) -> c_ulong --- // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htons) + @(deprecated("Use endian specific integers instead, https://odin-lang.org/docs/overview/#basic-types")) htons :: proc(hostshort: c_ushort) -> c_ushort --- } From e1fae5b9027ad6e79b949639ed403593e1591356 Mon Sep 17 00:00:00 2001 From: ryuukk <44361234+ryuukk@users.noreply.github.com> Date: Tue, 11 Jul 2023 03:51:28 +0200 Subject: [PATCH 132/144] Fix attribute --- core/sys/windows/ws2_32.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/sys/windows/ws2_32.odin b/core/sys/windows/ws2_32.odin index 6e5c37345..7b9cf1b89 100644 --- a/core/sys/windows/ws2_32.odin +++ b/core/sys/windows/ws2_32.odin @@ -211,9 +211,9 @@ foreign ws2_32 { // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-ntohs) ntohs :: proc(netshort: c_ushort) -> c_ushort --- // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htonl) - @(deprecated("Use endian specific integers instead, https://odin-lang.org/docs/overview/#basic-types")) + @(deprecated="Use endian specific integers instead, https://odin-lang.org/docs/overview/#basic-types") htonl :: proc(hostlong: c_ulong) -> c_ulong --- // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htons) - @(deprecated("Use endian specific integers instead, https://odin-lang.org/docs/overview/#basic-types")) + @(deprecated="Use endian specific integers instead, https://odin-lang.org/docs/overview/#basic-types") htons :: proc(hostshort: c_ushort) -> c_ushort --- } From 46f408cc9f86bbc840864c26675d7ccfd242242c Mon Sep 17 00:00:00 2001 From: Zac Nowicki Date: Tue, 11 Jul 2023 07:43:16 -0400 Subject: [PATCH 133/144] Fixup link format --- core/sync/primitives.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/sync/primitives.odin b/core/sync/primitives.odin index fdb86d835..5e71f6336 100644 --- a/core/sync/primitives.odin +++ b/core/sync/primitives.odin @@ -7,7 +7,7 @@ current_thread_id :: proc "contextless" () -> int { return _current_thread_id() } -// A Mutex is a [mutual exclusion lock](https://en.wikipedia.org/wiki/Mutual_exclusion). +// A Mutex is a [[mutual exclusion lock; https://en.wikipedia.org/wiki/Mutual_exclusion]] // It can be used to prevent more than one thread from executing the same piece of code, // and thus prevent access to same piece of memory by multiple threads, at the same time. // From 52700d6a840d5635cf8ebc3f9ebf516f9c47f615 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Tue, 11 Jul 2023 19:30:44 +0200 Subject: [PATCH 134/144] use lb_emit_select instead of lb_emit_if for exit check --- src/llvm_backend.cpp | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 6a4046d8d..90716dde0 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1827,30 +1827,12 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star args[0] = lb_addr_load(p, all_tests_slice); lbValue result = lb_emit_call(p, runner, args); - lbBlock *block_success = lb_create_block(p, "success"); - lbBlock *block_failure = lb_create_block(p, "failure"); - - lbValue result_success = lb_emit_comp(p, Token_CmpEq, result, lb_const_bool(m, t_bool, true)); - lb_emit_if(p, result_success, block_success, block_failure); - lbValue exit_runner = lb_find_package_value(m, str_lit("os"), str_lit("exit")); - - lb_start_block(p, block_success); - { - auto exit_args = array_make(temporary_allocator(), 1); - exit_args[0] = lb_const_int(m, t_int, 0); - lb_emit_call(p, exit_runner, exit_args); - } - - lb_start_block(p, block_failure); - { - auto exit_args = array_make(temporary_allocator(), 1); - exit_args[0] = lb_const_int(m, t_int, 1); - lb_emit_call(p, exit_runner, exit_args); - } + auto exit_args = array_make(temporary_allocator(), 1); + exit_args[0] = lb_emit_select(p, result, lb_const_int(m, t_int, 0), lb_const_int(m, t_int, 1)); + lb_emit_call(p, exit_runner, exit_args, ProcInlining_none); } else { - if (m->info->entry_point != nullptr) { - lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point); + if (m->info->entry_point != nullptr) { lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point); lb_emit_call(p, entry_point, {}, ProcInlining_no_inline); } From 3cce9721257f921b81bd757c453fecb8b5308227 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Tue, 11 Jul 2023 19:33:14 +0200 Subject: [PATCH 135/144] fix indentation --- src/llvm_backend.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 90716dde0..938c9b2ac 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1832,19 +1832,20 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star exit_args[0] = lb_emit_select(p, result, lb_const_int(m, t_int, 0), lb_const_int(m, t_int, 1)); lb_emit_call(p, exit_runner, exit_args, ProcInlining_none); } else { - if (m->info->entry_point != nullptr) { lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point); + if (m->info->entry_point != nullptr) { + lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point); lb_emit_call(p, entry_point, {}, ProcInlining_no_inline); } if (call_cleanup) { - lbValue cleanup_runtime_value = {cleanup_runtime->value, cleanup_runtime->type}; - lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none); + lbValue cleanup_runtime_value = {cleanup_runtime->value, cleanup_runtime->type}; + lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none); } if (is_dll_main) { - LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 1, false)); + LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 1, false)); } else { - LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false)); + LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false)); } } From 8068a3899d8a92b35ad89a2bcfb7f34e5eb9487e Mon Sep 17 00:00:00 2001 From: hchac Date: Tue, 11 Jul 2023 16:23:54 -0400 Subject: [PATCH 136/144] Don't gitignore folders named odin. --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 824e0c203..3528af624 100644 --- a/.gitignore +++ b/.gitignore @@ -270,6 +270,7 @@ bin/ # - Linux/MacOS odin +!odin/ odin.dSYM *.bin demo.bin @@ -286,4 +287,4 @@ shared/ *.sublime-workspace examples/bug/ build.sh -!core/debug/ \ No newline at end of file +!core/debug/ From c6957e4e313b7c46e687c2869d5681a7f59ec500 Mon Sep 17 00:00:00 2001 From: Eric Johnson Date: Tue, 11 Jul 2023 13:41:12 -0700 Subject: [PATCH 137/144] Fix small doc mistakes in arena.odin --- core/mem/virtual/arena.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/mem/virtual/arena.odin b/core/mem/virtual/arena.odin index e73a39660..cfd35ab05 100644 --- a/core/mem/virtual/arena.odin +++ b/core/mem/virtual/arena.odin @@ -242,7 +242,7 @@ arena_growing_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, min return arena_growing_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size) } -// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy. +// Ability to bootstrap allocate a struct with an arena within the struct itself using the static variant strategy. @(require_results) arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) { bootstrap: Arena @@ -258,7 +258,7 @@ arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintpt return } -// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy. +// Ability to bootstrap allocate a struct with an arena within the struct itself using the static variant strategy. @(require_results) arena_static_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) { return arena_static_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved) @@ -271,7 +271,7 @@ arena_allocator :: proc(arena: ^Arena) -> mem.Allocator { return mem.Allocator{arena_allocator_proc, arena} } -// The allocator procedured by an `Allocator` produced by `arena_allocator` +// The allocator procedure used by an `Allocator` produced by `arena_allocator` arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, @@ -328,7 +328,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, -// An `Arena_Temp` is a way to produce temporary watermarks to reset a arena to a previous state. +// An `Arena_Temp` is a way to produce temporary watermarks to reset an arena to a previous state. // All uses of an `Arena_Temp` must be handled by ending them with `arena_temp_end` or ignoring them with `arena_temp_ignore`. Arena_Temp :: struct { arena: ^Arena, From 635d671ee7facfa1a2026a544dd1b6d1f3a6422a Mon Sep 17 00:00:00 2001 From: Brennen Shaughnessy Date: Sat, 15 Jul 2023 15:59:58 -0400 Subject: [PATCH 138/144] Fixed typo in GGPO bindings --- vendor/ggpo/ggpo.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/ggpo/ggpo.odin b/vendor/ggpo/ggpo.odin index d17c33638..b38f4fb65 100644 --- a/vendor/ggpo/ggpo.odin +++ b/vendor/ggpo/ggpo.odin @@ -50,7 +50,7 @@ Player :: struct { player_num: c.int, using u: struct #raw_union { local: struct {}, - remove: struct { + remote: struct { ip_address: [32]byte, port: u16, }, From dcf3023d93de612575acd9794b039e2514c010c0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 17 Jul 2023 12:06:48 +0100 Subject: [PATCH 139/144] Fix bug caused by incorrect assert --- src/llvm_backend_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index c30170553..c9d30af7a 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4171,7 +4171,7 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { // HACK TODO(bill): THIS IS A MASSIVE HACK!!!! if (is_type_union(ft) && !are_types_identical(fet, ft) && !is_type_untyped(fet)) { - GB_ASSERT_MSG(union_variant_index(ft, fet) > 0, "%s", type_to_string(fet)); + GB_ASSERT_MSG(union_variant_index(ft, fet) >= 0, "%s", type_to_string(fet)); lb_emit_store_union_variant(p, gep, field_expr, fet); } else { From 921530dd01e940600692caba0b19185399daa2e0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 17 Jul 2023 12:43:56 +0100 Subject: [PATCH 140/144] Fix to allow procedure groups on objective-c types --- src/check_decl.cpp | 124 +++++++++--------- src/check_expr.cpp | 178 ++++++++++++++++---------- src/checker.cpp | 48 +++++++ src/llvm_backend_expr.cpp | 3 +- src/types.cpp | 2 +- vendor/darwin/Metal/MetalClasses.odin | 4 +- 6 files changed, 233 insertions(+), 126 deletions(-) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index b651e33e6..2b2fb867c 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -757,6 +757,66 @@ gb_internal String handle_link_name(CheckerContext *ctx, Token token, String lin return link_name; } +gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeContext const &ac) { + if (!(ac.objc_name.len || ac.objc_is_class_method || ac.objc_type)) { + return; + } + if (ac.objc_name.len == 0 && ac.objc_is_class_method) { + error(e->token, "@(objc_name) is required with @(objc_is_class_method)"); + } else if (ac.objc_type == nullptr) { + error(e->token, "@(objc_name) requires that @(objc_type) to be set"); + } else if (ac.objc_name.len == 0 && ac.objc_type) { + error(e->token, "@(objc_name) is required with @(objc_type)"); + } else { + Type *t = ac.objc_type; + if (t->kind == Type_Named) { + Entity *tn = t->Named.type_name; + + GB_ASSERT(tn->kind == Entity_TypeName); + + if (tn->scope != e->scope) { + error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope"); + } else { + mutex_lock(&global_type_name_objc_metadata_mutex); + defer (mutex_unlock(&global_type_name_objc_metadata_mutex)); + + if (!tn->TypeName.objc_metadata) { + tn->TypeName.objc_metadata = create_type_name_obj_c_metadata(); + } + auto *md = tn->TypeName.objc_metadata; + mutex_lock(md->mutex); + defer (mutex_unlock(md->mutex)); + + if (!ac.objc_is_class_method) { + bool ok = true; + for (TypeNameObjCMetadataEntry const &entry : md->value_entries) { + if (entry.name == ac.objc_name) { + error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name)); + ok = false; + break; + } + } + if (ok) { + array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e}); + } + } else { + bool ok = true; + for (TypeNameObjCMetadataEntry const &entry : md->type_entries) { + if (entry.name == ac.objc_name) { + error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name)); + ok = false; + break; + } + } + if (ok) { + array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e}); + } + } + } + } + } +} + gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { GB_ASSERT(e->type == nullptr); if (d->proc_lit->kind != Ast_ProcLit) { @@ -840,62 +900,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode; - if (ac.objc_name.len || ac.objc_is_class_method || ac.objc_type) { - if (ac.objc_name.len == 0 && ac.objc_is_class_method) { - error(e->token, "@(objc_name) is required with @(objc_is_class_method)"); - } else if (ac.objc_type == nullptr) { - error(e->token, "@(objc_name) requires that @(objc_type) to be set"); - } else if (ac.objc_name.len == 0 && ac.objc_type) { - error(e->token, "@(objc_name) is required with @(objc_type)"); - } else { - Type *t = ac.objc_type; - if (t->kind == Type_Named) { - Entity *tn = t->Named.type_name; - - GB_ASSERT(tn->kind == Entity_TypeName); - - if (tn->scope != e->scope) { - error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope"); - } else { - mutex_lock(&global_type_name_objc_metadata_mutex); - defer (mutex_unlock(&global_type_name_objc_metadata_mutex)); - - if (!tn->TypeName.objc_metadata) { - tn->TypeName.objc_metadata = create_type_name_obj_c_metadata(); - } - auto *md = tn->TypeName.objc_metadata; - mutex_lock(md->mutex); - defer (mutex_unlock(md->mutex)); - - if (!ac.objc_is_class_method) { - bool ok = true; - for (TypeNameObjCMetadataEntry const &entry : md->value_entries) { - if (entry.name == ac.objc_name) { - error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name)); - ok = false; - break; - } - } - if (ok) { - array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e}); - } - } else { - bool ok = true; - for (TypeNameObjCMetadataEntry const &entry : md->type_entries) { - if (entry.name == ac.objc_name) { - error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name)); - ok = false; - break; - } - } - if (ok) { - array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e}); - } - } - } - } - } - } + check_objc_methods(ctx, e, ac); if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) { error(e->token, "Attributes @(require_target_feature=...) and @(enable_target_feature=...) cannot be used together"); @@ -1241,7 +1246,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed"); } -gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) { +gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) { GB_ASSERT(pg_entity->kind == Entity_ProcGroup); auto *pge = &pg_entity->ProcGroup; String proc_group_name = pg_entity->token.string; @@ -1366,6 +1371,11 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, } } + AttributeContext ac = {}; + check_decl_attributes(ctx, d->attributes, proc_group_attribute, &ac); + check_objc_methods(ctx, pg_entity, ac); + + } gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_type) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b662c231f..bb953ee2e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6328,9 +6328,46 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, print_argument_types(); } + if (procs.count == 0) { + procs = proc_group_entities_cloned(c, *operand); + } if (procs.count > 0) { error_line("Did you mean to use one of the following:\n"); } + isize max_name_length = 0; + isize max_type_length = 0; + for (Entity *proc : procs) { + Type *t = base_type(proc->type); + if (t == t_invalid) continue; + String prefix = {}; + String prefix_sep = {}; + if (proc->pkg) { + prefix = proc->pkg->name; + prefix_sep = str_lit("."); + } + String name = proc->token.string; + max_name_length = gb_max(max_name_length, prefix.len + prefix_sep.len + name.len); + + + + gbString pt; + if (t->Proc.node != nullptr) { + pt = expr_to_string(t->Proc.node); + } else { + pt = type_to_string(t); + } + + max_type_length = gb_max(max_type_length, gb_string_length(pt)); + gb_string_free(pt); + } + + isize max_spaces = gb_max(max_name_length, max_type_length); + char *spaces = gb_alloc_array(temporary_allocator(), char, max_spaces+1); + for (isize i = 0; i < max_spaces; i++) { + spaces[i] = ' '; + } + spaces[max_spaces] = 0; + for (Entity *proc : procs) { TokenPos pos = proc->token.pos; Type *t = base_type(proc->type); @@ -6350,12 +6387,23 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, prefix_sep = str_lit("."); } String name = proc->token.string; + isize len = prefix.len + prefix_sep.len + name.len; + + int name_padding = cast(int)gb_max(max_name_length - len, 0); + int type_padding = cast(int)gb_max(max_type_length - gb_string_length(pt), 0); char const *sep = "::"; if (proc->kind == Entity_Variable) { sep = ":="; } - error_line("\t%.*s%.*s%.*s %s %s at %s\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, token_pos_to_string(pos)); + error_line("\t%.*s%.*s%.*s %.*s%s %s %.*sat %s\n", + LIT(prefix), LIT(prefix_sep), LIT(name), + name_padding, spaces, + sep, + pt, + type_padding, spaces, + token_pos_to_string(pos) + ); } if (procs.count > 0) { error_line("\n"); @@ -9315,13 +9363,13 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast ExprKind kind = check_expr_base(c, &x, se->expr, nullptr); c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr; - if (x.mode == Addressing_Invalid || x.type == t_invalid) { + if (x.mode == Addressing_Invalid || (x.type == t_invalid && x.mode != Addressing_ProcGroup)) { o->mode = Addressing_Invalid; o->type = t_invalid; o->expr = node; return kind; } - if (!is_type_proc(x.type)) { + if (!is_type_proc(x.type) && x.mode != Addressing_ProcGroup) { gbString type_str = type_to_string(x.type); error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str); gb_string_free(type_str); @@ -9344,76 +9392,76 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast first_arg->state_flags |= StateFlag_SelectorCallExpr; } - Type *pt = base_type(x.type); - GB_ASSERT(pt->kind == Type_Proc); - Type *first_type = nullptr; - String first_arg_name = {}; - if (pt->Proc.param_count > 0) { - Entity *f = pt->Proc.params->Tuple.variables[0]; - first_type = f->type; - first_arg_name = f->token.string; - } - if (first_arg_name.len == 0) { - first_arg_name = str_lit("_"); - } + if (e->kind != Entity_ProcGroup) { + Type *pt = base_type(x.type); + GB_ASSERT_MSG(pt->kind == Type_Proc, "%.*s %.*s %s", LIT(e->token.string), LIT(entity_strings[e->kind]), type_to_string(x.type)); + Type *first_type = nullptr; + String first_arg_name = {}; + if (pt->Proc.param_count > 0) { + Entity *f = pt->Proc.params->Tuple.variables[0]; + first_type = f->type; + first_arg_name = f->token.string; + } + if (first_arg_name.len == 0) { + first_arg_name = str_lit("_"); + } - if (first_type == nullptr) { - error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter"); - o->mode = Addressing_Invalid; - o->type = t_invalid; - o->expr = node; - return Expr_Stmt; - } + if (first_type == nullptr) { + error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter"); + o->mode = Addressing_Invalid; + o->type = t_invalid; + o->expr = node; + return Expr_Stmt; + } - Operand y = {}; - y.mode = first_arg->tav.mode; - y.type = first_arg->tav.type; - y.value = first_arg->tav.value; + Operand y = {}; + y.mode = first_arg->tav.mode; + y.type = first_arg->tav.type; + y.value = first_arg->tav.value; - if (check_is_assignable_to(c, &y, first_type)) { - // Do nothing, it's valid - } else { - Operand z = y; - z.type = type_deref(y.type); - if (check_is_assignable_to(c, &z, first_type)) { - // NOTE(bill): AST GENERATION HACK! - Token op = {Token_Pointer}; - first_arg = ast_deref_expr(first_arg->file(), first_arg, op); - } else if (y.mode == Addressing_Variable) { - Operand w = y; - w.type = alloc_type_pointer(y.type); - if (check_is_assignable_to(c, &w, first_type)) { + if (check_is_assignable_to(c, &y, first_type)) { + // Do nothing, it's valid + } else { + Operand z = y; + z.type = type_deref(y.type); + if (check_is_assignable_to(c, &z, first_type)) { // NOTE(bill): AST GENERATION HACK! - Token op = {Token_And}; - first_arg = ast_unary_expr(first_arg->file(), op, first_arg); + Token op = {Token_Pointer}; + first_arg = ast_deref_expr(first_arg->file(), first_arg, op); + } else if (y.mode == Addressing_Variable) { + Operand w = y; + w.type = alloc_type_pointer(y.type); + if (check_is_assignable_to(c, &w, first_type)) { + // NOTE(bill): AST GENERATION HACK! + Token op = {Token_And}; + first_arg = ast_unary_expr(first_arg->file(), op, first_arg); + } + } + } + + if (ce->args.count > 0) { + bool fail = false; + bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue); + for (Ast *arg : ce->args) { + bool mix = false; + if (first_is_field_value) { + mix = arg->kind != Ast_FieldValue; + } else { + mix = arg->kind == Ast_FieldValue; + } + if (mix) { + fail = true; + break; + } + } + if (!fail && first_is_field_value) { + Token op = {Token_Eq}; + AstFile *f = first_arg->file(); + first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op); } } } - if (ce->args.count > 0) { - bool fail = false; - bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue); - for (Ast *arg : ce->args) { - bool mix = false; - if (first_is_field_value) { - mix = arg->kind != Ast_FieldValue; - } else { - mix = arg->kind == Ast_FieldValue; - } - if (mix) { - fail = true; - break; - } - } - if (!fail && first_is_field_value) { - Token op = {Token_Eq}; - AstFile *f = first_arg->file(); - first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op); - } - } - - - auto modified_args = slice_make(heap_allocator(), ce->args.count+1); modified_args[0] = first_arg; slice_copy(&modified_args, ce->args, 1); diff --git a/src/checker.cpp b/src/checker.cpp index 396a04d7a..2a2cb5c42 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2935,6 +2935,54 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) { return false; } +gb_internal DECL_ATTRIBUTE_PROC(proc_group_attribute) { + if (name == ATTRIBUTE_USER_TAG_NAME) { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind != ExactValue_String) { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; + } else if (name == "objc_name") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_String) { + if (string_is_valid_identifier(ev.value_string)) { + ac->objc_name = ev.value_string; + } else { + error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string)); + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; + } else if (name == "objc_is_class_method") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_Bool) { + ac->objc_is_class_method = ev.value_bool; + } else { + error(elem, "Expected a boolean value for '%.*s'", LIT(name)); + } + return true; + } else if (name == "objc_type") { + if (value == nullptr) { + error(elem, "Expected a type for '%.*s'", LIT(name)); + } else { + Type *objc_type = check_type(c, value); + if (objc_type != nullptr) { + if (!has_type_got_objc_class_attribute(objc_type)) { + gbString t = type_to_string(objc_type); + error(value, "'%.*s' expected a named type with the attribute @(obj_class=), got type %s", LIT(name), t); + gb_string_free(t); + } else { + ac->objc_type = objc_type; + } + } + } + return true; + } + return false; +} + + gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { if (name == ATTRIBUTE_USER_TAG_NAME) { ExactValue ev = check_decl_attribute_value(c, value); diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index c9d30af7a..5e6831fc2 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4519,8 +4519,9 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { Selection sel = lookup_field(type, selector, false); GB_ASSERT(sel.entity != nullptr); if (sel.pseudo_field) { - GB_ASSERT(sel.entity->kind == Entity_Procedure); + GB_ASSERT(sel.entity->kind == Entity_Procedure || sel.entity->kind == Entity_ProcGroup); Entity *e = entity_of_node(sel_node); + GB_ASSERT(e->kind == Entity_Procedure); return lb_addr(lb_find_value_from_entity(p->module, e)); } diff --git a/src/types.cpp b/src/types.cpp index 385ca926d..847aea9f3 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -3081,7 +3081,7 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name mutex_lock(md->mutex); defer (mutex_unlock(md->mutex)); for (TypeNameObjCMetadataEntry const &entry : md->value_entries) { - GB_ASSERT(entry.entity->kind == Entity_Procedure); + GB_ASSERT(entry.entity->kind == Entity_Procedure || entry.entity->kind == Entity_ProcGroup); if (entry.name == field_name) { sel.entity = entry.entity; sel.pseudo_field = true; diff --git a/vendor/darwin/Metal/MetalClasses.odin b/vendor/darwin/Metal/MetalClasses.odin index 17f22e1d3..9239182db 100644 --- a/vendor/darwin/Metal/MetalClasses.odin +++ b/vendor/darwin/Metal/MetalClasses.odin @@ -5388,8 +5388,8 @@ Device_newBufferWithLength :: #force_inline proc "c" (self: ^Device, length: NS. @(objc_type=Device, objc_name="newBuffer") Device_newBuffer :: proc{ - Device_newBufferWithBytes, - Device_newBufferWithBytesNoCopy, + // Device_newBufferWithBytes, + // Device_newBufferWithBytesNoCopy, Device_newBufferWithSlice, Device_newBufferWithSliceNoCopy, Device_newBufferWithLength, From 88485d5467653d2768a925b90b0345b9a9bb62a9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 17 Jul 2023 12:57:33 +0100 Subject: [PATCH 141/144] Improve scoring for polymorphic procedures --- src/check_expr.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index bb953ee2e..87b6e52d8 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6258,14 +6258,18 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, } isize index = i; + ValidIndexAndScore item = {}; + item.score = data.score; + if (data.gen_entity != nullptr) { array_add(&proc_entities, data.gen_entity); index = proc_entities.count-1; + + // prefer non-polymorphic procedures over polymorphic + item.score += assign_score_function(1); } - ValidIndexAndScore item = {}; item.index = index; - item.score = data.score; array_add(&valids, item); } } @@ -6348,8 +6352,6 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, String name = proc->token.string; max_name_length = gb_max(max_name_length, prefix.len + prefix_sep.len + name.len); - - gbString pt; if (t->Proc.node != nullptr) { pt = expr_to_string(t->Proc.node); @@ -6417,8 +6419,8 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name); print_argument_types(); - for (isize i = 0; i < valids.count; i++) { - Entity *proc = proc_entities[valids[i].index]; + for (auto const &valid : valids) { + Entity *proc = proc_entities[valid.index]; GB_ASSERT(proc != nullptr); TokenPos pos = proc->token.pos; Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc); From 0f28857c59346ab6239527d51403899ffbf6885e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 17 Jul 2023 13:03:58 +0100 Subject: [PATCH 142/144] Improve type inference for procedure group stuff --- src/check_expr.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 87b6e52d8..98154f33d 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6136,7 +6136,6 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, { // NOTE(bill, 2019-07-13): This code is used to improve the type inference for procedure groups // where the same positional parameter has the same type value (and ellipsis) - bool proc_arg_count_all_equal = true; isize proc_arg_count = -1; for (Entity *p : procs) { Type *pt = base_type(p->type); @@ -6144,15 +6143,12 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, if (proc_arg_count < 0) { proc_arg_count = pt->Proc.param_count; } else { - if (proc_arg_count != pt->Proc.param_count) { - proc_arg_count_all_equal = false; - break; - } + proc_arg_count = gb_min(proc_arg_count, pt->Proc.param_count); } } } - if (proc_arg_count >= 0 && proc_arg_count_all_equal) { + if (proc_arg_count >= 0) { lhs_count = proc_arg_count; if (lhs_count > 0) { lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count); From 1b3657122cb69b96ec54dd878e4fbdbb00641bcc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 17 Jul 2023 16:02:18 +0100 Subject: [PATCH 143/144] Uncomment procedures in procedure group --- vendor/darwin/Metal/MetalClasses.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vendor/darwin/Metal/MetalClasses.odin b/vendor/darwin/Metal/MetalClasses.odin index 9239182db..17f22e1d3 100644 --- a/vendor/darwin/Metal/MetalClasses.odin +++ b/vendor/darwin/Metal/MetalClasses.odin @@ -5388,8 +5388,8 @@ Device_newBufferWithLength :: #force_inline proc "c" (self: ^Device, length: NS. @(objc_type=Device, objc_name="newBuffer") Device_newBuffer :: proc{ - // Device_newBufferWithBytes, - // Device_newBufferWithBytesNoCopy, + Device_newBufferWithBytes, + Device_newBufferWithBytesNoCopy, Device_newBufferWithSlice, Device_newBufferWithSliceNoCopy, Device_newBufferWithLength, From f9c083073e3d4c64c5e53f79db95e76deb67a39d Mon Sep 17 00:00:00 2001 From: jason Date: Fri, 21 Jul 2023 15:44:39 -0400 Subject: [PATCH 144/144] coalesce tombstones in map insert --- core/runtime/dynamic_map_internal.odin | 216 ++++++++++++++----------- 1 file changed, 118 insertions(+), 98 deletions(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 05c03028f..d34c29d4b 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -414,68 +414,21 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^ tk := map_cell_index_dynamic(sk, info.ks, 1) tv := map_cell_index_dynamic(sv, info.vs, 1) - for { - hp := &hs[pos] - element_hash := hp^ + swap_loop: for { + element_hash := hs[pos] if map_hash_is_empty(element_hash) { - kp := map_cell_index_dynamic(ks, info.ks, pos) - vp := map_cell_index_dynamic(vs, info.vs, pos) - intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(k), size_of_k) - intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(v), size_of_v) - hp^ = h + k_dst := map_cell_index_dynamic(ks, info.ks, pos) + v_dst := map_cell_index_dynamic(vs, info.vs, pos) + intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k) + intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v) + hs[pos] = h - return result if result != 0 else vp + return result if result != 0 else v_dst } if map_hash_is_deleted(element_hash) { - next_pos := (pos + 1) & mask - - // backward shift - for !map_hash_is_empty(hs[next_pos]) { - probe_distance := map_probe_distance(m^, hs[next_pos], next_pos) - if probe_distance == 0 { - break - } - probe_distance -= 1 - - kp := map_cell_index_dynamic(ks, info.ks, pos) - vp := map_cell_index_dynamic(vs, info.vs, pos) - kn := map_cell_index_dynamic(ks, info.ks, next_pos) - vn := map_cell_index_dynamic(vs, info.vs, next_pos) - - if distance > probe_distance { - if result == 0 { - result = vp - } - // move stored into pos; store next - intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(k), size_of_k) - intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(v), size_of_v) - hs[pos] = h - - intrinsics.mem_copy_non_overlapping(rawptr(k), rawptr(kn), size_of_k) - intrinsics.mem_copy_non_overlapping(rawptr(v), rawptr(vn), size_of_v) - h = hs[next_pos] - } else { - // move next back 1 - intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(kn), size_of_k) - intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(vn), size_of_v) - hs[pos] = hs[next_pos] - distance = probe_distance - } - hs[next_pos] = 0 - pos = (pos + 1) & mask - next_pos = (next_pos + 1) & mask - distance += 1 - } - - kp := map_cell_index_dynamic(ks, info.ks, pos) - vp := map_cell_index_dynamic(vs, info.vs, pos) - intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(k), size_of_k) - intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(v), size_of_v) - hs[pos] = h - - return result if result != 0 else vp + break swap_loop } if probe_distance := map_probe_distance(m^, element_hash, pos); distance > probe_distance { @@ -495,8 +448,8 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^ intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(tv), size_of_v) th := h - h = hp^ - hp^ = th + h = hs[pos] + hs[pos] = th distance = probe_distance } @@ -504,6 +457,103 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^ pos = (pos + 1) & mask distance += 1 } + + // backward shift loop + hs[pos] = 0 + look_ahead: uintptr = 1 + for { + la_pos := (pos + look_ahead) & mask + element_hash := hs[la_pos] + + if map_hash_is_deleted(element_hash) { + look_ahead += 1 + hs[la_pos] = 0 + continue + } + + k_dst := map_cell_index_dynamic(ks, info.ks, pos) + v_dst := map_cell_index_dynamic(vs, info.vs, pos) + + if map_hash_is_empty(element_hash) { + intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k) + intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v) + hs[pos] = h + + return result if result != 0 else v_dst + } + + k_src := map_cell_index_dynamic(ks, info.ks, la_pos) + v_src := map_cell_index_dynamic(vs, info.vs, la_pos) + probe_distance := map_probe_distance(m^, element_hash, la_pos) + + if probe_distance < look_ahead { + // probed can be made ideal while placing saved (ending condition) + if result == 0 { + result = v_dst + } + intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k) + intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v) + hs[pos] = h + + // This will be an ideal move + pos = (la_pos - probe_distance) & mask + look_ahead -= probe_distance + + // shift until we hit ideal/empty + for probe_distance != 0 { + k_dst = map_cell_index_dynamic(ks, info.ks, pos) + v_dst = map_cell_index_dynamic(vs, info.vs, pos) + + intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k_src), size_of_k) + intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v_src), size_of_v) + hs[pos] = element_hash + hs[la_pos] = 0 + + pos = (pos + 1) & mask + la_pos = (la_pos + 1) & mask + look_ahead = (la_pos - pos) & mask + element_hash = hs[la_pos] + if map_hash_is_empty(element_hash) { + return + } + + probe_distance = map_probe_distance(m^, element_hash, la_pos) + if probe_distance == 0 { + return + } + // can be ideal? + if probe_distance < look_ahead { + pos = (la_pos - probe_distance) & mask + } + k_src = map_cell_index_dynamic(ks, info.ks, la_pos) + v_src = map_cell_index_dynamic(vs, info.vs, la_pos) + } + return + } else if distance < probe_distance - look_ahead { + // shift back probed + intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k_src), size_of_k) + intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v_src), size_of_v) + hs[pos] = element_hash + hs[la_pos] = 0 + } else { + // place saved, save probed + if result == 0 { + result = v_dst + } + intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k) + intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v) + hs[pos] = h + + intrinsics.mem_copy_non_overlapping(rawptr(k), rawptr(k_src), size_of_k) + intrinsics.mem_copy_non_overlapping(rawptr(v), rawptr(v_src), size_of_v) + h = hs[la_pos] + hs[la_pos] = 0 + distance = probe_distance - look_ahead + } + + pos = (pos + 1) & mask + distance += 1 + } } @(require_results) @@ -696,49 +746,19 @@ map_erase_dynamic :: #force_inline proc "contextless" (#no_alias m: ^Raw_Map, #n m.len -= 1 ok = true - { // coalesce tombstones - // HACK NOTE(bill): This is an ugly bodge but it is coalescing the tombstone slots - mask := (uintptr(1)<