Skip to content

Commit 4ea9a4b

Browse files
committed
breaking syntax change: orelse keyword instead of ??
use the `zig-fmt-optional-default` branch to have zig fmt automatically do the changes. closes #1023
1 parent ec1b6f6 commit 4ea9a4b

33 files changed

+187
-189
lines changed

build.zig

+3-3
Original file line numberDiff line numberDiff line change
@@ -102,19 +102,19 @@ pub fn build(b: *Builder) !void {
102102

103103
b.default_step.dependOn(&exe.step);
104104

105-
const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") ?? false;
105+
const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") orelse false;
106106
if (!skip_self_hosted) {
107107
test_step.dependOn(&exe.step);
108108
}
109-
const verbose_link_exe = b.option(bool, "verbose-link", "Print link command for self hosted compiler") ?? false;
109+
const verbose_link_exe = b.option(bool, "verbose-link", "Print link command for self hosted compiler") orelse false;
110110
exe.setVerboseLink(verbose_link_exe);
111111

112112
b.installArtifact(exe);
113113
installStdLib(b, std_files);
114114
installCHeaders(b, c_header_files);
115115

116116
const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");
117-
const with_lldb = b.option(bool, "with-lldb", "Run tests in LLDB to get a backtrace if one fails") ?? false;
117+
const with_lldb = b.option(bool, "with-lldb", "Run tests in LLDB to get a backtrace if one fails") orelse false;
118118

119119
test_step.dependOn(docs_step);
120120

doc/docgen.zig

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ pub fn main() !void {
2525

2626
if (!args_it.skip()) @panic("expected self arg");
2727

28-
const zig_exe = try (args_it.next(allocator) ?? @panic("expected zig exe arg"));
28+
const zig_exe = try (args_it.next(allocator) orelse @panic("expected zig exe arg"));
2929
defer allocator.free(zig_exe);
3030

31-
const in_file_name = try (args_it.next(allocator) ?? @panic("expected input arg"));
31+
const in_file_name = try (args_it.next(allocator) orelse @panic("expected input arg"));
3232
defer allocator.free(in_file_name);
3333

34-
const out_file_name = try (args_it.next(allocator) ?? @panic("expected output arg"));
34+
const out_file_name = try (args_it.next(allocator) orelse @panic("expected output arg"));
3535
defer allocator.free(out_file_name);
3636

3737
var in_file = try os.File.openRead(allocator, in_file_name);

doc/langref.html.in

+8-8
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,7 @@ a ^= b</code></pre></td>
985985
</td>
986986
</tr>
987987
<tr>
988-
<td><pre><code class="zig">a ?? b</code></pre></td>
988+
<td><pre><code class="zig">a orelse b</code></pre></td>
989989
<td>
990990
<ul>
991991
<li>{#link|Optionals#}</li>
@@ -998,7 +998,7 @@ a ^= b</code></pre></td>
998998
</td>
999999
<td>
10001000
<pre><code class="zig">const value: ?u32 = null;
1001-
const unwrapped = value ?? 1234;
1001+
const unwrapped = value orelse 1234;
10021002
unwrapped == 1234</code></pre>
10031003
</td>
10041004
</tr>
@@ -1011,7 +1011,7 @@ unwrapped == 1234</code></pre>
10111011
</td>
10121012
<td>
10131013
Equivalent to:
1014-
<pre><code class="zig">a ?? unreachable</code></pre>
1014+
<pre><code class="zig">a orelse unreachable</code></pre>
10151015
</td>
10161016
<td>
10171017
<pre><code class="zig">const value: ?u32 = 5678;
@@ -1278,7 +1278,7 @@ x{} x.* x.?
12781278
== != &lt; &gt; &lt;= &gt;=
12791279
and
12801280
or
1281-
?? catch
1281+
orelse catch
12821282
= *= /= %= += -= &lt;&lt;= &gt;&gt;= &amp;= ^= |=</code></pre>
12831283
{#header_close#}
12841284
{#header_close#}
@@ -3062,7 +3062,7 @@ fn createFoo(param: i32) !Foo {
30623062
// but we want to return it if the function succeeds.
30633063
errdefer deallocateFoo(foo);
30643064

3065-
const tmp_buf = allocateTmpBuffer() ?? return error.OutOfMemory;
3065+
const tmp_buf = allocateTmpBuffer() orelse return error.OutOfMemory;
30663066
// tmp_buf is truly a temporary resource, and we for sure want to clean it up
30673067
// before this block leaves scope
30683068
defer deallocateTmpBuffer(tmp_buf);
@@ -3219,13 +3219,13 @@ struct Foo *do_a_thing(void) {
32193219
extern fn malloc(size: size_t) ?*u8;
32203220

32213221
fn doAThing() ?*Foo {
3222-
const ptr = malloc(1234) ?? return null;
3222+
const ptr = malloc(1234) orelse return null;
32233223
// ...
32243224
}
32253225
{#code_end#}
32263226
<p>
32273227
Here, Zig is at least as convenient, if not more, than C. And, the type of "ptr"
3228-
is <code>*u8</code> <em>not</em> <code>?*u8</code>. The <code>??</code> operator
3228+
is <code>*u8</code> <em>not</em> <code>?*u8</code>. The <code>orelse</code> keyword
32293229
unwrapped the optional type and therefore <code>ptr</code> is guaranteed to be non-null everywhere
32303230
it is used in the function.
32313231
</p>
@@ -5941,7 +5941,7 @@ AsmClobbers= ":" list(String, ",")
59415941

59425942
UnwrapExpression = BoolOrExpression (UnwrapOptional | UnwrapError) | BoolOrExpression
59435943

5944-
UnwrapOptional = "??" Expression
5944+
UnwrapOptional = "orelse" Expression
59455945

59465946
UnwrapError = "catch" option("|" Symbol "|") Expression
59475947

src-self-hosted/main.zig

+7-7
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ fn cmdBuild(allocator: *Allocator, args: []const []const u8) !void {
212212
const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig");
213213
defer allocator.free(build_runner_path);
214214

215-
const build_file = flags.single("build-file") ?? "build.zig";
215+
const build_file = flags.single("build-file") orelse "build.zig";
216216
const build_file_abs = try os.path.resolve(allocator, ".", build_file);
217217
defer allocator.free(build_file_abs);
218218

@@ -516,7 +516,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
516516

517517
const basename = os.path.basename(in_file.?);
518518
var it = mem.split(basename, ".");
519-
const root_name = it.next() ?? {
519+
const root_name = it.next() orelse {
520520
try stderr.write("file name cannot be empty\n");
521521
os.exit(1);
522522
};
@@ -535,7 +535,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
535535

536536
const zig_root_source_file = in_file;
537537

538-
const full_cache_dir = os.path.resolve(allocator, ".", flags.single("cache-dir") ?? "zig-cache"[0..]) catch {
538+
const full_cache_dir = os.path.resolve(allocator, ".", flags.single("cache-dir") orelse "zig-cache"[0..]) catch {
539539
os.exit(1);
540540
};
541541
defer allocator.free(full_cache_dir);
@@ -555,9 +555,9 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
555555
);
556556
defer module.destroy();
557557

558-
module.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") ?? "0", 10);
559-
module.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") ?? "0", 10);
560-
module.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") ?? "0", 10);
558+
module.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10);
559+
module.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10);
560+
module.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10);
561561

562562
module.is_test = false;
563563

@@ -652,7 +652,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
652652
}
653653

654654
try module.build();
655-
try module.link(flags.single("out-file") ?? null);
655+
try module.link(flags.single("out-file") orelse null);
656656

657657
if (flags.present("print-timing-info")) {
658658
// codegen_print_timing_info(g, stderr);

src-self-hosted/module.zig

+4-4
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,13 @@ pub const Module = struct {
130130
var name_buffer = try Buffer.init(allocator, name);
131131
errdefer name_buffer.deinit();
132132

133-
const context = c.LLVMContextCreate() ?? return error.OutOfMemory;
133+
const context = c.LLVMContextCreate() orelse return error.OutOfMemory;
134134
errdefer c.LLVMContextDispose(context);
135135

136-
const module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) ?? return error.OutOfMemory;
136+
const module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) orelse return error.OutOfMemory;
137137
errdefer c.LLVMDisposeModule(module);
138138

139-
const builder = c.LLVMCreateBuilderInContext(context) ?? return error.OutOfMemory;
139+
const builder = c.LLVMCreateBuilderInContext(context) orelse return error.OutOfMemory;
140140
errdefer c.LLVMDisposeBuilder(builder);
141141

142142
const module_ptr = try allocator.create(Module);
@@ -223,7 +223,7 @@ pub const Module = struct {
223223
c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr);
224224
}
225225

226-
const root_src_path = self.root_src_path ?? @panic("TODO handle null root src path");
226+
const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path");
227227
const root_src_real_path = os.path.real(self.allocator, root_src_path) catch |err| {
228228
try printError("unable to get real path '{}': {}", root_src_path, err);
229229
return err;

src/all_types.hpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@ enum NodeType {
387387
NodeTypeSliceExpr,
388388
NodeTypeFieldAccessExpr,
389389
NodeTypePtrDeref,
390+
NodeTypeUnwrapOptional,
390391
NodeTypeUse,
391392
NodeTypeBoolLiteral,
392393
NodeTypeNullLiteral,
@@ -575,6 +576,10 @@ struct AstNodeCatchExpr {
575576
AstNode *op2;
576577
};
577578

579+
struct AstNodeUnwrapOptional {
580+
AstNode *expr;
581+
};
582+
578583
enum CastOp {
579584
CastOpNoCast, // signifies the function call expression is not a cast
580585
CastOpNoop, // fn call expr is a cast, but does nothing
@@ -624,7 +629,6 @@ enum PrefixOp {
624629
PrefixOpNegation,
625630
PrefixOpNegationWrap,
626631
PrefixOpOptional,
627-
PrefixOpUnwrapOptional,
628632
PrefixOpAddrOf,
629633
};
630634

@@ -909,6 +913,7 @@ struct AstNode {
909913
AstNodeTestDecl test_decl;
910914
AstNodeBinOpExpr bin_op_expr;
911915
AstNodeCatchExpr unwrap_err_expr;
916+
AstNodeUnwrapOptional unwrap_optional;
912917
AstNodePrefixOpExpr prefix_op_expr;
913918
AstNodePointerType pointer_type;
914919
AstNodeFnCallExpr fn_call_expr;

src/analyze.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -3308,6 +3308,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
33083308
case NodeTypeAsmExpr:
33093309
case NodeTypeFieldAccessExpr:
33103310
case NodeTypePtrDeref:
3311+
case NodeTypeUnwrapOptional:
33113312
case NodeTypeStructField:
33123313
case NodeTypeContainerInitExpr:
33133314
case NodeTypeStructValueField:

src/ast_render.cpp

+10-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ static const char *bin_op_str(BinOpType bin_op) {
5050
case BinOpTypeAssignBitXor: return "^=";
5151
case BinOpTypeAssignBitOr: return "|=";
5252
case BinOpTypeAssignMergeErrorSets: return "||=";
53-
case BinOpTypeUnwrapOptional: return "??";
53+
case BinOpTypeUnwrapOptional: return "orelse";
5454
case BinOpTypeArrayCat: return "++";
5555
case BinOpTypeArrayMult: return "**";
5656
case BinOpTypeErrorUnion: return "!";
@@ -67,7 +67,6 @@ static const char *prefix_op_str(PrefixOp prefix_op) {
6767
case PrefixOpBoolNot: return "!";
6868
case PrefixOpBinNot: return "~";
6969
case PrefixOpOptional: return "?";
70-
case PrefixOpUnwrapOptional: return "??";
7170
case PrefixOpAddrOf: return "&";
7271
}
7372
zig_unreachable();
@@ -222,6 +221,8 @@ static const char *node_type_str(NodeType node_type) {
222221
return "FieldAccessExpr";
223222
case NodeTypePtrDeref:
224223
return "PtrDerefExpr";
224+
case NodeTypeUnwrapOptional:
225+
return "UnwrapOptional";
225226
case NodeTypeContainerDecl:
226227
return "ContainerDecl";
227228
case NodeTypeStructField:
@@ -711,6 +712,13 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
711712
fprintf(ar->f, ".*");
712713
break;
713714
}
715+
case NodeTypeUnwrapOptional:
716+
{
717+
AstNode *lhs = node->data.unwrap_optional.expr;
718+
render_node_ungrouped(ar, lhs);
719+
fprintf(ar->f, ".?");
720+
break;
721+
}
714722
case NodeTypeUndefinedLiteral:
715723
fprintf(ar->f, "undefined");
716724
break;

src/ir.cpp

+13-18
Original file line numberDiff line numberDiff line change
@@ -4661,21 +4661,6 @@ static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode
46614661
return ir_build_load_ptr(irb, scope, source_node, payload_ptr);
46624662
}
46634663

4664-
static IrInstruction *ir_gen_maybe_assert_ok(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
4665-
assert(node->type == NodeTypePrefixOpExpr);
4666-
AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
4667-
4668-
IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR);
4669-
if (maybe_ptr == irb->codegen->invalid_instruction)
4670-
return irb->codegen->invalid_instruction;
4671-
4672-
IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true);
4673-
if (lval.is_ptr)
4674-
return unwrapped_ptr;
4675-
4676-
return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
4677-
}
4678-
46794664
static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *node) {
46804665
assert(node->type == NodeTypePrefixOpExpr);
46814666
AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
@@ -4705,8 +4690,6 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod
47054690
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval);
47064691
case PrefixOpOptional:
47074692
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval);
4708-
case PrefixOpUnwrapOptional:
4709-
return ir_gen_maybe_assert_ok(irb, scope, node, lval);
47104693
case PrefixOpAddrOf: {
47114694
AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
47124695
return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR), lval);
@@ -6541,14 +6524,26 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
65416524
return ir_build_load_ptr(irb, scope, node, ptr_instruction);
65426525
}
65436526
case NodeTypePtrDeref: {
6544-
assert(node->type == NodeTypePtrDeref);
65456527
AstNode *expr_node = node->data.ptr_deref_expr.target;
65466528
IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval);
65476529
if (value == irb->codegen->invalid_instruction)
65486530
return value;
65496531

65506532
return ir_build_un_op(irb, scope, node, IrUnOpDereference, value);
65516533
}
6534+
case NodeTypeUnwrapOptional: {
6535+
AstNode *expr_node = node->data.unwrap_optional.expr;
6536+
6537+
IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR);
6538+
if (maybe_ptr == irb->codegen->invalid_instruction)
6539+
return irb->codegen->invalid_instruction;
6540+
6541+
IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true);
6542+
if (lval.is_ptr)
6543+
return unwrapped_ptr;
6544+
6545+
return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
6546+
}
65526547
case NodeTypeThisLiteral:
65536548
return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval);
65546549
case NodeTypeBoolLiteral:

src/parser.cpp

+7-6
Original file line numberDiff line numberDiff line change
@@ -1151,9 +1151,8 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index,
11511151
} else if (token->id == TokenIdQuestion) {
11521152
*token_index += 1;
11531153

1154-
AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, first_token);
1155-
node->data.prefix_op_expr.prefix_op = PrefixOpUnwrapOptional;
1156-
node->data.prefix_op_expr.primary_expr = primary_expr;
1154+
AstNode *node = ast_create_node(pc, NodeTypeUnwrapOptional, first_token);
1155+
node->data.unwrap_optional.expr = primary_expr;
11571156

11581157
primary_expr = node;
11591158
} else {
@@ -1173,7 +1172,6 @@ static PrefixOp tok_to_prefix_op(Token *token) {
11731172
case TokenIdMinusPercent: return PrefixOpNegationWrap;
11741173
case TokenIdTilde: return PrefixOpBinNot;
11751174
case TokenIdQuestion: return PrefixOpOptional;
1176-
case TokenIdDoubleQuestion: return PrefixOpUnwrapOptional;
11771175
case TokenIdAmpersand: return PrefixOpAddrOf;
11781176
default: return PrefixOpInvalid;
11791177
}
@@ -2312,7 +2310,7 @@ static BinOpType ast_parse_ass_op(ParseContext *pc, size_t *token_index, bool ma
23122310

23132311
/*
23142312
UnwrapExpression : BoolOrExpression (UnwrapOptional | UnwrapError) | BoolOrExpression
2315-
UnwrapOptional : "??" BoolOrExpression
2313+
UnwrapOptional = "orelse" Expression
23162314
UnwrapError = "catch" option("|" Symbol "|") Expression
23172315
*/
23182316
static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
@@ -2322,7 +2320,7 @@ static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, boo
23222320

23232321
Token *token = &pc->tokens->at(*token_index);
23242322

2325-
if (token->id == TokenIdDoubleQuestion) {
2323+
if (token->id == TokenIdKeywordOrElse) {
23262324
*token_index += 1;
23272325

23282326
AstNode *rhs = ast_parse_expression(pc, token_index, true);
@@ -3035,6 +3033,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
30353033
case NodeTypePtrDeref:
30363034
visit_field(&node->data.ptr_deref_expr.target, visit, context);
30373035
break;
3036+
case NodeTypeUnwrapOptional:
3037+
visit_field(&node->data.unwrap_optional.expr, visit, context);
3038+
break;
30383039
case NodeTypeUse:
30393040
visit_field(&node->data.use.expr, visit, context);
30403041
break;

0 commit comments

Comments
 (0)