| ... | @@ -852,7 +852,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) | ... | @@ -852,7 +852,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) |
| 852 | | 852 | |
| 853 | const initial_value = s.getRef(data.operand, .marked); | 853 | const initial_value = s.getRef(data.operand, .marked); |
| 854 | | 854 | |
| 855 | const runtime_known = try s.ensureValueRuntime(s.lazyNode(.operand), initial_value); | 855 | const runtime_known = try s.ensureRefRuntime(s.lazyNode(.operand), initial_value); |
| 856 | | 856 | |
| 857 | return try s.addRef(.memory, .{ | 857 | return try s.addRef(.memory, .{ |
| 858 | .init = runtime_known, | 858 | .init = runtime_known, |
| ... | @@ -1069,19 +1069,22 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) | ... | @@ -1069,19 +1069,22 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) |
| 1069 | | 1069 | |
| 1070 | assert(s.frame.mode == .run_time); | 1070 | assert(s.frame.mode == .run_time); |
| 1071 | | 1071 | |
| 1072 | const yes = s.getRef(if_data.yes, .no_mark); | 1072 | var yes = s.getRef(if_data.yes, .no_mark); |
| 1073 | const no = s.getRef(if_data.no, .no_mark); | 1073 | var no = s.getRef(if_data.no, .no_mark); |
| 1074 | | 1074 | |
| 1075 | const union_type = s.unionType(s.typeOfRef(yes), s.typeOfRef(no)); | 1075 | if (yes.value()) |val| yes = (try s.ensureValueRuntime(s.lazyNode(.lhs), val)).toAir(); |
| | 1076 | if (no.value()) |val| no = (try s.ensureValueRuntime(s.lazyNode(.rhs), val)).toAir(); |
| 1076 | | 1077 | |
| 1077 | // const yes_coerced = try s.coerceValue(s.lazyNode(.lhs), yes, union_type); | 1078 | const union_type = s.peerTypeResolution(&.{ s.typeOfRef(yes), s.typeOfRef(no) }); |
| 1078 | // const no_coerced = try s.coerceValue(s.lazyNode(.rhs), no, union_type); | 1079 | |
| | 1080 | const yes_coerced = try s.coerceBranch(s.lazyNode(.lhs), yes, union_type); |
| | 1081 | const no_coerced = try s.coerceBranch(s.lazyNode(.rhs), no, union_type); |
| 1079 | | 1082 | |
| 1080 | s.inst_map.add(s.frame.currentIndex(), try s.addRef(.@"if", .{ | 1083 | s.inst_map.add(s.frame.currentIndex(), try s.addRef(.@"if", .{ |
| 1081 | .cond = s.getRef(if_data.cond, .no_mark), | 1084 | .cond = s.getRef(if_data.cond, .no_mark), |
| 1082 | .data = try s.func.?.extra.append(s.gpa, Air.If.Data, .{ | 1085 | .data = try s.func.?.extra.append(s.gpa, Air.If.Data, .{ |
| 1083 | .yes = yes, | 1086 | .yes = yes_coerced, |
| 1084 | .no = no, | 1087 | .no = no_coerced, |
| 1085 | .union_ty = union_type, | 1088 | .union_ty = union_type, |
| 1086 | }), | 1089 | }), |
| 1087 | })); | 1090 | })); |
| ... | @@ -1243,7 +1246,10 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) | ... | @@ -1243,7 +1246,10 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) |
| 1243 | | 1246 | |
| 1244 | // When both branches are values, Sema has nowhere | 1247 | // When both branches are values, Sema has nowhere |
| 1245 | // to jump to, so only one Air instruction is emitted | 1248 | // to jump to, so only one Air instruction is emitted |
| 1246 | const union_type = s.unionType(yn[0].typeOf(s.ip), yn[1].typeOf(s.ip)); | 1249 | yn[0] = try s.ensureValueRuntime(s.lazyNode(.lhs), yn[0]); |
| | 1250 | yn[1] = try s.ensureValueRuntime(s.lazyNode(.lhs), yn[1]); |
| | 1251 | |
| | 1252 | const union_type = s.peerTypeResolution(&.{ yn[0].typeOf(s.ip), yn[1].typeOf(s.ip) }); |
| 1247 | | 1253 | |
| 1248 | const yes_coerced = try s.coerceValue(s.lazyNode(.lhs), yn[0], union_type); | 1254 | const yes_coerced = try s.coerceValue(s.lazyNode(.lhs), yn[0], union_type); |
| 1249 | const no_coerced = try s.coerceValue(s.lazyNode(.rhs), yn[1], union_type); | 1255 | const no_coerced = try s.coerceValue(s.lazyNode(.rhs), yn[1], union_type); |
| ... | @@ -1403,7 +1409,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) | ... | @@ -1403,7 +1409,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) |
| 1403 | try s.ensureAtRuntime(s.lazyNode(.main_node)); | 1409 | try s.ensureAtRuntime(s.lazyNode(.main_node)); |
| 1404 | | 1410 | |
| 1405 | for (scratch.slice(), 0..) |*ref, i| { | 1411 | for (scratch.slice(), 0..) |*ref, i| { |
| 1406 | ref.* = try s.ensureValueRuntime(s.lazyNode(.argument(@intCast(i))), ref.*); | 1412 | ref.* = try s.ensureRefRuntime(s.lazyNode(.argument(@intCast(i))), ref.*); |
| 1407 | } | 1413 | } |
| 1408 | } | 1414 | } |
| 1409 | | 1415 | |
| ... | @@ -1814,68 +1820,84 @@ fn coerceValue(s: *Sema, src: LazyNode, value: Value, to: Type) Error!Value { | ... | @@ -1814,68 +1820,84 @@ fn coerceValue(s: *Sema, src: LazyNode, value: Value, to: Type) Error!Value { |
| 1814 | return s.coercionError(src, from, to); | 1820 | return s.coercionError(src, from, to); |
| 1815 | } | 1821 | } |
| 1816 | | 1822 | |
| 1817 | fn ensureValueRuntime(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) Error!Air.Inst.Ref { | 1823 | fn coerceBranch(s: *Sema, src: LazyNode, ref: Air.Inst.Ref, to: Type) !Air.Inst.Ref { |
| 1818 | switch (ref.decode()) { | 1824 | return switch (ref.decode()) { |
| 1819 | .value => |v| { | 1825 | .index => |block| { |
| 1820 | const ty = v.typeOf(s.ip); | 1826 | if (s.typeOfIndex(block) != to) return ref; |
| 1821 | switch (ty) { | 1827 | @panic("TODO"); |
| 1822 | // Allowed at runtime | 1828 | }, |
| 1823 | .bool, | 1829 | .value => |value| (try s.coerceValue(src, value, to)).toAir(), |
| 1824 | .float, | 1830 | }; |
| 1825 | .int32, | 1831 | } |
| 1826 | .string, | | |
| 1827 | .void, | | |
| 1828 | .empty_tuple, | | |
| 1829 | => {}, | | |
| 1830 | .numeric_literal => { | | |
| 1831 | const float = v.toFloat(s.ip) orelse unreachable; | | |
| 1832 | | | |
| 1833 | if (!core.inInt32Range(float)) { | | |
| 1834 | try s.report.warn(.numeric_int_var_ambiguity, .{ | | |
| 1835 | .src = src, | | |
| 1836 | }); | | |
| 1837 | } | | |
| 1838 | | 1832 | |
| 1839 | return (try s.makeValue(.{ | 1833 | fn ensureValueRuntime(s: *Sema, src: LazyNode, value: Value) Error!Value { |
| 1840 | .float = float, | 1834 | const ty = value.typeOf(s.ip); |
| 1841 | })).toAir(); | 1835 | return switch (ty) { |
| 1842 | }, | 1836 | // Allowed at runtime |
| 1843 | .numeric_literal_array => { | 1837 | .bool, |
| 1844 | try s.report.warn(.numeric_int_array_var_ambiguity, .{ | 1838 | .float, |
| 1845 | .src = src, | 1839 | .int32, |
| 1846 | }); | 1840 | .string, |
| 1847 | return (try s.coerceArrayValue(src, v, .float)).toAir(); | 1841 | .void, |
| 1848 | }, | 1842 | .empty_tuple, |
| 1849 | .never_array => { | 1843 | => value, |
| 1850 | if (v.valueData(s.ip, .array_value).get(s.ip).items_len != 0) { | 1844 | .numeric_literal => { |
| 1851 | return s.fail(.runtime_incompatible_type, .{ | 1845 | const float = value.toFloat(s.ip) orelse unreachable; |
| 1852 | .src = src, | 1846 | |
| 1853 | .type = ty, | 1847 | std.debug.print("wtf:{d}\n", .{float}); |
| 1854 | }); | 1848 | if (core.inInt32Range(float)) { |
| 1855 | } | 1849 | try s.report.warn(.numeric_int_var_ambiguity, .{ |
| 1856 | }, | | |
| 1857 | .type, | | |
| 1858 | .never, | | |
| 1859 | => return s.fail(.runtime_incompatible_type, .{ | | |
| 1860 | .src = src, | 1850 | .src = src, |
| 1861 | .type = ty, | 1851 | }); |
| 1862 | }), | 1852 | } |
| 1863 | .@"opaque" => @panic("TODO: represent comptime-known opaques better"), | | |
| 1864 | _ => { | | |
| 1865 | if (!ty.hasPossibleRepresentation(s.ip)) { | | |
| 1866 | return s.fail(.runtime_incompatible_type, .{ | | |
| 1867 | .src = src, | | |
| 1868 | .type = ty, | | |
| 1869 | }); | | |
| 1870 | } | | |
| 1871 | | 1853 | |
| 1872 | // When a function is used as a runtime operand, it needs to | 1854 | return try s.makeValue(.{ |
| 1873 | // have a resolved return type, so that calling it later is | 1855 | .float = float, |
| 1874 | // able to know what it returns. | 1856 | }); |
| 1875 | _ = try s.resolveFunctionPrototype(ref, ty); | 1857 | }, |
| 1876 | }, | 1858 | .numeric_literal_array => { |
| | 1859 | try s.report.warn(.numeric_int_array_var_ambiguity, .{ |
| | 1860 | .src = src, |
| | 1861 | }); |
| | 1862 | return try s.coerceArrayValue(src, value, .float); |
| | 1863 | }, |
| | 1864 | .never_array => { |
| | 1865 | if (value.valueData(s.ip, .array_value).get(s.ip).items_len != 0) { |
| | 1866 | return s.fail(.runtime_incompatible_type, .{ |
| | 1867 | .src = src, |
| | 1868 | .type = ty, |
| | 1869 | }); |
| 1877 | } | 1870 | } |
| | 1871 | return value; |
| 1878 | }, | 1872 | }, |
| | 1873 | .type, |
| | 1874 | .never, |
| | 1875 | => return s.fail(.runtime_incompatible_type, .{ |
| | 1876 | .src = src, |
| | 1877 | .type = ty, |
| | 1878 | }), |
| | 1879 | .@"opaque" => @panic("TODO: represent comptime-known opaques better"), |
| | 1880 | _ => { |
| | 1881 | if (!ty.hasPossibleRepresentation(s.ip)) { |
| | 1882 | return s.fail(.runtime_incompatible_type, .{ |
| | 1883 | .src = src, |
| | 1884 | .type = ty, |
| | 1885 | }); |
| | 1886 | } |
| | 1887 | |
| | 1888 | // When a function is used as a runtime operand, it needs to |
| | 1889 | // have a resolved return type, so that calling it later is |
| | 1890 | // able to know what it returns. |
| | 1891 | _ = try s.resolveFunctionPrototype(value.toAir(), ty); |
| | 1892 | |
| | 1893 | return value; |
| | 1894 | }, |
| | 1895 | }; |
| | 1896 | } |
| | 1897 | |
| | 1898 | fn ensureRefRuntime(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) Error!Air.Inst.Ref { |
| | 1899 | switch (ref.decode()) { |
| | 1900 | .value => |v| return (try s.ensureValueRuntime(src, v)).toAir(), |
| 1879 | .index => { | 1901 | .index => { |
| 1880 | const ty = s.typeOfRef(ref); | 1902 | const ty = s.typeOfRef(ref); |
| 1881 | if (!ty.hasPossibleRepresentation(s.ip)) { | 1903 | if (!ty.hasPossibleRepresentation(s.ip)) { |
| ... | @@ -1884,13 +1906,13 @@ fn ensureValueRuntime(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) Error!Air.Inst | ... | @@ -1884,13 +1906,13 @@ fn ensureValueRuntime(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) Error!Air.Inst |
| 1884 | .type = ty, | 1906 | .type = ty, |
| 1885 | }); | 1907 | }); |
| 1886 | } | 1908 | } |
| | 1909 | return ref; |
| 1887 | }, | 1910 | }, |
| 1888 | } | 1911 | } |
| 1889 | return ref; | | |
| 1890 | } | 1912 | } |
| 1891 | | 1913 | |
| 1892 | fn ensureValueDefinedRepresentation(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) Error!Air.Inst.Ref { | 1914 | fn ensureValueDefinedRepresentation(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) Error!Air.Inst.Ref { |
| 1893 | const runtime = try s.ensureValueRuntime(src, ref); | 1915 | const runtime = try s.ensureRefRuntime(src, ref); |
| 1894 | const ty = s.typeOfRef(runtime); | 1916 | const ty = s.typeOfRef(runtime); |
| 1895 | if (!ty.hasDefinedRepresentation(s.ip)) { | 1917 | if (!ty.hasDefinedRepresentation(s.ip)) { |
| 1896 | return s.fail(.type_no_defined_representation, .{ | 1918 | return s.fail(.type_no_defined_representation, .{ |
| ... | @@ -1901,25 +1923,27 @@ fn ensureValueDefinedRepresentation(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) | ... | @@ -1901,25 +1923,27 @@ fn ensureValueDefinedRepresentation(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) |
| 1901 | return runtime; | 1923 | return runtime; |
| 1902 | } | 1924 | } |
| 1903 | | 1925 | |
| 1904 | fn unionType(s: *Sema, a: Type, b: Type) Type { | 1926 | fn peerTypeResolution(s: *Sema, types: []const Type) Type { |
| 1905 | if (a == b) { | 1927 | var a = types[0]; |
| 1906 | return a; | 1928 | for (types[1..]) |b| { |
| | 1929 | if (a == b) { |
| | 1930 | continue; |
| | 1931 | } else if (b == .never) { |
| | 1932 | continue; |
| | 1933 | } else if (a == .never) { |
| | 1934 | a = b; |
| | 1935 | } else { |
| | 1936 | std.debug.panic( |
| | 1937 | "TODO: when two non-equal types implicitly merge (if/match), this should generate a union type.\na: {}\nb: {}", |
| | 1938 | .{ |
| | 1939 | a.dump(s.ip), |
| | 1940 | b.dump(s.ip), |
| | 1941 | }, |
| | 1942 | ); |
| | 1943 | return s.ip.get(s.gpa, .{ .@"union" = &.{ a, b } }); |
| | 1944 | } |
| 1907 | } | 1945 | } |
| 1908 | | 1946 | return a; |
| 1909 | if (a == .never) | | |
| 1910 | return b; | | |
| 1911 | | | |
| 1912 | if (b == .never) | | |
| 1913 | return a; | | |
| 1914 | | | |
| 1915 | std.debug.panic( | | |
| 1916 | "TODO: when two non-equal types implicitly merge (if/match), this should generate a union type.\na: {}\nb: {}", | | |
| 1917 | .{ | | |
| 1918 | a.dump(s.ip), | | |
| 1919 | b.dump(s.ip), | | |
| 1920 | }, | | |
| 1921 | ); | | |
| 1922 | // return s.ip.get(s.gpa, .{ .@"union" = &.{ a, b } }); | | |
| 1923 | } | 1947 | } |
| 1924 | | 1948 | |
| 1925 | fn coerceArrayValue(s: *Sema, src: LazyNode, value: Value, item_type: Type) !Value { | 1949 | fn coerceArrayValue(s: *Sema, src: LazyNode, value: Value, item_type: Type) !Value { |