Commit d9fed5cdfd

Andrew Kelley <andrew@ziglang.org>
2019-08-30 20:53:44
align(@alignOf(T)) T does not force resolution of T
1 parent 9666706
Changed files (4)
src
std
test
stage1
behavior
src/ir.cpp
@@ -12613,10 +12613,27 @@ static bool ir_resolve_const_align(CodeGen *codegen, IrExecutable *exec, AstNode
     return true;
 }
 
-static bool ir_resolve_align(IrAnalyze *ira, IrInstruction *value, uint32_t *out) {
+static bool ir_resolve_align(IrAnalyze *ira, IrInstruction *value, ZigType *elem_type, uint32_t *out) {
     if (type_is_invalid(value->value.type))
         return false;
 
+    // Look for this pattern: `*align(@alignOf(T)) T`.
+    // This can be resolved to be `*out = 0` without resolving any alignment.
+    if (elem_type != nullptr && value->value.special == ConstValSpecialLazy &&
+        value->value.data.x_lazy->id == LazyValueIdAlignOf)
+    {
+        LazyValueAlignOf *lazy_align_of = reinterpret_cast<LazyValueAlignOf *>(value->value.data.x_lazy);
+
+        ZigType *lazy_elem_type = ir_resolve_type(lazy_align_of->ira, lazy_align_of->target_type);
+        if (type_is_invalid(lazy_elem_type))
+            return false;
+
+        if (elem_type == lazy_elem_type) {
+            *out = 0;
+            return true;
+        }
+    }
+
     IrInstruction *casted_value = ir_implicit_cast(ira, value, get_align_amt_type(ira->codegen));
     if (type_is_invalid(casted_value->value.type))
         return false;
@@ -14424,7 +14441,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira,
         }
         var->align_bytes = get_abi_alignment(ira->codegen, result_type);
     } else {
-        if (!ir_resolve_align(ira, decl_var_instruction->align_value->child, &var->align_bytes)) {
+        if (!ir_resolve_align(ira, decl_var_instruction->align_value->child, nullptr, &var->align_bytes)) {
             var->var_type = ira->codegen->builtin_types.entry_invalid;
         }
     }
@@ -14879,7 +14896,7 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe
 
             if (alloca_src->base.child == nullptr || is_comptime) {
                 uint32_t align = 0;
-                if (alloca_src->align != nullptr && !ir_resolve_align(ira, alloca_src->align->child, &align)) {
+                if (alloca_src->align != nullptr && !ir_resolve_align(ira, alloca_src->align->child, nullptr, &align)) {
                     return ira->codegen->invalid_instruction;
                 }
                 IrInstruction *alloca_gen;
@@ -15896,7 +15913,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
             copy_const_val(&const_instruction->base.value, align_result, true);
 
             uint32_t align_bytes = 0;
-            ir_resolve_align(ira, &const_instruction->base, &align_bytes);
+            ir_resolve_align(ira, &const_instruction->base, nullptr, &align_bytes);
             impl_fn->align_bytes = align_bytes;
             inst_fn_type_id.alignment = align_bytes;
         }
@@ -23948,7 +23965,7 @@ static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruct
 static IrInstruction *ir_analyze_instruction_align_cast(IrAnalyze *ira, IrInstructionAlignCast *instruction) {
     uint32_t align_bytes;
     IrInstruction *align_bytes_inst = instruction->align_bytes->child;
-    if (!ir_resolve_align(ira, align_bytes_inst, &align_bytes))
+    if (!ir_resolve_align(ira, align_bytes_inst, nullptr, &align_bytes))
         return ira->codegen->invalid_instruction;
 
     IrInstruction *target = instruction->target->child;
@@ -23974,7 +23991,7 @@ static IrInstruction *ir_analyze_instruction_opaque_type(IrAnalyze *ira, IrInstr
 static IrInstruction *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrInstructionSetAlignStack *instruction) {
     uint32_t align_bytes;
     IrInstruction *align_bytes_inst = instruction->align_bytes->child;
-    if (!ir_resolve_align(ira, align_bytes_inst, &align_bytes))
+    if (!ir_resolve_align(ira, align_bytes_inst, nullptr, &align_bytes))
         return ira->codegen->invalid_instruction;
 
     if (align_bytes > 256) {
@@ -25555,7 +25572,7 @@ static ZigType *ir_resolve_lazy_fn_type(IrAnalyze *ira, AstNode *source_node, La
     }
 
     if (lazy_fn_type->align_inst != nullptr) {
-        if (!ir_resolve_align(ira, lazy_fn_type->align_inst, &fn_type_id.alignment))
+        if (!ir_resolve_align(ira, lazy_fn_type->align_inst, nullptr, &fn_type_id.alignment))
             return nullptr;
     }
 
@@ -25690,14 +25707,15 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) {
             LazyValueSliceType *lazy_slice_type = reinterpret_cast<LazyValueSliceType *>(val->data.x_lazy);
             IrAnalyze *ira = lazy_slice_type->ira;
 
+            ZigType *elem_type = ir_resolve_type(ira, lazy_slice_type->elem_type);
+            if (type_is_invalid(elem_type))
+                return ErrorSemanticAnalyzeFail;
+
             uint32_t align_bytes = 0;
             if (lazy_slice_type->align_inst != nullptr) {
-                if (!ir_resolve_align(ira, lazy_slice_type->align_inst, &align_bytes))
+                if (!ir_resolve_align(ira, lazy_slice_type->align_inst, elem_type, &align_bytes))
                     return ErrorSemanticAnalyzeFail;
             }
-            ZigType *elem_type = ir_resolve_type(ira, lazy_slice_type->elem_type);
-            if (type_is_invalid(elem_type))
-                return ErrorSemanticAnalyzeFail;
 
             switch (elem_type->id) {
                 case ZigTypeIdInvalid: // handled above
@@ -25750,14 +25768,15 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) {
             LazyValuePtrType *lazy_ptr_type = reinterpret_cast<LazyValuePtrType *>(val->data.x_lazy);
             IrAnalyze *ira = lazy_ptr_type->ira;
 
+            ZigType *elem_type = ir_resolve_type(ira, lazy_ptr_type->elem_type);
+            if (type_is_invalid(elem_type))
+                return ErrorSemanticAnalyzeFail;
+
             uint32_t align_bytes = 0;
             if (lazy_ptr_type->align_inst != nullptr) {
-                if (!ir_resolve_align(ira, lazy_ptr_type->align_inst, &align_bytes))
+                if (!ir_resolve_align(ira, lazy_ptr_type->align_inst, elem_type, &align_bytes))
                     return ErrorSemanticAnalyzeFail;
             }
-            ZigType *elem_type = ir_resolve_type(ira, lazy_ptr_type->elem_type);
-            if (type_is_invalid(elem_type))
-                return ErrorSemanticAnalyzeFail;
 
             if (elem_type->id == ZigTypeIdUnreachable) {
                 ir_add_error(ira, lazy_ptr_type->elem_type,
std/array_list.zig
@@ -10,6 +10,11 @@ pub fn ArrayList(comptime T: type) type {
 }
 
 pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
+    if (alignment) |a| {
+        if (a == @alignOf(T)) {
+            return AlignedArrayList(T, null);
+        }
+    }
     return struct {
         const Self = @This();
 
std/mem.zig
@@ -94,24 +94,30 @@ pub const Allocator = struct {
     }
 
     pub fn alloc(self: *Allocator, comptime T: type, n: usize) Error![]T {
-        return self.alignedAlloc(T, @alignOf(T), n);
+        return self.alignedAlloc(T, null, n);
     }
 
     pub fn alignedAlloc(
         self: *Allocator,
         comptime T: type,
-        comptime alignment: u29,
+        /// null means naturally aligned
+        comptime alignment: ?u29,
         n: usize,
-    ) Error![]align(alignment) T {
+    ) Error![]align(alignment orelse @alignOf(T)) T {
+        const a = if (alignment) |a| blk: {
+            if (a == @alignOf(T)) return alignedAlloc(self, T, null, n);
+            break :blk a;
+        } else @alignOf(T);
+
         if (n == 0) {
-            return ([*]align(alignment) T)(undefined)[0..0];
+            return ([*]align(a) T)(undefined)[0..0];
         }
 
         const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
-        const byte_slice = try self.reallocFn(self, ([*]u8)(undefined)[0..0], undefined, byte_count, alignment);
+        const byte_slice = try self.reallocFn(self, ([*]u8)(undefined)[0..0], undefined, byte_count, a);
         assert(byte_slice.len == byte_count);
         @memset(byte_slice.ptr, undefined, byte_slice.len);
-        return @bytesToSlice(T, @alignCast(alignment, byte_slice));
+        return @bytesToSlice(T, @alignCast(a, byte_slice));
     }
 
     /// This function requests a new byte size for an existing allocation,
test/stage1/behavior/align.zig
@@ -1,4 +1,5 @@
-const expect = @import("std").testing.expect;
+const std = @import("std");
+const expect = std.testing.expect;
 const builtin = @import("builtin");
 
 var foo: u8 align(4) = 100;
@@ -305,3 +306,25 @@ test "struct field explicit alignment" {
     comptime expect(@typeOf(&node.massive_byte) == *align(64) u8);
     expect(@ptrToInt(&node.massive_byte) % 64 == 0);
 }
+
+test "align(@alignOf(T)) T does not force resolution of T" {
+    const S = struct {
+        const A = struct {
+            a: *align(@alignOf(A)) A,
+        };
+        fn doTheTest() void {
+            suspend {
+                resume @frame();
+            }
+            _ = bar(@Frame(doTheTest));
+        }
+        fn bar(comptime T: type) *align(@alignOf(T)) T {
+            ok = true;
+            return undefined;
+        }
+
+        var ok = false;
+    };
+    _ = async S.doTheTest();
+    expect(S.ok);
+}