| ... | ... | @@ -852,7 +852,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) |
| 852 | 852 | |
| 853 | 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 | 857 | return try s.addRef(.memory, .{ |
| 858 | 858 | .init = runtime_known, |
| ... | ... | @@ -1069,19 +1069,22 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) |
| 1069 | 1069 | |
| 1070 | 1070 | assert(s.frame.mode == .run_time); |
| 1071 | 1071 | |
| 1072 | | const yes = s.getRef(if_data.yes, .no_mark); |
| 1073 | | const no = s.getRef(if_data.no, .no_mark); |
| 1072 | var yes = s.getRef(if_data.yes, .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 no_coerced = try s.coerceValue(s.lazyNode(.rhs), no, union_type); |
| 1078 | const union_type = s.peerTypeResolution(&.{ s.typeOfRef(yes), s.typeOfRef(no) }); |
| 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 | 1083 | s.inst_map.add(s.frame.currentIndex(), try s.addRef(.@"if", .{ |
| 1081 | 1084 | .cond = s.getRef(if_data.cond, .no_mark), |
| 1082 | 1085 | .data = try s.func.?.extra.append(s.gpa, Air.If.Data, .{ |
| 1083 | | .yes = yes, |
| 1084 | | .no = no, |
| 1086 | .yes = yes_coerced, |
| 1087 | .no = no_coerced, |
| 1085 | 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 | 1246 | |
| 1244 | 1247 | // When both branches are values, Sema has nowhere |
| 1245 | 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 | 1254 | const yes_coerced = try s.coerceValue(s.lazyNode(.lhs), yn[0], union_type); |
| 1249 | 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 | 1409 | try s.ensureAtRuntime(s.lazyNode(.main_node)); |
| 1404 | 1410 | |
| 1405 | 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 | 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 { |
| 1818 | | switch (ref.decode()) { |
| 1819 | | .value => |v| { |
| 1820 | | const ty = v.typeOf(s.ip); |
| 1821 | | switch (ty) { |
| 1822 | | // Allowed at runtime |
| 1823 | | .bool, |
| 1824 | | .float, |
| 1825 | | .int32, |
| 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 | | } |
| 1823 | fn coerceBranch(s: *Sema, src: LazyNode, ref: Air.Inst.Ref, to: Type) !Air.Inst.Ref { |
| 1824 | return switch (ref.decode()) { |
| 1825 | .index => |block| { |
| 1826 | if (s.typeOfIndex(block) != to) return ref; |
| 1827 | @panic("TODO"); |
| 1828 | }, |
| 1829 | .value => |value| (try s.coerceValue(src, value, to)).toAir(), |
| 1830 | }; |
| 1831 | } |
| 1838 | 1832 | |
| 1839 | | return (try s.makeValue(.{ |
| 1840 | | .float = float, |
| 1841 | | })).toAir(); |
| 1842 | | }, |
| 1843 | | .numeric_literal_array => { |
| 1844 | | try s.report.warn(.numeric_int_array_var_ambiguity, .{ |
| 1845 | | .src = src, |
| 1846 | | }); |
| 1847 | | return (try s.coerceArrayValue(src, v, .float)).toAir(); |
| 1848 | | }, |
| 1849 | | .never_array => { |
| 1850 | | if (v.valueData(s.ip, .array_value).get(s.ip).items_len != 0) { |
| 1851 | | return s.fail(.runtime_incompatible_type, .{ |
| 1852 | | .src = src, |
| 1853 | | .type = ty, |
| 1854 | | }); |
| 1855 | | } |
| 1856 | | }, |
| 1857 | | .type, |
| 1858 | | .never, |
| 1859 | | => return s.fail(.runtime_incompatible_type, .{ |
| 1833 | fn ensureValueRuntime(s: *Sema, src: LazyNode, value: Value) Error!Value { |
| 1834 | const ty = value.typeOf(s.ip); |
| 1835 | return switch (ty) { |
| 1836 | // Allowed at runtime |
| 1837 | .bool, |
| 1838 | .float, |
| 1839 | .int32, |
| 1840 | .string, |
| 1841 | .void, |
| 1842 | .empty_tuple, |
| 1843 | => value, |
| 1844 | .numeric_literal => { |
| 1845 | const float = value.toFloat(s.ip) orelse unreachable; |
| 1846 | |
| 1847 | std.debug.print("wtf:{d}\n", .{float}); |
| 1848 | if (core.inInt32Range(float)) { |
| 1849 | try s.report.warn(.numeric_int_var_ambiguity, .{ |
| 1860 | 1850 | .src = src, |
| 1861 | | .type = ty, |
| 1862 | | }), |
| 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 | | } |
| 1851 | }); |
| 1852 | } |
| 1871 | 1853 | |
| 1872 | | // When a function is used as a runtime operand, it needs to |
| 1873 | | // have a resolved return type, so that calling it later is |
| 1874 | | // able to know what it returns. |
| 1875 | | _ = try s.resolveFunctionPrototype(ref, ty); |
| 1876 | | }, |
| 1854 | return try s.makeValue(.{ |
| 1855 | .float = float, |
| 1856 | }); |
| 1857 | }, |
| 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 | 1901 | .index => { |
| 1880 | 1902 | const ty = s.typeOfRef(ref); |
| 1881 | 1903 | if (!ty.hasPossibleRepresentation(s.ip)) { |
| ... | ... | @@ -1884,13 +1906,13 @@ fn ensureValueRuntime(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) Error!Air.Inst |
| 1884 | 1906 | .type = ty, |
| 1885 | 1907 | }); |
| 1886 | 1908 | } |
| 1909 | return ref; |
| 1887 | 1910 | }, |
| 1888 | 1911 | } |
| 1889 | | return ref; |
| 1890 | 1912 | } |
| 1891 | 1913 | |
| 1892 | 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 | 1916 | const ty = s.typeOfRef(runtime); |
| 1895 | 1917 | if (!ty.hasDefinedRepresentation(s.ip)) { |
| 1896 | 1918 | return s.fail(.type_no_defined_representation, .{ |
| ... | ... | @@ -1901,25 +1923,27 @@ fn ensureValueDefinedRepresentation(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) |
| 1901 | 1923 | return runtime; |
| 1902 | 1924 | } |
| 1903 | 1925 | |
| 1904 | | fn unionType(s: *Sema, a: Type, b: Type) Type { |
| 1905 | | if (a == b) { |
| 1906 | | return a; |
| 1926 | fn peerTypeResolution(s: *Sema, types: []const Type) Type { |
| 1927 | var a = types[0]; |
| 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 | | |
| 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 } }); |
| 1946 | return a; |
| 1923 | 1947 | } |
| 1924 | 1948 | |
| 1925 | 1949 | fn coerceArrayValue(s: *Sema, src: LazyNode, value: Value, item_type: Type) !Value { |