Commit 77678b2cbc

Andrew Kelley <superjoe30@gmail.com>
2018-06-10 07:13:51
breaking syntax change: orelse keyword instead of ?? (#1096)
use the `zig-fmt-optional-default` branch to have zig fmt automatically do the changes. closes #1023
1 parent ec1b6f6
doc/docgen.zig
@@ -25,13 +25,13 @@ pub fn main() !void {
 
     if (!args_it.skip()) @panic("expected self arg");
 
-    const zig_exe = try (args_it.next(allocator) ?? @panic("expected zig exe arg"));
+    const zig_exe = try (args_it.next(allocator) orelse @panic("expected zig exe arg"));
     defer allocator.free(zig_exe);
 
-    const in_file_name = try (args_it.next(allocator) ?? @panic("expected input arg"));
+    const in_file_name = try (args_it.next(allocator) orelse @panic("expected input arg"));
     defer allocator.free(in_file_name);
 
-    const out_file_name = try (args_it.next(allocator) ?? @panic("expected output arg"));
+    const out_file_name = try (args_it.next(allocator) orelse @panic("expected output arg"));
     defer allocator.free(out_file_name);
 
     var in_file = try os.File.openRead(allocator, in_file_name);
doc/langref.html.in
@@ -985,7 +985,7 @@ a ^= b</code></pre></td>
           </td>
         </tr>
         <tr>
-          <td><pre><code class="zig">a ?? b</code></pre></td>
+          <td><pre><code class="zig">a orelse b</code></pre></td>
           <td>
             <ul>
               <li>{#link|Optionals#}</li>
@@ -998,7 +998,7 @@ a ^= b</code></pre></td>
           </td>
           <td>
             <pre><code class="zig">const value: ?u32 = null;
-const unwrapped = value ?? 1234;
+const unwrapped = value orelse 1234;
 unwrapped == 1234</code></pre>
           </td>
         </tr>
@@ -1011,7 +1011,7 @@ unwrapped == 1234</code></pre>
           </td>
           <td>
             Equivalent to:
-            <pre><code class="zig">a ?? unreachable</code></pre>
+            <pre><code class="zig">a orelse unreachable</code></pre>
           </td>
           <td>
             <pre><code class="zig">const value: ?u32 = 5678;
@@ -1278,7 +1278,7 @@ x{} x.* x.?
 == != &lt; &gt; &lt;= &gt;=
 and
 or
-?? catch
+orelse catch
 = *= /= %= += -= &lt;&lt;= &gt;&gt;= &amp;= ^= |=</code></pre>
       {#header_close#}
       {#header_close#}
@@ -3062,7 +3062,7 @@ fn createFoo(param: i32) !Foo {
     // but we want to return it if the function succeeds.
     errdefer deallocateFoo(foo);
 
-    const tmp_buf = allocateTmpBuffer() ?? return error.OutOfMemory;
+    const tmp_buf = allocateTmpBuffer() orelse return error.OutOfMemory;
     // tmp_buf is truly a temporary resource, and we for sure want to clean it up
     // before this block leaves scope
     defer deallocateTmpBuffer(tmp_buf);
@@ -3219,13 +3219,13 @@ struct Foo *do_a_thing(void) {
 extern fn malloc(size: size_t) ?*u8;
 
 fn doAThing() ?*Foo {
-    const ptr = malloc(1234) ?? return null;
+    const ptr = malloc(1234) orelse return null;
     // ...
 }
       {#code_end#}
       <p>
         Here, Zig is at least as convenient, if not more, than C. And, the type of "ptr"
-        is <code>*u8</code> <em>not</em> <code>?*u8</code>. The <code>??</code> operator
+        is <code>*u8</code> <em>not</em> <code>?*u8</code>. The <code>orelse</code> keyword
         unwrapped the optional type and therefore <code>ptr</code> is guaranteed to be non-null everywhere
         it is used in the function.
       </p>
@@ -5941,7 +5941,7 @@ AsmClobbers= ":" list(String, ",")
 
 UnwrapExpression = BoolOrExpression (UnwrapOptional | UnwrapError) | BoolOrExpression
 
-UnwrapOptional = "??" Expression
+UnwrapOptional = "orelse" Expression
 
 UnwrapError = "catch" option("|" Symbol "|") Expression
 
src/all_types.hpp
@@ -387,6 +387,7 @@ enum NodeType {
     NodeTypeSliceExpr,
     NodeTypeFieldAccessExpr,
     NodeTypePtrDeref,
+    NodeTypeUnwrapOptional,
     NodeTypeUse,
     NodeTypeBoolLiteral,
     NodeTypeNullLiteral,
@@ -575,6 +576,10 @@ struct AstNodeCatchExpr {
     AstNode *op2;
 };
 
+struct AstNodeUnwrapOptional {
+    AstNode *expr;
+};
+
 enum CastOp {
     CastOpNoCast, // signifies the function call expression is not a cast
     CastOpNoop, // fn call expr is a cast, but does nothing
@@ -624,7 +629,6 @@ enum PrefixOp {
     PrefixOpNegation,
     PrefixOpNegationWrap,
     PrefixOpOptional,
-    PrefixOpUnwrapOptional,
     PrefixOpAddrOf,
 };
 
@@ -909,6 +913,7 @@ struct AstNode {
         AstNodeTestDecl test_decl;
         AstNodeBinOpExpr bin_op_expr;
         AstNodeCatchExpr unwrap_err_expr;
+        AstNodeUnwrapOptional unwrap_optional;
         AstNodePrefixOpExpr prefix_op_expr;
         AstNodePointerType pointer_type;
         AstNodeFnCallExpr fn_call_expr;
src/analyze.cpp
@@ -3308,6 +3308,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
         case NodeTypeAsmExpr:
         case NodeTypeFieldAccessExpr:
         case NodeTypePtrDeref:
+        case NodeTypeUnwrapOptional:
         case NodeTypeStructField:
         case NodeTypeContainerInitExpr:
         case NodeTypeStructValueField:
src/ast_render.cpp
@@ -50,7 +50,7 @@ static const char *bin_op_str(BinOpType bin_op) {
         case BinOpTypeAssignBitXor:           return "^=";
         case BinOpTypeAssignBitOr:            return "|=";
         case BinOpTypeAssignMergeErrorSets:   return "||=";
-        case BinOpTypeUnwrapOptional:         return "??";
+        case BinOpTypeUnwrapOptional:         return "orelse";
         case BinOpTypeArrayCat:               return "++";
         case BinOpTypeArrayMult:              return "**";
         case BinOpTypeErrorUnion:             return "!";
@@ -67,7 +67,6 @@ static const char *prefix_op_str(PrefixOp prefix_op) {
         case PrefixOpBoolNot: return "!";
         case PrefixOpBinNot: return "~";
         case PrefixOpOptional: return "?";
-        case PrefixOpUnwrapOptional: return "??";
         case PrefixOpAddrOf: return "&";
     }
     zig_unreachable();
@@ -222,6 +221,8 @@ static const char *node_type_str(NodeType node_type) {
             return "FieldAccessExpr";
         case NodeTypePtrDeref:
             return "PtrDerefExpr";
+        case NodeTypeUnwrapOptional:
+            return "UnwrapOptional";
         case NodeTypeContainerDecl:
             return "ContainerDecl";
         case NodeTypeStructField:
@@ -711,6 +712,13 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
                 fprintf(ar->f, ".*");
                 break;
             }
+        case NodeTypeUnwrapOptional:
+            {
+                AstNode *lhs = node->data.unwrap_optional.expr;
+                render_node_ungrouped(ar, lhs);
+                fprintf(ar->f, ".?");
+                break;
+            }
         case NodeTypeUndefinedLiteral:
             fprintf(ar->f, "undefined");
             break;
src/ir.cpp
@@ -4661,21 +4661,6 @@ static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode
     return ir_build_load_ptr(irb, scope, source_node, payload_ptr);
 }
 
-static IrInstruction *ir_gen_maybe_assert_ok(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
-    assert(node->type == NodeTypePrefixOpExpr);
-    AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
-
-    IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR);
-    if (maybe_ptr == irb->codegen->invalid_instruction)
-        return irb->codegen->invalid_instruction;
-
-    IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true);
-    if (lval.is_ptr)
-        return unwrapped_ptr;
-
-    return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
-}
-
 static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *node) {
     assert(node->type == NodeTypePrefixOpExpr);
     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
             return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval);
         case PrefixOpOptional:
             return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval);
-        case PrefixOpUnwrapOptional:
-            return ir_gen_maybe_assert_ok(irb, scope, node, lval);
         case PrefixOpAddrOf: {
             AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
             return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR), lval);
@@ -6541,7 +6524,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
                 return ir_build_load_ptr(irb, scope, node, ptr_instruction);
             }
         case NodeTypePtrDeref: {
-            assert(node->type == NodeTypePtrDeref);
             AstNode *expr_node = node->data.ptr_deref_expr.target;
             IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval);
             if (value == irb->codegen->invalid_instruction)
@@ -6549,6 +6531,19 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
 
             return ir_build_un_op(irb, scope, node, IrUnOpDereference, value);
         }
+        case NodeTypeUnwrapOptional: {
+            AstNode *expr_node = node->data.unwrap_optional.expr;
+
+            IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR);
+            if (maybe_ptr == irb->codegen->invalid_instruction)
+                return irb->codegen->invalid_instruction;
+
+            IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true);
+            if (lval.is_ptr)
+                return unwrapped_ptr;
+
+            return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
+        }
         case NodeTypeThisLiteral:
             return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval);
         case NodeTypeBoolLiteral:
src/parser.cpp
@@ -1151,9 +1151,8 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index,
             } else if (token->id == TokenIdQuestion) {
                 *token_index += 1;
 
-                AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, first_token);
-                node->data.prefix_op_expr.prefix_op = PrefixOpUnwrapOptional;
-                node->data.prefix_op_expr.primary_expr = primary_expr;
+                AstNode *node = ast_create_node(pc, NodeTypeUnwrapOptional, first_token);
+                node->data.unwrap_optional.expr = primary_expr;
 
                 primary_expr = node;
             } else {
@@ -1173,7 +1172,6 @@ static PrefixOp tok_to_prefix_op(Token *token) {
         case TokenIdMinusPercent: return PrefixOpNegationWrap;
         case TokenIdTilde: return PrefixOpBinNot;
         case TokenIdQuestion: return PrefixOpOptional;
-        case TokenIdDoubleQuestion: return PrefixOpUnwrapOptional;
         case TokenIdAmpersand: return PrefixOpAddrOf;
         default: return PrefixOpInvalid;
     }
@@ -2312,7 +2310,7 @@ static BinOpType ast_parse_ass_op(ParseContext *pc, size_t *token_index, bool ma
 
 /*
 UnwrapExpression : BoolOrExpression (UnwrapOptional | UnwrapError) | BoolOrExpression
-UnwrapOptional : "??" BoolOrExpression
+UnwrapOptional = "orelse" Expression
 UnwrapError = "catch" option("|" Symbol "|") Expression
 */
 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
 
     Token *token = &pc->tokens->at(*token_index);
 
-    if (token->id == TokenIdDoubleQuestion) {
+    if (token->id == TokenIdKeywordOrElse) {
         *token_index += 1;
 
         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
         case NodeTypePtrDeref:
             visit_field(&node->data.ptr_deref_expr.target, visit, context);
             break;
+        case NodeTypeUnwrapOptional:
+            visit_field(&node->data.unwrap_optional.expr, visit, context);
+            break;
         case NodeTypeUse:
             visit_field(&node->data.use.expr, visit, context);
             break;
src/tokenizer.cpp
@@ -134,6 +134,7 @@ static const struct ZigKeyword zig_keywords[] = {
     {"noalias", TokenIdKeywordNoAlias},
     {"null", TokenIdKeywordNull},
     {"or", TokenIdKeywordOr},
+    {"orelse", TokenIdKeywordOrElse},
     {"packed", TokenIdKeywordPacked},
     {"promise", TokenIdKeywordPromise},
     {"pub", TokenIdKeywordPub},
@@ -215,7 +216,6 @@ enum TokenizeState {
     TokenizeStateSawGreaterThanGreaterThan,
     TokenizeStateSawDot,
     TokenizeStateSawDotDot,
-    TokenizeStateSawQuestionMark,
     TokenizeStateSawAtSign,
     TokenizeStateCharCode,
     TokenizeStateError,
@@ -532,6 +532,10 @@ void tokenize(Buf *buf, Tokenization *out) {
                         begin_token(&t, TokenIdComma);
                         end_token(&t);
                         break;
+                    case '?':
+                        begin_token(&t, TokenIdQuestion);
+                        end_token(&t);
+                        break;
                     case '{':
                         begin_token(&t, TokenIdLBrace);
                         end_token(&t);
@@ -624,28 +628,10 @@ void tokenize(Buf *buf, Tokenization *out) {
                         begin_token(&t, TokenIdDot);
                         t.state = TokenizeStateSawDot;
                         break;
-                    case '?':
-                        begin_token(&t, TokenIdQuestion);
-                        t.state = TokenizeStateSawQuestionMark;
-                        break;
                     default:
                         invalid_char_error(&t, c);
                 }
                 break;
-            case TokenizeStateSawQuestionMark:
-                switch (c) {
-                    case '?':
-                        set_token_id(&t, t.cur_tok, TokenIdDoubleQuestion);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
-                        break;
-                    default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
-                        continue;
-                }
-                break;
             case TokenizeStateSawDot:
                 switch (c) {
                     case '.':
@@ -1480,7 +1466,6 @@ void tokenize(Buf *buf, Tokenization *out) {
         case TokenizeStateSawGreaterThan:
         case TokenizeStateSawGreaterThanGreaterThan:
         case TokenizeStateSawDot:
-        case TokenizeStateSawQuestionMark:
         case TokenizeStateSawAtSign:
         case TokenizeStateSawStarPercent:
         case TokenizeStateSawPlusPercent:
@@ -1545,7 +1530,6 @@ const char * token_name(TokenId id) {
         case TokenIdDash: return "-";
         case TokenIdDivEq: return "/=";
         case TokenIdDot: return ".";
-        case TokenIdDoubleQuestion: return "??";
         case TokenIdEllipsis2: return "..";
         case TokenIdEllipsis3: return "...";
         case TokenIdEof: return "EOF";
@@ -1582,6 +1566,7 @@ const char * token_name(TokenId id) {
         case TokenIdKeywordNoAlias: return "noalias";
         case TokenIdKeywordNull: return "null";
         case TokenIdKeywordOr: return "or";
+        case TokenIdKeywordOrElse: return "orelse";
         case TokenIdKeywordPacked: return "packed";
         case TokenIdKeywordPromise: return "promise";
         case TokenIdKeywordPub: return "pub";
src/tokenizer.hpp
@@ -41,7 +41,6 @@ enum TokenId {
     TokenIdDash,
     TokenIdDivEq,
     TokenIdDot,
-    TokenIdDoubleQuestion,
     TokenIdEllipsis2,
     TokenIdEllipsis3,
     TokenIdEof,
@@ -76,6 +75,7 @@ enum TokenId {
     TokenIdKeywordNoAlias,
     TokenIdKeywordNull,
     TokenIdKeywordOr,
+    TokenIdKeywordOrElse,
     TokenIdKeywordPacked,
     TokenIdKeywordPromise,
     TokenIdKeywordPub,
src/translate_c.cpp
@@ -260,6 +260,12 @@ static AstNode *trans_create_node_prefix_op(Context *c, PrefixOp op, AstNode *ch
     return node;
 }
 
+static AstNode *trans_create_node_unwrap_null(Context *c, AstNode *child_node) {
+    AstNode *node = trans_create_node(c, NodeTypeUnwrapOptional);
+    node->data.unwrap_optional.expr = child_node;
+    return node;
+}
+
 static AstNode *trans_create_node_bin_op(Context *c, AstNode *lhs_node, BinOpType op, AstNode *rhs_node) {
     AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
     node->data.bin_op_expr.op1 = lhs_node;
@@ -382,7 +388,7 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r
     fn_def->data.fn_def.fn_proto = fn_proto;
     fn_proto->data.fn_proto.fn_def_node = fn_def;
 
-    AstNode *unwrap_node = trans_create_node_prefix_op(c, PrefixOpUnwrapOptional, ref_node);
+    AstNode *unwrap_node = trans_create_node_unwrap_null(c, ref_node);
     AstNode *fn_call_node = trans_create_node(c, NodeTypeFnCallExpr);
     fn_call_node->data.fn_call_expr.fn_ref_expr = unwrap_node;
 
@@ -409,10 +415,6 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r
     return fn_def;
 }
 
-static AstNode *trans_create_node_unwrap_null(Context *c, AstNode *child) {
-    return trans_create_node_prefix_op(c, PrefixOpUnwrapOptional, child);
-}
-
 static AstNode *get_global(Context *c, Buf *name) {
     {
         auto entry = c->global_table.maybe_get(name);
@@ -1963,7 +1965,7 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc
                 bool is_fn_ptr = qual_type_is_fn_ptr(stmt->getSubExpr()->getType());
                 if (is_fn_ptr)
                     return value_node;
-                AstNode *unwrapped = trans_create_node_prefix_op(c, PrefixOpUnwrapOptional, value_node);
+                AstNode *unwrapped = trans_create_node_unwrap_null(c, value_node);
                 return trans_create_node_ptr_deref(c, unwrapped);
             }
         case UO_Plus:
@@ -2587,7 +2589,7 @@ static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope *
             }
         }
         if (callee_node == nullptr) {
-            callee_node = trans_create_node_prefix_op(c, PrefixOpUnwrapOptional, callee_raw_node);
+            callee_node = trans_create_node_unwrap_null(c, callee_raw_node);
         }
     } else {
         callee_node = callee_raw_node;
src-self-hosted/main.zig
@@ -212,7 +212,7 @@ fn cmdBuild(allocator: *Allocator, args: []const []const u8) !void {
     const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig");
     defer allocator.free(build_runner_path);
 
-    const build_file = flags.single("build-file") ?? "build.zig";
+    const build_file = flags.single("build-file") orelse "build.zig";
     const build_file_abs = try os.path.resolve(allocator, ".", build_file);
     defer allocator.free(build_file_abs);
 
@@ -516,7 +516,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
 
     const basename = os.path.basename(in_file.?);
     var it = mem.split(basename, ".");
-    const root_name = it.next() ?? {
+    const root_name = it.next() orelse {
         try stderr.write("file name cannot be empty\n");
         os.exit(1);
     };
@@ -535,7 +535,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
 
     const zig_root_source_file = in_file;
 
-    const full_cache_dir = os.path.resolve(allocator, ".", flags.single("cache-dir") ?? "zig-cache"[0..]) catch {
+    const full_cache_dir = os.path.resolve(allocator, ".", flags.single("cache-dir") orelse "zig-cache"[0..]) catch {
         os.exit(1);
     };
     defer allocator.free(full_cache_dir);
@@ -555,9 +555,9 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
     );
     defer module.destroy();
 
-    module.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") ?? "0", 10);
-    module.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") ?? "0", 10);
-    module.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") ?? "0", 10);
+    module.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10);
+    module.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10);
+    module.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10);
 
     module.is_test = false;
 
@@ -652,7 +652,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
     }
 
     try module.build();
-    try module.link(flags.single("out-file") ?? null);
+    try module.link(flags.single("out-file") orelse null);
 
     if (flags.present("print-timing-info")) {
         // codegen_print_timing_info(g, stderr);
src-self-hosted/module.zig
@@ -130,13 +130,13 @@ pub const Module = struct {
         var name_buffer = try Buffer.init(allocator, name);
         errdefer name_buffer.deinit();
 
-        const context = c.LLVMContextCreate() ?? return error.OutOfMemory;
+        const context = c.LLVMContextCreate() orelse return error.OutOfMemory;
         errdefer c.LLVMContextDispose(context);
 
-        const module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) ?? return error.OutOfMemory;
+        const module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) orelse return error.OutOfMemory;
         errdefer c.LLVMDisposeModule(module);
 
-        const builder = c.LLVMCreateBuilderInContext(context) ?? return error.OutOfMemory;
+        const builder = c.LLVMCreateBuilderInContext(context) orelse return error.OutOfMemory;
         errdefer c.LLVMDisposeBuilder(builder);
 
         const module_ptr = try allocator.create(Module);
@@ -223,7 +223,7 @@ pub const Module = struct {
             c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr);
         }
 
-        const root_src_path = self.root_src_path ?? @panic("TODO handle null root src path");
+        const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path");
         const root_src_real_path = os.path.real(self.allocator, root_src_path) catch |err| {
             try printError("unable to get real path '{}': {}", root_src_path, err);
             return err;
std/atomic/queue.zig
@@ -33,8 +33,8 @@ pub fn Queue(comptime T: type) type {
         pub fn get(self: *Self) ?*Node {
             var head = @atomicLoad(*Node, &self.head, AtomicOrder.SeqCst);
             while (true) {
-                const node = head.next ?? return null;
-                head = @cmpxchgWeak(*Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return node;
+                const node = head.next orelse return null;
+                head = @cmpxchgWeak(*Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return node;
             }
         }
     };
std/atomic/stack.zig
@@ -28,14 +28,14 @@ pub fn Stack(comptime T: type) type {
             var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst);
             while (true) {
                 node.next = root;
-                root = @cmpxchgWeak(?*Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? break;
+                root = @cmpxchgWeak(?*Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse break;
             }
         }
 
         pub fn pop(self: *Self) ?*Node {
             var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst);
             while (true) {
-                root = @cmpxchgWeak(?*Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root;
+                root = @cmpxchgWeak(?*Node, &self.root, root, (root orelse return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return root;
             }
         }
 
std/debug/index.zig
@@ -208,7 +208,7 @@ fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: us
                 .name = "???",
                 .address = address,
             };
-            const symbol = debug_info.symbol_table.search(address) ?? &unknown;
+            const symbol = debug_info.symbol_table.search(address) orelse &unknown;
             try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n", symbol.name, address);
         },
         else => {
@@ -268,10 +268,10 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace {
             try st.elf.openFile(allocator, &st.self_exe_file);
             errdefer st.elf.close();
 
-            st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo;
-            st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo;
-            st.debug_str = (try st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo;
-            st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo;
+            st.debug_info = (try st.elf.findSection(".debug_info")) orelse return error.MissingDebugInfo;
+            st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) orelse return error.MissingDebugInfo;
+            st.debug_str = (try st.elf.findSection(".debug_str")) orelse return error.MissingDebugInfo;
+            st.debug_line = (try st.elf.findSection(".debug_line")) orelse return error.MissingDebugInfo;
             st.debug_ranges = (try st.elf.findSection(".debug_ranges"));
             try scanAllCompileUnits(st);
             return st;
@@ -443,7 +443,7 @@ const Die = struct {
     }
 
     fn getAttrAddr(self: *const Die, id: u64) !u64 {
-        const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
+        const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
         return switch (form_value.*) {
             FormValue.Address => |value| value,
             else => error.InvalidDebugInfo,
@@ -451,7 +451,7 @@ const Die = struct {
     }
 
     fn getAttrSecOffset(self: *const Die, id: u64) !u64 {
-        const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
+        const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
         return switch (form_value.*) {
             FormValue.Const => |value| value.asUnsignedLe(),
             FormValue.SecOffset => |value| value,
@@ -460,7 +460,7 @@ const Die = struct {
     }
 
     fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 {
-        const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
+        const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
         return switch (form_value.*) {
             FormValue.Const => |value| value.asUnsignedLe(),
             else => error.InvalidDebugInfo,
@@ -468,7 +468,7 @@ const Die = struct {
     }
 
     fn getAttrString(self: *const Die, st: *ElfStackTrace, id: u64) ![]u8 {
-        const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
+        const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
         return switch (form_value.*) {
             FormValue.String => |value| value,
             FormValue.StrPtr => |offset| getString(st, offset),
@@ -748,7 +748,7 @@ fn parseDie(st: *ElfStackTrace, abbrev_table: *const AbbrevTable, is_64: bool) !
     var in_file_stream = io.FileInStream.init(in_file);
     const in_stream = &in_file_stream.stream;
     const abbrev_code = try readULeb128(in_stream);
-    const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) ?? return error.InvalidDebugInfo;
+    const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
 
     var result = Die{
         .tag_id = table_entry.tag_id,
std/os/linux/vdso.zig
@@ -28,7 +28,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize {
             }
         }
     }
-    const dynv = maybe_dynv ?? return 0;
+    const dynv = maybe_dynv orelse return 0;
     if (base == @maxValue(usize)) return 0;
 
     var maybe_strings: ?[*]u8 = null;
@@ -52,9 +52,9 @@ pub fn lookup(vername: []const u8, name: []const u8) usize {
         }
     }
 
-    const strings = maybe_strings ?? return 0;
-    const syms = maybe_syms ?? return 0;
-    const hashtab = maybe_hashtab ?? return 0;
+    const strings = maybe_strings orelse return 0;
+    const syms = maybe_syms orelse return 0;
+    const hashtab = maybe_hashtab orelse return 0;
     if (maybe_verdef == null) maybe_versym = null;
 
     const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON);
std/os/windows/util.zig
@@ -153,7 +153,7 @@ pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap)
 pub fn windowsLoadDll(allocator: *mem.Allocator, dll_path: []const u8) !windows.HMODULE {
     const padded_buff = try cstr.addNullByte(allocator, dll_path);
     defer allocator.free(padded_buff);
-    return windows.LoadLibraryA(padded_buff.ptr) ?? error.DllNotFound;
+    return windows.LoadLibraryA(padded_buff.ptr) orelse error.DllNotFound;
 }
 
 pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
std/os/index.zig
@@ -425,7 +425,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator:
         return posixExecveErrnoToErr(posix.getErrno(posix.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr)));
     }
 
-    const PATH = getEnvPosix("PATH") ?? "/usr/local/bin:/bin/:/usr/bin";
+    const PATH = getEnvPosix("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";
     // PATH.len because it is >= the largest search_path
     // +1 for the / to join the search path and exe_path
     // +1 for the null terminating byte
@@ -490,7 +490,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
     errdefer result.deinit();
 
     if (is_windows) {
-        const ptr = windows.GetEnvironmentStringsA() ?? return error.OutOfMemory;
+        const ptr = windows.GetEnvironmentStringsA() orelse return error.OutOfMemory;
         defer assert(windows.FreeEnvironmentStringsA(ptr) != 0);
 
         var i: usize = 0;
@@ -573,7 +573,7 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) ![]u8 {
             return allocator.shrink(u8, buf, result);
         }
     } else {
-        const result = getEnvPosix(key) ?? return error.EnvironmentVariableNotFound;
+        const result = getEnvPosix(key) orelse return error.EnvironmentVariableNotFound;
         return mem.dupe(allocator, u8, result);
     }
 }
@@ -1641,7 +1641,7 @@ pub const ArgIterator = struct {
         if (builtin.os == Os.windows) {
             return self.inner.next(allocator);
         } else {
-            return mem.dupe(allocator, u8, self.inner.next() ?? return null);
+            return mem.dupe(allocator, u8, self.inner.next() orelse return null);
         }
     }
 
@@ -2457,9 +2457,9 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
             }
         };
 
-        const heap_handle = windows.GetProcessHeap() ?? return SpawnThreadError.OutOfMemory;
+        const heap_handle = windows.GetProcessHeap() orelse return SpawnThreadError.OutOfMemory;
         const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext);
-        const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) ?? return SpawnThreadError.OutOfMemory;
+        const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) orelse return SpawnThreadError.OutOfMemory;
         errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0);
         const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count];
         const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable;
@@ -2468,7 +2468,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
         outer_context.thread.data.alloc_start = bytes_ptr;
 
         const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner);
-        outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) ?? {
+        outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse {
             const err = windows.GetLastError();
             return switch (err) {
                 else => os.unexpectedErrorWindows(err),
std/os/path.zig
@@ -182,8 +182,8 @@ pub fn windowsParsePath(path: []const u8) WindowsPath {
             }
 
             var it = mem.split(path, []u8{this_sep});
-            _ = (it.next() ?? return relative_path);
-            _ = (it.next() ?? return relative_path);
+            _ = (it.next() orelse return relative_path);
+            _ = (it.next() orelse return relative_path);
             return WindowsPath{
                 .is_abs = isAbsoluteWindows(path),
                 .kind = WindowsPath.Kind.NetworkShare,
@@ -200,8 +200,8 @@ pub fn windowsParsePath(path: []const u8) WindowsPath {
             }
 
             var it = mem.split(path, []u8{this_sep});
-            _ = (it.next() ?? return relative_path);
-            _ = (it.next() ?? return relative_path);
+            _ = (it.next() orelse return relative_path);
+            _ = (it.next() orelse return relative_path);
             return WindowsPath{
                 .is_abs = isAbsoluteWindows(path),
                 .kind = WindowsPath.Kind.NetworkShare,
@@ -923,7 +923,7 @@ pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8)
     var from_it = mem.split(resolved_from, "/\\");
     var to_it = mem.split(resolved_to, "/\\");
     while (true) {
-        const from_component = from_it.next() ?? return mem.dupe(allocator, u8, to_it.rest());
+        const from_component = from_it.next() orelse return mem.dupe(allocator, u8, to_it.rest());
         const to_rest = to_it.rest();
         if (to_it.next()) |to_component| {
             // TODO ASCII is wrong, we actually need full unicode support to compare paths.
@@ -974,7 +974,7 @@ pub fn relativePosix(allocator: *Allocator, from: []const u8, to: []const u8) ![
     var from_it = mem.split(resolved_from, "/");
     var to_it = mem.split(resolved_to, "/");
     while (true) {
-        const from_component = from_it.next() ?? return mem.dupe(allocator, u8, to_it.rest());
+        const from_component = from_it.next() orelse return mem.dupe(allocator, u8, to_it.rest());
         const to_rest = to_it.rest();
         if (to_it.next()) |to_component| {
             if (mem.eql(u8, from_component, to_component))
std/special/build_runner.zig
@@ -27,15 +27,15 @@ pub fn main() !void {
     // skip my own exe name
     _ = arg_it.skip();
 
-    const zig_exe = try unwrapArg(arg_it.next(allocator) ?? {
+    const zig_exe = try unwrapArg(arg_it.next(allocator) orelse {
         warn("Expected first argument to be path to zig compiler\n");
         return error.InvalidArgs;
     });
-    const build_root = try unwrapArg(arg_it.next(allocator) ?? {
+    const build_root = try unwrapArg(arg_it.next(allocator) orelse {
         warn("Expected second argument to be build root directory path\n");
         return error.InvalidArgs;
     });
-    const cache_root = try unwrapArg(arg_it.next(allocator) ?? {
+    const cache_root = try unwrapArg(arg_it.next(allocator) orelse {
         warn("Expected third argument to be cache root directory path\n");
         return error.InvalidArgs;
     });
@@ -84,12 +84,12 @@ pub fn main() !void {
             } else if (mem.eql(u8, arg, "--help")) {
                 return usage(&builder, false, try stdout_stream);
             } else if (mem.eql(u8, arg, "--prefix")) {
-                prefix = try unwrapArg(arg_it.next(allocator) ?? {
+                prefix = try unwrapArg(arg_it.next(allocator) orelse {
                     warn("Expected argument after --prefix\n\n");
                     return usageAndErr(&builder, false, try stderr_stream);
                 });
             } else if (mem.eql(u8, arg, "--search-prefix")) {
-                const search_prefix = try unwrapArg(arg_it.next(allocator) ?? {
+                const search_prefix = try unwrapArg(arg_it.next(allocator) orelse {
                     warn("Expected argument after --search-prefix\n\n");
                     return usageAndErr(&builder, false, try stderr_stream);
                 });
std/zig/parse.zig
@@ -43,7 +43,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
 
     // skip over line comments at the top of the file
     while (true) {
-        const next_tok = tok_it.peek() ?? break;
+        const next_tok = tok_it.peek() orelse break;
         if (next_tok.id != Token.Id.LineComment) break;
         _ = tok_it.next();
     }
@@ -197,7 +197,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
                     const lib_name_token = nextToken(&tok_it, &tree);
                     const lib_name_token_index = lib_name_token.index;
                     const lib_name_token_ptr = lib_name_token.ptr;
-                    break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index, &tree)) ?? {
+                    break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index, &tree)) orelse {
                         prevToken(&tok_it, &tree);
                         break :blk null;
                     };
@@ -1434,13 +1434,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
                 try stack.append(State{
                     .ExpectTokenSave = ExpectTokenSave{
                         .id = Token.Id.AngleBracketRight,
-                        .ptr = &async_node.rangle_bracket.?                    },
+                        .ptr = &async_node.rangle_bracket.?,
+                    },
                 });
                 try stack.append(State{ .TypeExprBegin = OptionalCtx{ .RequiredNull = &async_node.allocator_type } });
                 continue;
             },
             State.AsyncEnd => |ctx| {
-                const node = ctx.ctx.get() ?? continue;
+                const node = ctx.ctx.get() orelse continue;
 
                 switch (node.id) {
                     ast.Node.Id.FnProto => {
@@ -1813,7 +1814,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
                 continue;
             },
             State.RangeExpressionEnd => |opt_ctx| {
-                const lhs = opt_ctx.get() ?? continue;
+                const lhs = opt_ctx.get() orelse continue;
 
                 if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| {
                     const node = try arena.construct(ast.Node.InfixOp{
@@ -1835,7 +1836,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
             },
 
             State.AssignmentExpressionEnd => |opt_ctx| {
-                const lhs = opt_ctx.get() ?? continue;
+                const lhs = opt_ctx.get() orelse continue;
 
                 const token = nextToken(&tok_it, &tree);
                 const token_index = token.index;
@@ -1865,7 +1866,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
             },
 
             State.UnwrapExpressionEnd => |opt_ctx| {
-                const lhs = opt_ctx.get() ?? continue;
+                const lhs = opt_ctx.get() orelse continue;
 
                 const token = nextToken(&tok_it, &tree);
                 const token_index = token.index;
@@ -1900,7 +1901,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
             },
 
             State.BoolOrExpressionEnd => |opt_ctx| {
-                const lhs = opt_ctx.get() ?? continue;
+                const lhs = opt_ctx.get() orelse continue;
 
                 if (eatToken(&tok_it, &tree, Token.Id.Keyword_or)) |or_token| {
                     const node = try arena.construct(ast.Node.InfixOp{
@@ -1924,7 +1925,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
             },
 
             State.BoolAndExpressionEnd => |opt_ctx| {
-                const lhs = opt_ctx.get() ?? continue;
+                const lhs = opt_ctx.get() orelse continue;
 
                 if (eatToken(&tok_it, &tree, Token.Id.Keyword_and)) |and_token| {
                     const node = try arena.construct(ast.Node.InfixOp{
@@ -1948,7 +1949,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
             },
 
             State.ComparisonExpressionEnd => |opt_ctx| {
-                const lhs = opt_ctx.get() ?? continue;
+                const lhs = opt_ctx.get() orelse continue;
 
                 const token = nextToken(&tok_it, &tree);
                 const token_index = token.index;
@@ -1978,7 +1979,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
             },
 
             State.BinaryOrExpressionEnd => |opt_ctx| {
-                const lhs = opt_ctx.get() ?? continue;
+                const lhs = opt_ctx.get() orelse continue;
 
                 if (eatToken(&tok_it, &tree, Token.Id.Pipe)) |pipe| {
                     const node = try arena.construct(ast.Node.InfixOp{
@@ -2002,7 +2003,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
             },
 
             State.BinaryXorExpressionEnd => |opt_ctx| {
-                const lhs = opt_ctx.get() ?? continue;
+                const lhs = opt_ctx.get() orelse continue;
 
                 if (eatToken(&tok_it, &tree, Token.Id.Caret)) |caret| {
                     const node = try arena.construct(ast.Node.InfixOp{
@@ -2026,7 +2027,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
             },
 
             State.BinaryAndExpressionEnd => |opt_ctx| {
-                const lhs = opt_ctx.get() ?? continue;
+                const lhs = opt_ctx.get() orelse continue;
 
                 if (eatToken(&tok_it, &tree, Token.Id.Ampersand)) |ampersand| {
                     const node = try arena.construct(ast.Node.InfixOp{
@@ -2050,7 +2051,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
             },
 
             State.BitShiftExpressionEnd => |opt_ctx| {
-                const lhs = opt_ctx.get() ?? continue;
+                const lhs = opt_ctx.get() orelse continue;
 
                 const token = nextToken(&tok_it, &tree);
                 const token_index = token.index;
@@ -2080,7 +2081,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
             },
 
             State.AdditionExpressionEnd => |opt_ctx| {
-                const lhs = opt_ctx.get() ?? continue;
+                const lhs = opt_ctx.get() orelse continue;
 
                 const token = nextToken(&tok_it, &tree);
                 const token_index = token.index;
@@ -2110,7 +2111,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
             },
 
             State.MultiplyExpressionEnd => |opt_ctx| {
-                const lhs = opt_ctx.get() ?? continue;
+                const lhs = opt_ctx.get() orelse continue;
 
                 const token = nextToken(&tok_it, &tree);
                 const token_index = token.index;
@@ -2141,7 +2142,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
             },
 
             State.CurlySuffixExpressionEnd => |opt_ctx| {
-                const lhs = opt_ctx.get() ?? continue;
+                const lhs = opt_ctx.get() orelse continue;
 
                 if (tok_it.peek().?.id == Token.Id.Period) {
                     const node = try arena.construct(ast.Node.SuffixOp{
@@ -2189,7 +2190,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
             },
 
             State.TypeExprEnd => |opt_ctx| {
-                const lhs = opt_ctx.get() ?? continue;
+                const lhs = opt_ctx.get() orelse continue;
 
                 if (eatToken(&tok_it, &tree, Token.Id.Bang)) |bang| {
                     const node = try arena.construct(ast.Node.InfixOp{
@@ -2269,7 +2270,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
             },
 
             State.SuffixOpExpressionEnd => |opt_ctx| {
-                const lhs = opt_ctx.get() ?? continue;
+                const lhs = opt_ctx.get() orelse continue;
 
                 const token = nextToken(&tok_it, &tree);
                 const token_index = token.index;
@@ -2418,7 +2419,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
                         continue;
                     },
                     Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
-                        opt_ctx.store((try parseStringLiteral(arena, &tok_it, token.ptr, token.index, &tree)) ?? unreachable);
+                        opt_ctx.store((try parseStringLiteral(arena, &tok_it, token.ptr, token.index, &tree)) orelse unreachable);
                         continue;
                     },
                     Token.Id.LParen => {
@@ -2648,7 +2649,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
                 const token = nextToken(&tok_it, &tree);
                 const token_index = token.index;
                 const token_ptr = token.ptr;
-                opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index, &tree)) ?? {
+                opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index, &tree)) orelse {
                     prevToken(&tok_it, &tree);
                     if (opt_ctx != OptionalCtx.Optional) {
                         ((try tree.errors.addOne())).* = Error{ .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr{ .token = token_index } };
@@ -3348,7 +3349,7 @@ fn nextToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) AnnotatedTok
     assert(result.ptr.id != Token.Id.LineComment);
 
     while (true) {
-        const next_tok = tok_it.peek() ?? return result;
+        const next_tok = tok_it.peek() orelse return result;
         if (next_tok.id != Token.Id.LineComment) return result;
         _ = tok_it.next();
     }
@@ -3356,7 +3357,7 @@ fn nextToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) AnnotatedTok
 
 fn prevToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) void {
     while (true) {
-        const prev_tok = tok_it.prev() ?? return;
+        const prev_tok = tok_it.prev() orelse return;
         if (prev_tok.id == Token.Id.LineComment) continue;
         return;
     }
std/zig/render.zig
@@ -83,7 +83,7 @@ fn renderRoot(
     var start_col: usize = 0;
     var it = tree.root_node.decls.iterator(0);
     while (true) {
-        var decl = (it.next() ?? return).*;
+        var decl = (it.next() orelse return).*;
         // look for zig fmt: off comment
         var start_token_index = decl.firstToken();
         zig_fmt_loop: while (start_token_index != 0) {
@@ -112,7 +112,7 @@ fn renderRoot(
                         const start = tree.tokens.at(start_token_index + 1).start;
                         try stream.print("{}\n", tree.source[start..end_token.end]);
                         while (tree.tokens.at(decl.firstToken()).start < end_token.end) {
-                            decl = (it.next() ?? return).*;
+                            decl = (it.next() orelse return).*;
                         }
                         break :zig_fmt_loop;
                     }
@@ -1993,7 +1993,7 @@ fn renderDocComments(
     indent: usize,
     start_col: *usize,
 ) (@typeOf(stream).Child.Error || Error)!void {
-    const comment = node.doc_comments ?? return;
+    const comment = node.doc_comments orelse return;
     var it = comment.lines.iterator(0);
     const first_token = node.firstToken();
     while (it.next()) |line_token_index| {
@@ -2021,7 +2021,7 @@ fn nodeIsBlock(base: *const ast.Node) bool {
 }
 
 fn nodeCausesSliceOpSpace(base: *ast.Node) bool {
-    const infix_op = base.cast(ast.Node.InfixOp) ?? return false;
+    const infix_op = base.cast(ast.Node.InfixOp) orelse return false;
     return switch (infix_op.op) {
         ast.Node.InfixOp.Op.Period => false,
         else => true,
std/buf_map.zig
@@ -19,7 +19,7 @@ pub const BufMap = struct {
     pub fn deinit(self: *const BufMap) void {
         var it = self.hash_map.iterator();
         while (true) {
-            const entry = it.next() ?? break;
+            const entry = it.next() orelse break;
             self.free(entry.key);
             self.free(entry.value);
         }
@@ -37,12 +37,12 @@ pub const BufMap = struct {
     }
 
     pub fn get(self: *const BufMap, key: []const u8) ?[]const u8 {
-        const entry = self.hash_map.get(key) ?? return null;
+        const entry = self.hash_map.get(key) orelse return null;
         return entry.value;
     }
 
     pub fn delete(self: *BufMap, key: []const u8) void {
-        const entry = self.hash_map.remove(key) ?? return;
+        const entry = self.hash_map.remove(key) orelse return;
         self.free(entry.key);
         self.free(entry.value);
     }
std/buf_set.zig
@@ -17,7 +17,7 @@ pub const BufSet = struct {
     pub fn deinit(self: *const BufSet) void {
         var it = self.hash_map.iterator();
         while (true) {
-            const entry = it.next() ?? break;
+            const entry = it.next() orelse break;
             self.free(entry.key);
         }
 
@@ -33,7 +33,7 @@ pub const BufSet = struct {
     }
 
     pub fn delete(self: *BufSet, key: []const u8) void {
-        const entry = self.hash_map.remove(key) ?? return;
+        const entry = self.hash_map.remove(key) orelse return;
         self.free(entry.key);
     }
 
std/build.zig
@@ -136,7 +136,7 @@ pub const Builder = struct {
     }
 
     pub fn setInstallPrefix(self: *Builder, maybe_prefix: ?[]const u8) void {
-        self.prefix = maybe_prefix ?? "/usr/local"; // TODO better default
+        self.prefix = maybe_prefix orelse "/usr/local"; // TODO better default
         self.lib_dir = os.path.join(self.allocator, self.prefix, "lib") catch unreachable;
         self.exe_dir = os.path.join(self.allocator, self.prefix, "bin") catch unreachable;
     }
@@ -312,9 +312,9 @@ pub const Builder = struct {
         if (os.getEnvVarOwned(self.allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| {
             var it = mem.split(nix_cflags_compile, " ");
             while (true) {
-                const word = it.next() ?? break;
+                const word = it.next() orelse break;
                 if (mem.eql(u8, word, "-isystem")) {
-                    const include_path = it.next() ?? {
+                    const include_path = it.next() orelse {
                         warn("Expected argument after -isystem in NIX_CFLAGS_COMPILE\n");
                         break;
                     };
@@ -330,9 +330,9 @@ pub const Builder = struct {
         if (os.getEnvVarOwned(self.allocator, "NIX_LDFLAGS")) |nix_ldflags| {
             var it = mem.split(nix_ldflags, " ");
             while (true) {
-                const word = it.next() ?? break;
+                const word = it.next() orelse break;
                 if (mem.eql(u8, word, "-rpath")) {
-                    const rpath = it.next() ?? {
+                    const rpath = it.next() orelse {
                         warn("Expected argument after -rpath in NIX_LDFLAGS\n");
                         break;
                     };
@@ -362,7 +362,7 @@ pub const Builder = struct {
         }
         self.available_options_list.append(available_option) catch unreachable;
 
-        const entry = self.user_input_options.get(name) ?? return null;
+        const entry = self.user_input_options.get(name) orelse return null;
         entry.value.used = true;
         switch (type_id) {
             TypeId.Bool => switch (entry.value.value) {
@@ -416,9 +416,9 @@ pub const Builder = struct {
     pub fn standardReleaseOptions(self: *Builder) builtin.Mode {
         if (self.release_mode) |mode| return mode;
 
-        const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") ?? false;
-        const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") ?? false;
-        const release_small = self.option(bool, "release-small", "size optimizations on and safety off") ?? false;
+        const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") orelse false;
+        const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") orelse false;
+        const release_small = self.option(bool, "release-small", "size optimizations on and safety off") orelse false;
 
         const mode = if (release_safe and !release_fast and !release_small) builtin.Mode.ReleaseSafe else if (release_fast and !release_safe and !release_small) builtin.Mode.ReleaseFast else if (release_small and !release_fast and !release_safe) builtin.Mode.ReleaseSmall else if (!release_fast and !release_safe and !release_small) builtin.Mode.Debug else x: {
             warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)");
@@ -518,7 +518,7 @@ pub const Builder = struct {
         // make sure all args are used
         var it = self.user_input_options.iterator();
         while (true) {
-            const entry = it.next() ?? break;
+            const entry = it.next() orelse break;
             if (!entry.value.used) {
                 warn("Invalid option: -D{}\n\n", entry.key);
                 self.markInvalidUserInput();
@@ -1246,7 +1246,7 @@ pub const LibExeObjStep = struct {
         {
             var it = self.link_libs.iterator();
             while (true) {
-                const entry = it.next() ?? break;
+                const entry = it.next() orelse break;
                 zig_args.append("--library") catch unreachable;
                 zig_args.append(entry.key) catch unreachable;
             }
@@ -1696,7 +1696,7 @@ pub const TestStep = struct {
         {
             var it = self.link_libs.iterator();
             while (true) {
-                const entry = it.next() ?? break;
+                const entry = it.next() orelse break;
                 try zig_args.append("--library");
                 try zig_args.append(entry.key);
             }
std/heap.zig
@@ -97,12 +97,12 @@ pub const DirectAllocator = struct {
             },
             Os.windows => {
                 const amt = n + alignment + @sizeOf(usize);
-                const heap_handle = self.heap_handle ?? blk: {
-                    const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) ?? return error.OutOfMemory;
+                const heap_handle = self.heap_handle orelse blk: {
+                    const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) orelse return error.OutOfMemory;
                     self.heap_handle = hh;
                     break :blk hh;
                 };
-                const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) ?? return error.OutOfMemory;
+                const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
                 const root_addr = @ptrToInt(ptr);
                 const rem = @rem(root_addr, alignment);
                 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
@@ -142,7 +142,7 @@ pub const DirectAllocator = struct {
                 const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;
                 const old_ptr = @intToPtr(*c_void, root_addr);
                 const amt = new_size + alignment + @sizeOf(usize);
-                const new_ptr = os.windows.HeapReAlloc(self.heap_handle.?, 0, old_ptr, amt) ?? blk: {
+                const new_ptr = os.windows.HeapReAlloc(self.heap_handle.?, 0, old_ptr, amt) orelse blk: {
                     if (new_size > old_mem.len) return error.OutOfMemory;
                     const new_record_addr = old_record_addr - new_size + old_mem.len;
                     @intToPtr(*align(1) usize, new_record_addr).* = root_addr;
@@ -343,7 +343,7 @@ pub const ThreadSafeFixedBufferAllocator = struct {
             if (new_end_index > self.buffer.len) {
                 return error.OutOfMemory;
             }
-            end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) ?? return self.buffer[adjusted_index..new_end_index];
+            end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse return self.buffer[adjusted_index..new_end_index];
         }
     }
 
std/linked_list.zig
@@ -169,7 +169,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
         /// Returns:
         ///     A pointer to the last node in the list.
         pub fn pop(list: *Self) ?*Node {
-            const last = list.last ?? return null;
+            const last = list.last orelse return null;
             list.remove(last);
             return last;
         }
@@ -179,7 +179,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
         /// Returns:
         ///     A pointer to the first node in the list.
         pub fn popFirst(list: *Self) ?*Node {
-            const first = list.first ?? return null;
+            const first = list.first orelse return null;
             list.remove(first);
             return first;
         }
std/unicode.zig
@@ -220,7 +220,7 @@ const Utf8Iterator = struct {
     }
 
     pub fn nextCodepoint(it: *Utf8Iterator) ?u32 {
-        const slice = it.nextCodepointSlice() ?? return null;
+        const slice = it.nextCodepointSlice() orelse return null;
 
         switch (slice.len) {
             1 => return u32(slice[0]),
test/cases/cast.zig
@@ -73,7 +73,7 @@ fn Struct(comptime T: type) type {
 
         fn maybePointer(self: ?*const Self) Self {
             const none = Self{ .x = if (T == void) void{} else 0 };
-            return (self ?? &none).*;
+            return (self orelse &none).*;
         }
     };
 }
@@ -87,7 +87,7 @@ const Union = union {
 
     fn maybePointer(self: ?*const Union) Union {
         const none = Union{ .x = 0 };
-        return (self ?? &none).*;
+        return (self orelse &none).*;
     }
 };
 
@@ -100,7 +100,7 @@ const Enum = enum {
     }
 
     fn maybePointer(self: ?*const Enum) Enum {
-        return (self ?? &Enum.None).*;
+        return (self orelse &Enum.None).*;
     }
 };
 
test/cases/null.zig
@@ -15,13 +15,13 @@ test "optional type" {
 
     const next_x: ?i32 = null;
 
-    const z = next_x ?? 1234;
+    const z = next_x orelse 1234;
 
     assert(z == 1234);
 
     const final_x: ?i32 = 13;
 
-    const num = final_x ?? unreachable;
+    const num = final_x orelse unreachable;
 
     assert(num == 13);
 }
@@ -38,7 +38,7 @@ test "test maybe object and get a pointer to the inner value" {
 
 test "rhs maybe unwrap return" {
     const x: ?bool = true;
-    const y = x ?? return;
+    const y = x orelse return;
 }
 
 test "maybe return" {
@@ -53,7 +53,7 @@ fn maybeReturnImpl() void {
 }
 
 fn foo(x: ?i32) ?bool {
-    const value = x ?? return null;
+    const value = x orelse return null;
     return value > 1234;
 }
 
@@ -140,6 +140,6 @@ test "unwrap optional which is field of global var" {
 }
 
 test "null with default unwrap" {
-    const x: i32 = null ?? 1;
+    const x: i32 = null orelse 1;
     assert(x == 1);
 }
test/compile_errors.zig
@@ -2296,7 +2296,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\
         \\    defer try canFail();
         \\
-        \\    const a = maybeInt() ?? return;
+        \\    const a = maybeInt() orelse return;
         \\}
         \\
         \\fn canFail() error!void { }
test/translate_c.zig
@@ -246,13 +246,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\pub extern var fn_ptr: ?extern fn() void;
     ,
         \\pub inline fn foo() void {
-        \\    return (??fn_ptr)();
+        \\    return fn_ptr.?();
         \\}
     ,
         \\pub extern var fn_ptr2: ?extern fn(c_int, f32) u8;
     ,
         \\pub inline fn bar(arg0: c_int, arg1: f32) u8 {
-        \\    return (??fn_ptr2)(arg0, arg1);
+        \\    return fn_ptr2.?(arg0, arg1);
         \\}
     );
 
@@ -608,7 +608,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\    field: c_int,
         \\};
         \\pub export fn read_field(foo: ?[*]struct_Foo) c_int {
-        \\    return (??foo).field;
+        \\    return foo.?.field;
         \\}
     );
 
@@ -969,11 +969,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\pub export fn bar() void {
         \\    var f: ?extern fn() void = foo;
         \\    var b: ?extern fn() c_int = baz;
-        \\    (??f)();
-        \\    (??f)();
+        \\    f.?();
+        \\    f.?();
         \\    foo();
-        \\    _ = (??b)();
-        \\    _ = (??b)();
+        \\    _ = b.?();
+        \\    _ = b.?();
         \\    _ = baz();
         \\}
     );
@@ -984,7 +984,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\}
     ,
         \\pub export fn foo(x: ?[*]c_int) void {
-        \\    (??x).* = 1;
+        \\    x.?.* = 1;
         \\}
     );
 
@@ -1012,7 +1012,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\pub fn foo() c_int {
         \\    var x: c_int = 1234;
         \\    var ptr: ?[*]c_int = &x;
-        \\    return (??ptr).*;
+        \\    return ptr.?.*;
         \\}
     );
 
@@ -1119,7 +1119,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\pub const glClearPFN = PFNGLCLEARPROC;
     ,
         \\pub inline fn glClearUnion(arg0: GLbitfield) void {
-        \\    return (??glProcs.gl.Clear)(arg0);
+        \\    return glProcs.gl.Clear.?(arg0);
         \\}
     ,
         \\pub const OpenGLProcs = union_OpenGLProcs;
build.zig
@@ -102,11 +102,11 @@ pub fn build(b: *Builder) !void {
 
     b.default_step.dependOn(&exe.step);
 
-    const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") ?? false;
+    const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") orelse false;
     if (!skip_self_hosted) {
         test_step.dependOn(&exe.step);
     }
-    const verbose_link_exe = b.option(bool, "verbose-link", "Print link command for self hosted compiler") ?? false;
+    const verbose_link_exe = b.option(bool, "verbose-link", "Print link command for self hosted compiler") orelse false;
     exe.setVerboseLink(verbose_link_exe);
 
     b.installArtifact(exe);
@@ -114,7 +114,7 @@ pub fn build(b: *Builder) !void {
     installCHeaders(b, c_header_files);
 
     const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");
-    const with_lldb = b.option(bool, "with-lldb", "Run tests in LLDB to get a backtrace if one fails") ?? false;
+    const with_lldb = b.option(bool, "with-lldb", "Run tests in LLDB to get a backtrace if one fails") orelse false;
 
     test_step.dependOn(docs_step);