feat(lang): universal backtick raw identifier — valid in value, decl, AND type position [F0.6]
AGRA ruling (attempt 4): `` `name `` is THE LITERAL identifier `name`, usable in EVERY position — the backtick only means "treat this token as a plain identifier, never the reserved keyword/type", and is never part of the name's text. - Raw in TYPE position is now VALID (reverses attempt-2 "raw is not a type"): `parseTypeExpr` emits a raw `type_expr`; `TypeResolver.resolveNamed` gains a `skip_builtin` flag (threaded from `te.is_raw` via lower.zig + type_bridge) so a `` `s2 `` reference resolves to a `` `s2 ``-declared type (struct/enum/union/alias), else a normal "unknown type 's2'" error (reportIfUnknownType skips the builtin exemption when raw). Bare `s2` in type position stays the builtin int. - Every declaration-name site is is_raw-exemptible: `is_raw` added to TypeExpr + StructDecl/EnumDecl/UnionDecl/ErrorSetDecl/ProtocolDecl/ForeignClassDecl/UfcsAlias/ NamespaceDecl/ImportDecl/CImportDecl/LibraryDecl; parser threads name_is_raw to every decl parse fn; namespace imports carry it through imports.addNamespace. Typed-const path (`` `s2 : s64 : 5 ``) now threads name_span+is_raw (fixes the 1:1-caret bug). - Check<->exemption made structurally symmetric: checkBindingName/checkDeclName take is_raw as a REQUIRED argument and skip inside the check, so no call site can validate a name without honoring the exemption (the desync cause of prior rounds). - Bare reserved-name declarations of every kind still error (0076 preserved); `#import c` foreign names stay auto-raw + bare-callable. specs.md + readme.md updated to the universal model. issue 0089 RESOLVED banner rewritten. Examples: replace 1139 (raw-not-a-type) with 0154 (raw type reference); add 0155 (typed const + union tag) and 1141 (bare type-decl negatives). Gate: zig build + zig build test + run_examples (426 passed, 0 failed).
This commit is contained in:
@@ -88,7 +88,7 @@ pub const Parser = struct {
|
||||
// Check for #import c { ... } (C import block)
|
||||
if (self.current.tag == .identifier and std.mem.eql(u8, self.tokenSlice(self.current), "c") and self.peekNext() == .l_brace) {
|
||||
self.advance(); // consume 'c'
|
||||
return self.parseCImportBlock(start, null);
|
||||
return self.parseCImportBlock(start, null, false);
|
||||
}
|
||||
if (self.current.tag != .string_literal) {
|
||||
return self.fail("expected string path after '#import'");
|
||||
@@ -183,7 +183,7 @@ pub const Parser = struct {
|
||||
// Check for name :: #import c { ... }
|
||||
if (self.current.tag == .identifier and std.mem.eql(u8, self.tokenSlice(self.current), "c") and self.peekNext() == .l_brace) {
|
||||
self.advance(); // consume 'c'
|
||||
return self.parseCImportBlock(start_pos, name);
|
||||
return self.parseCImportBlock(start_pos, name, name_is_raw);
|
||||
}
|
||||
if (self.current.tag != .string_literal) {
|
||||
return self.fail("expected string path after '#import'");
|
||||
@@ -192,7 +192,7 @@ pub const Parser = struct {
|
||||
const path = raw[1 .. raw.len - 1];
|
||||
self.advance();
|
||||
try self.expect(.semicolon);
|
||||
return try self.createNode(start_pos, .{ .import_decl = .{ .path = path, .name = name } });
|
||||
return try self.createNode(start_pos, .{ .import_decl = .{ .path = path, .name = name, .is_raw = name_is_raw } });
|
||||
}
|
||||
|
||||
// Named library: name :: #library "libname";
|
||||
@@ -205,7 +205,7 @@ pub const Parser = struct {
|
||||
const lib_name = raw[1 .. raw.len - 1];
|
||||
self.advance();
|
||||
try self.expect(.semicolon);
|
||||
return try self.createNode(start_pos, .{ .library_decl = .{ .lib_name = lib_name, .name = name } });
|
||||
return try self.createNode(start_pos, .{ .library_decl = .{ .lib_name = lib_name, .name = name, .is_raw = name_is_raw } });
|
||||
}
|
||||
|
||||
// Compile-time evaluation: name :: #run expr;
|
||||
@@ -229,22 +229,22 @@ pub const Parser = struct {
|
||||
|
||||
// Enum declaration
|
||||
if (self.current.tag == .kw_enum) {
|
||||
return self.parseEnumDecl(name, start_pos);
|
||||
return self.parseEnumDecl(name, start_pos, name_is_raw);
|
||||
}
|
||||
|
||||
// Error-set declaration: name :: error { TagA, TagB }
|
||||
if (self.current.tag == .kw_error) {
|
||||
return self.parseErrorSetDecl(name, start_pos);
|
||||
return self.parseErrorSetDecl(name, start_pos, name_is_raw);
|
||||
}
|
||||
|
||||
// Struct declaration
|
||||
if (self.current.tag == .kw_struct) {
|
||||
return self.parseStructDecl(name, start_pos);
|
||||
return self.parseStructDecl(name, start_pos, name_is_raw);
|
||||
}
|
||||
|
||||
// Protocol declaration
|
||||
if (self.current.tag == .kw_protocol) {
|
||||
return self.parseProtocolDecl(name, start_pos);
|
||||
return self.parseProtocolDecl(name, start_pos, name_is_raw);
|
||||
}
|
||||
|
||||
// Foreign-type binding with optional prefix modifiers:
|
||||
@@ -255,12 +255,12 @@ pub const Parser = struct {
|
||||
// `#foreign` flips that to "reference an existing class on the foreign side."
|
||||
// `#jni_main` flags the class as the launchable entry (Android Activity).
|
||||
if (self.tryParseForeignClassPrefix()) |prefix| {
|
||||
return self.parseForeignClassDecl(name, start_pos, prefix.runtime, prefix.is_foreign, prefix.is_main);
|
||||
return self.parseForeignClassDecl(name, start_pos, prefix.runtime, prefix.is_foreign, prefix.is_main, name_is_raw);
|
||||
}
|
||||
|
||||
// C-style union declaration
|
||||
if (self.current.tag == .kw_union) {
|
||||
return self.parseUnionDecl(name, start_pos);
|
||||
return self.parseUnionDecl(name, start_pos, name_is_raw);
|
||||
}
|
||||
|
||||
// UFCS alias: name :: ufcs target;
|
||||
@@ -272,7 +272,7 @@ pub const Parser = struct {
|
||||
const target = self.tokenSlice(self.current);
|
||||
self.advance();
|
||||
try self.expect(.semicolon);
|
||||
return try self.createNode(start_pos, .{ .ufcs_alias = .{ .name = name, .target = target } });
|
||||
return try self.createNode(start_pos, .{ .ufcs_alias = .{ .name = name, .target = target, .is_raw = name_is_raw } });
|
||||
}
|
||||
|
||||
// Function declaration: (params) -> type { body } or () { body }
|
||||
@@ -332,7 +332,7 @@ pub const Parser = struct {
|
||||
return try self.createNode(start_pos, .{ .const_decl = .{ .name = name, .type_annotation = null, .value = value, .name_span = name_span, .is_raw = name_is_raw } });
|
||||
}
|
||||
|
||||
fn parseCImportBlock(self: *Parser, start: u32, name: ?[]const u8) anyerror!*Node {
|
||||
fn parseCImportBlock(self: *Parser, start: u32, name: ?[]const u8, name_is_raw: bool) anyerror!*Node {
|
||||
try self.expect(.l_brace);
|
||||
var includes = std.ArrayList([]const u8).empty;
|
||||
var sources = std.ArrayList([]const u8).empty;
|
||||
@@ -381,6 +381,7 @@ pub const Parser = struct {
|
||||
.defines = try defines.toOwnedSlice(self.allocator),
|
||||
.flags = try flags.toOwnedSlice(self.allocator),
|
||||
.name = name,
|
||||
.is_raw = name_is_raw,
|
||||
} });
|
||||
}
|
||||
|
||||
@@ -394,7 +395,7 @@ pub const Parser = struct {
|
||||
self.advance();
|
||||
const value = try self.parseExpr();
|
||||
try self.expectSemicolonAfter(value);
|
||||
return try self.createNode(start_pos, .{ .const_decl = .{ .name = name, .type_annotation = type_node, .value = value } });
|
||||
return try self.createNode(start_pos, .{ .const_decl = .{ .name = name, .type_annotation = type_node, .value = value, .name_span = name_span, .is_raw = name_is_raw } });
|
||||
}
|
||||
|
||||
if (self.current.tag == .equal) {
|
||||
@@ -629,11 +630,16 @@ pub const Parser = struct {
|
||||
}
|
||||
|
||||
if (self.current.tag.isTypeKeyword() or self.isIdentLike()) {
|
||||
// A backtick raw identifier (`` `s2 ``) is a VALUE-name escape; it is
|
||||
// never a type. Reject it in type position rather than silently
|
||||
// type-classifying it (issue 0089).
|
||||
// A backtick raw identifier (`` `s2 ``) in type position is the
|
||||
// LITERAL name `s2` used as a type reference — never the builtin /
|
||||
// reserved keyword. It is always a plain named-type reference (no
|
||||
// qualified-path, `Closure`, or parameterized continuation), so emit
|
||||
// a raw `type_expr` and return; resolution skips the builtin
|
||||
// classifier and looks up a `` `s2 ``-declared type (issue 0089).
|
||||
if (self.current.is_raw) {
|
||||
return self.failFmt("`{s}` is a raw identifier, not a type — the backtick escape names a value, never a type", .{self.tokenSlice(self.current)});
|
||||
const raw_name = self.tokenSlice(self.current);
|
||||
self.advance();
|
||||
return try self.createNode(start, .{ .type_expr = .{ .name = raw_name, .is_raw = true } });
|
||||
}
|
||||
var name = self.tokenSlice(self.current);
|
||||
self.advance();
|
||||
@@ -787,20 +793,20 @@ pub const Parser = struct {
|
||||
}
|
||||
// Inline struct type in type position: struct { ... }
|
||||
if (self.current.tag == .kw_struct) {
|
||||
return try self.parseStructDecl("__anon", start);
|
||||
return try self.parseStructDecl("__anon", start, false);
|
||||
}
|
||||
// Inline C-style union in type position: union { ... }
|
||||
if (self.current.tag == .kw_union) {
|
||||
return try self.parseUnionDecl("__anon", start);
|
||||
return try self.parseUnionDecl("__anon", start, false);
|
||||
}
|
||||
// Inline enum type in type position: enum { ... }
|
||||
if (self.current.tag == .kw_enum) {
|
||||
return try self.parseEnumDecl("__anon", start);
|
||||
return try self.parseEnumDecl("__anon", start, false);
|
||||
}
|
||||
return self.fail("expected type name");
|
||||
}
|
||||
|
||||
fn parseEnumDecl(self: *Parser, name: []const u8, start_pos: u32) anyerror!*Node {
|
||||
fn parseEnumDecl(self: *Parser, name: []const u8, start_pos: u32, name_is_raw: bool) anyerror!*Node {
|
||||
self.advance(); // skip 'enum'
|
||||
|
||||
// Check for 'flags' modifier: enum flags { ... }
|
||||
@@ -874,10 +880,11 @@ pub const Parser = struct {
|
||||
.is_flags = is_flags,
|
||||
.variant_values = if (has_any_value) try variant_values.toOwnedSlice(self.allocator) else &.{},
|
||||
.backing_type = backing_type,
|
||||
.is_raw = name_is_raw,
|
||||
} });
|
||||
}
|
||||
|
||||
fn parseErrorSetDecl(self: *Parser, name: []const u8, start_pos: u32) anyerror!*Node {
|
||||
fn parseErrorSetDecl(self: *Parser, name: []const u8, start_pos: u32, name_is_raw: bool) anyerror!*Node {
|
||||
self.advance(); // skip 'error'
|
||||
try self.expect(.l_brace);
|
||||
var tag_names = std.ArrayList([]const u8).empty;
|
||||
@@ -899,10 +906,11 @@ pub const Parser = struct {
|
||||
return try self.createNode(start_pos, .{ .error_set_decl = .{
|
||||
.name = name,
|
||||
.tag_names = try tag_names.toOwnedSlice(self.allocator),
|
||||
.is_raw = name_is_raw,
|
||||
} });
|
||||
}
|
||||
|
||||
fn parseUnionDecl(self: *Parser, name: []const u8, start_pos: u32) anyerror!*Node {
|
||||
fn parseUnionDecl(self: *Parser, name: []const u8, start_pos: u32, name_is_raw: bool) anyerror!*Node {
|
||||
self.advance(); // skip 'union'
|
||||
try self.expect(.l_brace);
|
||||
var field_names = std.ArrayList([]const u8).empty;
|
||||
@@ -914,7 +922,7 @@ pub const Parser = struct {
|
||||
const anon_field = try std.fmt.allocPrint(self.allocator, "__anon_{d}", .{anon_idx});
|
||||
anon_idx += 1;
|
||||
const anon_struct_name = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ name, anon_field });
|
||||
const struct_node = try self.parseStructDecl(anon_struct_name, self.current.loc.start);
|
||||
const struct_node = try self.parseStructDecl(anon_struct_name, self.current.loc.start, false);
|
||||
try field_names.append(self.allocator, anon_field);
|
||||
try field_types.append(self.allocator, struct_node);
|
||||
if (self.current.tag == .semicolon) {
|
||||
@@ -942,10 +950,11 @@ pub const Parser = struct {
|
||||
.name = name,
|
||||
.field_names = try field_names.toOwnedSlice(self.allocator),
|
||||
.field_types = try field_types.toOwnedSlice(self.allocator),
|
||||
.is_raw = name_is_raw,
|
||||
} });
|
||||
}
|
||||
|
||||
fn parseStructDecl(self: *Parser, name: []const u8, start_pos: u32) anyerror!*Node {
|
||||
fn parseStructDecl(self: *Parser, name: []const u8, start_pos: u32, name_is_raw: bool) anyerror!*Node {
|
||||
self.advance(); // skip 'struct'
|
||||
|
||||
// Optional `#compiler` attribute: all methods inside this struct are
|
||||
@@ -1133,10 +1142,11 @@ pub const Parser = struct {
|
||||
.using_entries = try using_entries.toOwnedSlice(self.allocator),
|
||||
.methods = try methods.toOwnedSlice(self.allocator),
|
||||
.constants = try constants.toOwnedSlice(self.allocator),
|
||||
.is_raw = name_is_raw,
|
||||
} });
|
||||
}
|
||||
|
||||
fn parseProtocolDecl(self: *Parser, name: []const u8, start_pos: u32) anyerror!*Node {
|
||||
fn parseProtocolDecl(self: *Parser, name: []const u8, start_pos: u32, name_is_raw: bool) anyerror!*Node {
|
||||
self.advance(); // skip 'protocol'
|
||||
|
||||
// Optional type params: protocol(Target: Type, U: Type) { ... }
|
||||
@@ -1249,6 +1259,7 @@ pub const Parser = struct {
|
||||
.methods = try methods.toOwnedSlice(self.allocator),
|
||||
.is_inline = is_inline,
|
||||
.type_params = try type_params.toOwnedSlice(self.allocator),
|
||||
.is_raw = name_is_raw,
|
||||
} });
|
||||
}
|
||||
|
||||
@@ -1335,7 +1346,7 @@ pub const Parser = struct {
|
||||
};
|
||||
}
|
||||
|
||||
fn parseForeignClassDecl(self: *Parser, name: []const u8, start_pos: u32, runtime: ast.ForeignRuntime, is_foreign: bool, is_main: bool) anyerror!*Node {
|
||||
fn parseForeignClassDecl(self: *Parser, name: []const u8, start_pos: u32, runtime: ast.ForeignRuntime, is_foreign: bool, is_main: bool, name_is_raw: bool) anyerror!*Node {
|
||||
self.advance(); // skip directive token
|
||||
|
||||
try self.expect(.l_paren);
|
||||
@@ -1576,6 +1587,7 @@ pub const Parser = struct {
|
||||
.members = try members.toOwnedSlice(self.allocator),
|
||||
.is_foreign = is_foreign,
|
||||
.is_main = is_main,
|
||||
.is_raw = name_is_raw,
|
||||
} });
|
||||
}
|
||||
|
||||
@@ -2820,15 +2832,15 @@ pub const Parser = struct {
|
||||
},
|
||||
.kw_struct => {
|
||||
// Anonymous struct expression: struct { value: T; count: u32; }
|
||||
return try self.parseStructDecl("__anon", start);
|
||||
return try self.parseStructDecl("__anon", start, false);
|
||||
},
|
||||
.kw_enum => {
|
||||
// Anonymous enum expression: enum { variant: T; other: u32; }
|
||||
return try self.parseEnumDecl("__anon", start);
|
||||
return try self.parseEnumDecl("__anon", start, false);
|
||||
},
|
||||
.kw_union => {
|
||||
// Anonymous C-style union expression: union { f: f32; i: s32; }
|
||||
return try self.parseUnionDecl("__anon", start);
|
||||
return try self.parseUnionDecl("__anon", start, false);
|
||||
},
|
||||
.kw_if => {
|
||||
return self.parseIfExpr();
|
||||
|
||||
Reference in New Issue
Block a user