@@ -12,7 +12,7 @@ import typechecker {
1212 CheckedVisibility, EnumId, FunctionId, Module, ModuleId, Scope, ScopeId, StructId, Type, TypeId, VarId, builtin
1313 never_type_id, unknown_type_id, void_type_id
1414}
15- import types { CheckedGenericParameter, CheckedParameter, FunctionGenericParameter, SafetyMode, StructLikeId }
15+ import types { CheckedGenericParameter, CheckedParameter, FunctionGenericParameter, SafetyMode, StructLikeId, Value }
1616import utility { Queue, Span, escape_for_quotes, interpret_escapes, join, panic, prepend_to_each, todo }
1717import compiler { Compiler }
1818import jakt::path { Path }
@@ -230,6 +230,9 @@ struct CodeGenerator {
230230 yield_method: YieldMethod? = None
231231 // Whether the latest output needs to use ErrorOr as return value.
232232 restricted(codegen_value_match, current_error_handler, codegen_statement) yields_erroror: bool = false
233+ // Map from value type's TypeId to list of unique Const TypeIds of that type; mapping per-type tag enums for non-trivial comptime parameters
234+ comptime_tag_enums: [TypeId: [TypeId]] = [:]
235+ comptime_tags_emitted: bool = false
233236
234237 // `forward_error_with_try` controls whether errors are handled through the TRY() macro or through an external mechanism put by the caller.
235238 // noreturn functions may not throw, so let them crash instead.
@@ -301,6 +304,135 @@ struct CodeGenerator {
301304 panic("Cyclic module imports")
302305 }
303306
307+ fn add_comptime_tag(mut this, const_type_id: TypeId, value: Value) throws {
308+ let value_type_id = value.impl.type_id(&mut .program)
309+ if not .comptime_tag_enums.contains(value_type_id) {
310+ .comptime_tag_enums.set(value_type_id, [const_type_id])
311+ } else {
312+ mut list = .comptime_tag_enums[value_type_id]
313+ for existing in list {
314+ if existing.equals(const_type_id) {
315+ return
316+ }
317+ }
318+ list.push(const_type_id)
319+ .comptime_tag_enums.set(value_type_id, list)
320+ }
321+ }
322+
323+ fn should_use_comptime_tag(this, param_type_id: TypeId) -> bool {
324+ if .program.is_trivial_in_cpp(type_id: param_type_id) {
325+ return false
326+ }
327+ guard .program.get_type(param_type_id) is TypeVariable(is_value, value_type_id) else {
328+ return false
329+ }
330+ return is_value and value_type_id.has_value()
331+ }
332+
333+ fn collect_comptime_tags(mut this) throws {
334+ // Scan all types in all modules for non-trivial comptime args
335+ for module in .program.modules {
336+ for type_with_skip in module.types {
337+ match type_with_skip.type {
338+ GenericInstance(id, args) => {
339+ let struct_ = .program.get_struct(id)
340+ for i in 0..args.size() {
341+ if i < struct_.generic_parameters.size() {
342+ let param_type_id = struct_.generic_parameters[i].type_id
343+ if .should_use_comptime_tag(param_type_id) {
344+ if .program.get_type(args[i]) is Const(value) {
345+ .add_comptime_tag(const_type_id: args[i], value)
346+ }
347+ }
348+ }
349+ }
350+ }
351+ GenericEnumInstance(id, args) => {
352+ let enum_ = .program.get_enum(id)
353+ for i in 0..args.size() {
354+ if i < enum_.generic_parameters.size() {
355+ let param_type_id = enum_.generic_parameters[i].type_id
356+ if .should_use_comptime_tag(param_type_id) {
357+ if .program.get_type(args[i]) is Const(value) {
358+ .add_comptime_tag(const_type_id: args[i], value)
359+ }
360+ }
361+ }
362+ }
363+ }
364+ else => {}
365+ }
366+ }
367+
368+ for function in module.functions {
369+ for specialization in function.generics.specializations {
370+ for i in 0..specialization.size() {
371+ if i < function.generics.params.size() {
372+ let param_type_id = function.generics.params[i].checked_parameter.type_id
373+ if .should_use_comptime_tag(param_type_id) {
374+ if .program.get_type(specialization[i]) is Const(value) {
375+ .add_comptime_tag(const_type_id: specialization[i], value)
376+ }
377+ }
378+ }
379+ }
380+ }
381+ }
382+ }
383+ }
384+
385+ fn sanitize_cpp_identifier(anon name: String) -> String {
386+ mut result = StringBuilder::create()
387+ for cp in name.code_points() {
388+ if (cp >= 'a' and cp <= 'z') or (cp >= 'A' and cp <= 'Z') or (cp >= '0' and cp <= '9') or cp == '_' {
389+ result.append(cp)
390+ } else {
391+ result.append('_')
392+ }
393+ }
394+ return result.to_string()
395+ }
396+
397+ fn codegen_comptime_tag_enums(mut this, output: &mut StringBuilder) throws {
398+ for entry in .comptime_tag_enums {
399+ let value_type_id = entry.0
400+ let const_type_ids = entry.1
401+ let type_name = .program.type_name(value_type_id)
402+ let sanitized_name = CodeGenerator::sanitize_cpp_identifier(type_name)
403+
404+ output.appendff("enum class __jakt_comptime_{} : size_t {{\n", sanitized_name)
405+ for i in 0..const_type_ids.size() {
406+ // Use codegen_type to generate the C++ representation as a comment
407+ let value_repr = .codegen_type(const_type_ids[i])
408+ output.appendff("__value_{}, // {}\n", i, value_repr)
409+ }
410+ output.append("};\n")
411+ }
412+ }
413+
414+ fn get_comptime_tag_for_const(mut this, type_id: TypeId) throws -> (String, String)? {
415+ guard .program.get_type(type_id) is Const(value) else {
416+ return None
417+ }
418+
419+ let value_type_id = value.impl.type_id(program: &mut .program)
420+ guard .comptime_tag_enums.contains(value_type_id) else {
421+ return None
422+ }
423+
424+ let const_type_ids = .comptime_tag_enums[value_type_id]
425+ for i in 0..const_type_ids.size() {
426+ if const_type_ids[i].equals(type_id) {
427+ let type_name = .program.type_name(value_type_id)
428+ let sanitized_name = CodeGenerator::sanitize_cpp_identifier(type_name)
429+ return (format("__jakt_comptime_{}", sanitized_name), format("__value_{}", i))
430+ }
431+ }
432+
433+ return None
434+ }
435+
304436 fn generate(compiler: Compiler, anon program: CheckedProgram, debug_info: bool, exported_files: &mut [String:String]) throws -> [String:(String, String)] {
305437 mut generator = CodeGenerator(
306438 compiler
@@ -320,6 +452,8 @@ struct CodeGenerator {
320452 fresh_label_counter: 0
321453 )
322454
455+ generator.collect_comptime_tags()
456+
323457 mut result: [String:(String, String)] = [:]
324458
325459 // Share the output builder between stages so that any module
@@ -405,6 +539,12 @@ struct CodeGenerator {
405539 mut after_headers = StringBuilder::create()
406540 after_headers.append("namespace Jakt {\n")
407541
542+ // Emit comptime tag enums once in the first module header
543+ if module.is_root and not generator.comptime_tags_emitted {
544+ generator.codegen_comptime_tag_enums(output: &mut after_headers)
545+ generator.comptime_tags_emitted = true
546+ }
547+
408548 if not module.is_root {
409549 generator.namespace_stack.push(module.name)
410550 }
@@ -917,6 +1057,21 @@ struct CodeGenerator {
9171057 if i < function.generics.params.size() {
9181058 let param_type_id = function.generics.params[i].checked_parameter.type_id
9191059 if not .program.is_trivial_in_cpp(type_id: param_type_id) {
1060+ if .program.get_type(param_type_id) is TypeVariable(is_value, value_type_id) and is_value and value_type_id.has_value() {
1061+ if .comptime_tag_enums.contains(value_type_id!) {
1062+ if .program.get_type(arg) is TypeVariable(name) {
1063+ if first {
1064+ first = false
1065+ } else {
1066+ output.append(", ")
1067+ }
1068+ let type_name = .program.type_name(value_type_id!)
1069+ let sanitized_name = CodeGenerator::sanitize_cpp_identifier(type_name)
1070+ output.appendff("__jakt_comptime_{} {}", sanitized_name, name)
1071+ }
1072+ continue
1073+ }
1074+ }
9201075 continue
9211076 }
9221077 }
@@ -980,6 +1135,14 @@ struct CodeGenerator {
9801135 if i < function.generics.params.size() {
9811136 let param_type_id = function.generics.params[i].checked_parameter.type_id
9821137 if not .program.is_trivial_in_cpp(type_id: param_type_id) {
1138+ if .get_comptime_tag_for_const(type_id: specialization2[i]) is Some(tag) {
1139+ if first {
1140+ first = false
1141+ } else {
1142+ output.append(", ")
1143+ }
1144+ output.appendff("{}::{}", tag.0, tag.1)
1145+ }
9831146 continue
9841147 }
9851148 }
@@ -1483,6 +1646,11 @@ struct CodeGenerator {
14831646 if .program.is_trivial_in_cpp(type_id: param.type_id) {
14841647 return true
14851648 }
1649+ if .program.get_type(param.type_id) is TypeVariable(is_value, value_type_id) and is_value and value_type_id.has_value() {
1650+ if .comptime_tag_enums.contains(value_type_id!) {
1651+ return true
1652+ }
1653+ }
14861654 }
14871655 return false
14881656 }
@@ -1492,6 +1660,11 @@ struct CodeGenerator {
14921660 if .program.is_trivial_in_cpp(type_id: param.checked_parameter.type_id) {
14931661 return true
14941662 }
1663+ if .program.get_type(param.checked_parameter.type_id) is TypeVariable(is_value, value_type_id) and is_value and value_type_id.has_value() {
1664+ if .comptime_tag_enums.contains(value_type_id!) {
1665+ return true
1666+ }
1667+ }
14951668 }
14961669 return false
14971670 }
@@ -1549,6 +1722,25 @@ struct CodeGenerator {
15491722 mut first = true
15501723 for id in parameters {
15511724 if not .program.is_trivial_in_cpp(type_id: id) {
1725+ // Check if this is a non-trivial comptime value param with a tag enum
1726+ if .program.get_type(id) is TypeVariable(is_value, value_type_id) and is_value and value_type_id.has_value() {
1727+ if .comptime_tag_enums.contains(value_type_id!) {
1728+ if first {
1729+ first = false
1730+ } else {
1731+ output.append(",")
1732+ }
1733+ let type_name = .program.type_name(value_type_id!)
1734+ let sanitized_name = CodeGenerator::sanitize_cpp_identifier(type_name)
1735+ let tag_enum_name = format("__jakt_comptime_{}", sanitized_name)
1736+ output.append(tag_enum_name)
1737+ output.append(" ")
1738+ let name = .codegen_type(id)
1739+ output.append(name)
1740+ names.push(name)
1741+ continue
1742+ }
1743+ }
15521744 continue
15531745 }
15541746 if first {
@@ -4772,6 +4964,14 @@ struct CodeGenerator {
47724964 if i < struct_.generic_parameters.size() {
47734965 let param_type_id = struct_.generic_parameters[i].type_id
47744966 if not .program.is_trivial_in_cpp(type_id: param_type_id) {
4967+ if .get_comptime_tag_for_const(type_id: args[i]) is Some(tag) {
4968+ if not first {
4969+ output.append(", ")
4970+ } else {
4971+ first = false
4972+ }
4973+ output.appendff("{}::{}", tag.0, tag.1)
4974+ }
47754975 continue
47764976 }
47774977 }
@@ -4792,6 +4992,14 @@ struct CodeGenerator {
47924992 if i < struct_.generic_parameters.size() {
47934993 let param_type_id = struct_.generic_parameters[i].type_id
47944994 if not .program.is_trivial_in_cpp(type_id: param_type_id) {
4995+ if .get_comptime_tag_for_const(type_id: args[i]) is Some(tag) {
4996+ if not first {
4997+ output.append(", ")
4998+ } else {
4999+ first = false
5000+ }
5001+ output.appendff("{}::{}", tag.0, tag.1)
5002+ }
47955003 continue
47965004 }
47975005 }
@@ -4851,6 +5059,9 @@ struct CodeGenerator {
48515059 if i < callee.generics.params.size() {
48525060 let param_type_id = callee.generics.params[i].checked_parameter.type_id
48535061 if not .program.is_trivial_in_cpp(type_id: param_type_id) {
5062+ if .get_comptime_tag_for_const(type_id: generic_parameters[i]) is Some(tag) {
5063+ types.push(format("{}::{}", tag.0, tag.1))
5064+ }
48545065 continue
48555066 }
48565067 }
@@ -5319,6 +5530,14 @@ struct CodeGenerator {
53195530 if i < struct_.generic_parameters.size() {
53205531 let param_type_id = struct_.generic_parameters[i].type_id
53215532 if not .program.is_trivial_in_cpp(type_id: param_type_id) {
5533+ if .get_comptime_tag_for_const(type_id: args[i]) is Some(tag) {
5534+ if not first {
5535+ output += ","
5536+ } else {
5537+ first = false
5538+ }
5539+ output += format("{}::{}", tag.0, tag.1)
5540+ }
53225541 continue
53235542 }
53245543 }
@@ -5374,6 +5593,14 @@ struct CodeGenerator {
53745593 if i < enum_.generic_parameters.size() {
53755594 let param_type_id = enum_.generic_parameters[i].type_id
53765595 if not .program.is_trivial_in_cpp(type_id: param_type_id) {
5596+ if .get_comptime_tag_for_const(type_id: args[i]) is Some(tag) {
5597+ if not first {
5598+ output.append(", ")
5599+ } else {
5600+ first = false
5601+ }
5602+ output.appendff("{}::{}", tag.0, tag.1)
5603+ }
53775604 continue
53785605 }
53795606 }
@@ -5915,6 +6142,14 @@ struct CodeGenerator {
59156142 if i < function.generics.params.size() {
59166143 let param_type_id = function.generics.params[i].checked_parameter.type_id
59176144 if not .program.is_trivial_in_cpp(type_id: param_type_id) {
6145+ if .get_comptime_tag_for_const(type_id: instantiation[i]) is Some(tag) {
6146+ if not first {
6147+ output.append(", ")
6148+ } else {
6149+ first = false
6150+ }
6151+ output.appendff("{}::{}", tag.0, tag.1)
6152+ }
59186153 continue
59196154 }
59206155 }
0 commit comments