Skip to content

Commit

Permalink
chore(compiler): Add warning for print and toString with unsafe v…
Browse files Browse the repository at this point in the history
…alues (#2018)
  • Loading branch information
spotandjake committed Mar 3, 2024
1 parent 1626a1f commit e64f2ff
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 34 deletions.
58 changes: 53 additions & 5 deletions compiler/src/typed/typed_well_formedness.re
Expand Up @@ -42,10 +42,10 @@ let rec resolve_unsafe_type = ({exp_type}) => {
switch (t.desc) {
| TTyConstr(path, _, _) =>
switch (path) {
| t when t == Builtin_types.path_wasmi32 => "WasmI32"
| t when t == Builtin_types.path_wasmi64 => "WasmI64"
| t when t == Builtin_types.path_wasmf32 => "WasmF32"
| t when t == Builtin_types.path_wasmf64 => "WasmI64"
| t when t == Builtin_types.path_wasmi32 => "I32"
| t when t == Builtin_types.path_wasmi64 => "I64"
| t when t == Builtin_types.path_wasmf32 => "F32"
| t when t == Builtin_types.path_wasmf64 => "I64"
| _ => failwith("Impossible: type cannot be a wasm unsafe value")
}
| TTyLink(t) => type_is_wasm_unsafe(t)
Expand Down Expand Up @@ -263,7 +263,8 @@ module WellFormednessArg: TypedtreeIter.IteratorArgument = {
if (List.exists(((_, arg)) => exp_is_wasm_unsafe(arg), args)) {
let typeName =
switch (args) {
| [(_, arg), _] => resolve_unsafe_type(arg)
| [(_, arg), _] when exp_is_wasm_unsafe(arg) =>
"Wasm" ++ resolve_unsafe_type(arg)
| _ => "(WasmI32 | WasmI64 | WasmF32 | WasmF64)"
};
let warning =
Expand All @@ -276,6 +277,53 @@ module WellFormednessArg: TypedtreeIter.IteratorArgument = {
Grain_parsing.Location.prerr_warning(exp_loc, warning);
};
}
// Check: Warn if using Pervasives print on WasmXX types
| TExpApp(
{
exp_desc:
TExpIdent(
Path.PExternal(Path.PIdent({name: "Pervasives"}), "print"),
_,
_,
),
},
_,
args,
) =>
switch (List.find_opt(((_, arg)) => exp_is_wasm_unsafe(arg), args)) {
| Some((_, arg)) =>
let typeName = resolve_unsafe_type(arg);
let warning = Grain_utils.Warnings.PrintUnsafe(typeName);
if (Grain_utils.Warnings.is_active(warning)) {
Grain_parsing.Location.prerr_warning(exp_loc, warning);
};
| _ => ()
}
// Check: Warn if using Pervasives toString on WasmXX types
| TExpApp(
{
exp_desc:
TExpIdent(
Path.PExternal(
Path.PIdent({name: "Pervasives"}),
"toString",
),
_,
_,
),
},
_,
args,
) =>
switch (List.find_opt(((_, arg)) => exp_is_wasm_unsafe(arg), args)) {
| Some((_, arg)) =>
let typeName = resolve_unsafe_type(arg);
let warning = Grain_utils.Warnings.ToStringUnsafe(typeName);
if (Grain_utils.Warnings.is_active(warning)) {
Grain_parsing.Location.prerr_warning(exp_loc, warning);
};
| _ => ()
}
// Check: Warn if using Int32.fromNumber(<literal>)
| TExpApp(
{
Expand Down
22 changes: 18 additions & 4 deletions compiler/src/utils/warnings.re
Expand Up @@ -30,9 +30,11 @@ type t =
| FromNumberLiteralU64(string)
| FromNumberLiteralF32(string)
| FromNumberLiteralF64(string)
| UselessRecordSpread;
| UselessRecordSpread
| PrintUnsafe(string)
| ToStringUnsafe(string);

let last_warning_number = 24;
let last_warning_number = 26;

let number =
fun
Expand All @@ -59,7 +61,9 @@ let number =
| FromNumberLiteralU64(_) => 21
| FromNumberLiteralF32(_) => 22
| FromNumberLiteralF64(_) => 23
| UselessRecordSpread => last_warning_number;
| UselessRecordSpread => 24
| PrintUnsafe(_) => 25
| ToStringUnsafe(_) => last_warning_number;

let message =
fun
Expand Down Expand Up @@ -157,7 +161,15 @@ let message =
"it looks like you are calling Float64.fromNumber() with a constant number. Try using the literal syntax (e.g. `%sd`) instead.",
String.contains(n, '.') ? n : n ++ ".",
)
| UselessRecordSpread => "this record spread is useless as all of the record's fields are overridden.";
| UselessRecordSpread => "this record spread is useless as all of the record's fields are overridden."
| PrintUnsafe(typ) =>
"it looks like you are using `print` on an unsafe Wasm value here.\nThis is generally unsafe and will cause errors. Use `DebugPrint.print`"
++ typ
++ " from the `runtime/debugPrint` module instead."
| ToStringUnsafe(typ) =>
"it looks like you are using `toString` on an unsafe Wasm value here.\nThis is generally unsafe and will cause errors. Use `DebugPrint.toString`"
++ typ
++ " from the `runtime/debugPrint` module instead.";

let sub_locs =
fun
Expand Down Expand Up @@ -207,6 +219,8 @@ let defaults = [
FromNumberLiteralF32(""),
FromNumberLiteralF64(""),
UselessRecordSpread,
PrintUnsafe(""),
ToStringUnsafe(""),
];

let _ = List.iter(x => current^.active[number(x)] = true, defaults);
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/utils/warnings.rei
Expand Up @@ -44,7 +44,9 @@ type t =
| FromNumberLiteralU64(string)
| FromNumberLiteralF32(string)
| FromNumberLiteralF64(string)
| UselessRecordSpread;
| UselessRecordSpread
| PrintUnsafe(string)
| ToStringUnsafe(string);

let is_active: t => bool;
let is_error: t => bool;
Expand Down
48 changes: 24 additions & 24 deletions compiler/test/__snapshots__/basic_functionality.1bf5759c.0.snapshot
Expand Up @@ -12,18 +12,18 @@ basic functionality › unsafe_wasm_globals
(import \"_genv\" \"runtimeHeapStart\" (global $runtimeHeapStart_0 i32))
(import \"_genv\" \"runtimeHeapNextPtr\" (global $runtimeHeapNextPtr_0 (mut i32)))
(import \"_genv\" \"metadataPtr\" (global $metadataPtr_0 i32))
(import \"GRAIN$MODULE$unsafeWasmGlobalsExports.gr\" \"GRAIN$EXPORT$_F64_VAL\" (global $_F64_VAL_1147 (mut f64)))
(import \"GRAIN$MODULE$runtime/debugPrint.gr\" \"GRAIN$EXPORT$printF64\" (global $printF64_1146 (mut i32)))
(import \"GRAIN$MODULE$unsafeWasmGlobalsExports.gr\" \"GRAIN$EXPORT$_F32_VAL\" (global $_F32_VAL_1145 (mut f32)))
(import \"GRAIN$MODULE$runtime/debugPrint.gr\" \"GRAIN$EXPORT$printF32\" (global $printF32_1144 (mut i32)))
(import \"GRAIN$MODULE$unsafeWasmGlobalsExports.gr\" \"GRAIN$EXPORT$_I64_VAL\" (global $_I64_VAL_1143 (mut i64)))
(import \"GRAIN$MODULE$runtime/debugPrint.gr\" \"GRAIN$EXPORT$printI64\" (global $printI64_1142 (mut i32)))
(import \"GRAIN$MODULE$unsafeWasmGlobalsExports.gr\" \"GRAIN$EXPORT$_I32_VAL\" (global $_I32_VAL_1141 (mut i32)))
(import \"GRAIN$MODULE$runtime/debugPrint.gr\" \"GRAIN$EXPORT$printI32\" (global $printI32_1140 (mut i32)))
(import \"GRAIN$MODULE$runtime/debugPrint.gr\" \"printF64\" (func $printF64_1146 (param i32 f64) (result i32)))
(import \"GRAIN$MODULE$runtime/debugPrint.gr\" \"printF32\" (func $printF32_1144 (param i32 f32) (result i32)))
(import \"GRAIN$MODULE$runtime/debugPrint.gr\" \"printI64\" (func $printI64_1142 (param i32 i64) (result i32)))
(import \"GRAIN$MODULE$runtime/debugPrint.gr\" \"printI32\" (func $printI32_1140 (param i32 i32) (result i32)))
(import \"GRAIN$MODULE$unsafeWasmGlobalsExports.gr\" \"GRAIN$EXPORT$_F64_VAL\" (global $_F64_VAL_1159 (mut f64)))
(import \"GRAIN$MODULE$runtime/debugPrint.gr\" \"GRAIN$EXPORT$printF64\" (global $printF64_1158 (mut i32)))
(import \"GRAIN$MODULE$unsafeWasmGlobalsExports.gr\" \"GRAIN$EXPORT$_F32_VAL\" (global $_F32_VAL_1157 (mut f32)))
(import \"GRAIN$MODULE$runtime/debugPrint.gr\" \"GRAIN$EXPORT$printF32\" (global $printF32_1156 (mut i32)))
(import \"GRAIN$MODULE$unsafeWasmGlobalsExports.gr\" \"GRAIN$EXPORT$_I64_VAL\" (global $_I64_VAL_1155 (mut i64)))
(import \"GRAIN$MODULE$runtime/debugPrint.gr\" \"GRAIN$EXPORT$printI64\" (global $printI64_1154 (mut i32)))
(import \"GRAIN$MODULE$unsafeWasmGlobalsExports.gr\" \"GRAIN$EXPORT$_I32_VAL\" (global $_I32_VAL_1153 (mut i32)))
(import \"GRAIN$MODULE$runtime/debugPrint.gr\" \"GRAIN$EXPORT$printI32\" (global $printI32_1152 (mut i32)))
(import \"GRAIN$MODULE$runtime/debugPrint.gr\" \"printF64\" (func $printF64_1158 (param i32 f64) (result i32)))
(import \"GRAIN$MODULE$runtime/debugPrint.gr\" \"printF32\" (func $printF32_1156 (param i32 f32) (result i32)))
(import \"GRAIN$MODULE$runtime/debugPrint.gr\" \"printI64\" (func $printI64_1154 (param i32 i64) (result i32)))
(import \"GRAIN$MODULE$runtime/debugPrint.gr\" \"printI32\" (func $printI32_1152 (param i32 i32) (result i32)))
(global $GRAIN$TABLE_SIZE i32 (i32.const 0))
(memory $0 0)
(elem $elem (global.get $relocBase_0))
Expand All @@ -40,26 +40,26 @@ basic functionality › unsafe_wasm_globals
(local $5 f64)
(block $compile_block.1
(drop
(call $printI32_1140
(global.get $printI32_1140)
(global.get $_I32_VAL_1141)
(call $printI32_1152
(global.get $printI32_1152)
(global.get $_I32_VAL_1153)
)
)
(drop
(call $printI64_1142
(global.get $printI64_1142)
(global.get $_I64_VAL_1143)
(call $printI64_1154
(global.get $printI64_1154)
(global.get $_I64_VAL_1155)
)
)
(drop
(call $printF32_1144
(global.get $printF32_1144)
(global.get $_F32_VAL_1145)
(call $printF32_1156
(global.get $printF32_1156)
(global.get $_F32_VAL_1157)
)
)
(return_call $printF64_1146
(global.get $printF64_1146)
(global.get $_F64_VAL_1147)
(return_call $printF64_1158
(global.get $printF64_1158)
(global.get $_F64_VAL_1159)
)
)
)
Expand Down
20 changes: 20 additions & 0 deletions stdlib/runtime/debugPrint.gr
Expand Up @@ -48,3 +48,23 @@ provide let printF32 = val => {
provide let printF64 = val => {
print(Utils.dtoa(val))
}

@unsafe
provide let toStringI32 = val => {
Utils.itoa32(val, 10n)
}

@unsafe
provide let toStringI64 = val => {
Utils.itoa64(val, 10n)
}

@unsafe
provide let toStringF32 = val => {
Utils.dtoa(WasmF64.promoteF32(val))
}

@unsafe
provide let toStringF64 = val => {
Utils.dtoa(val)
}
24 changes: 24 additions & 0 deletions stdlib/runtime/debugPrint.md
Expand Up @@ -36,3 +36,27 @@ printF32 : (val: WasmF32) => Void
printF64 : (val: WasmF64) => Void
```

### DebugPrint.**toStringI32**

```grain
toStringI32 : (val: WasmI32) => String
```

### DebugPrint.**toStringI64**

```grain
toStringI64 : (val: WasmI64) => String
```

### DebugPrint.**toStringF32**

```grain
toStringF32 : (val: WasmF32) => String
```

### DebugPrint.**toStringF64**

```grain
toStringF64 : (val: WasmF64) => String
```

0 comments on commit e64f2ff

Please sign in to comment.