Repository: rust-lang/rust-analyzer
Pull request #15268 (2023-07-11 21:45:32)

Add write_bytes and ctlz intrinsics


Patch:
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index a1bab09795..47c9354768 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -1627,24 +1627,26 @@ impl Evaluator<'_> {
             .ok_or_else(|| MirEvalError::UndefinedBehavior("out of bound memory read".to_string()))
     }
 
-    fn write_memory(&mut self, addr: Address, r: &[u8]) -> Result<()> {
-        if r.is_empty() {
-            return Ok(());
-        }
+    fn write_memory_using_ref(&mut self, addr: Address, size: usize) -> Result<&mut [u8]> {
         let (mem, pos) = match addr {
             Stack(it) => (&mut self.stack, it),
             Heap(it) => (&mut self.heap, it),
             Invalid(it) => {
                 return Err(MirEvalError::UndefinedBehavior(format!(
-                    "write invalid memory address {it} with content {r:?}"
+                    "write invalid memory address {it} with size {size}"
                 )));
             }
         };
-        mem.get_mut(pos..pos + r.len())
-            .ok_or_else(|| {
-                MirEvalError::UndefinedBehavior("out of bound memory write".to_string())
-            })?
-            .copy_from_slice(r);
+        Ok(mem.get_mut(pos..pos + size).ok_or_else(|| {
+            MirEvalError::UndefinedBehavior("out of bound memory write".to_string())
+        })?)
+    }
+
+    fn write_memory(&mut self, addr: Address, r: &[u8]) -> Result<()> {
+        if r.is_empty() {
+            return Ok(());
+        }
+        self.write_memory_using_ref(addr, r.len())?.copy_from_slice(r);
         Ok(())
     }
 
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index 3b8b720122..cedc453803 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -800,15 +800,25 @@ impl Evaluator<'_> {
             }
             "ctpop" => {
                 let [arg] = args else {
-                    return Err(MirEvalError::TypeError("likely arg is not provided"));
+                    return Err(MirEvalError::TypeError("ctpop arg is not provided"));
                 };
                 let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).count_ones();
                 destination
                     .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size])
             }
+            "ctlz" | "ctlz_nonzero" => {
+                let [arg] = args else {
+                    return Err(MirEvalError::TypeError("cttz arg is not provided"));
+                };
+                let result =
+                    u128::from_le_bytes(pad16(arg.get(self)?, false)).leading_zeros() as usize;
+                let result = result - (128 - arg.interval.size * 8);
+                destination
+                    .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size])
+            }
             "cttz" | "cttz_nonzero" => {
                 let [arg] = args else {
-                    return Err(MirEvalError::TypeError("likely arg is not provided"));
+                    return Err(MirEvalError::TypeError("cttz arg is not provided"));
                 };
                 let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).trailing_zeros();
                 destination
@@ -932,6 +942,24 @@ impl Evaluator<'_> {
                 let addr = Address::from_bytes(arg.interval.get(self)?)?;
                 destination.write_from_interval(self, Interval { addr, size: destination.size })
             }
+            "write_bytes" => {
+                let [dst, val, count] = args else {
+                    return Err(MirEvalError::TypeError("write_bytes args are not provided"));
+                };
+                let count = from_bytes!(usize, count.get(self)?);
+                let val = from_bytes!(u8, val.get(self)?);
+                let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+                else {
+                    return Err(MirEvalError::TypeError(
+                        "write_bytes generic arg is not provided",
+                    ));
+                };
+                let dst = Address::from_bytes(dst.get(self)?)?;
+                let size = self.size_of_sized(ty, locals, "copy_nonoverlapping ptr type")?;
+                let size = count * size;
+                self.write_memory_using_ref(dst, size)?.fill(val);
+                Ok(())
+            }
             _ => not_supported!("unknown intrinsic {name}"),
         }
     }
diff --git a/crates/hir-ty/src/mir/eval/shim/simd.rs b/crates/hir-ty/src/mir/eval/shim/simd.rs
index bc9529d38f..ec74631048 100644
--- a/crates/hir-ty/src/mir/eval/shim/simd.rs
+++ b/crates/hir-ty/src/mir/eval/shim/simd.rs
@@ -44,7 +44,12 @@ impl Evaluator<'_> {
                     }
                 };
                 match try_const_usize(self.db, len) {
-                    Some(_) => not_supported!("array like simd type"),
+                    Some(len) => {
+                        let Some(ty) = subst.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else {
+                            return Err(MirEvalError::TypeError("simd type with no ty param"));
+                        };
+                        Ok((len as usize, ty.clone()))
+                    }
                     None => Err(MirEvalError::TypeError("simd type with unevaluatable len param")),
                 }
             }
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index d8fa8cf01a..3610858790 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -1880,6 +1880,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
 
 fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
     Ok(match (source_ty.kind(Interner), target_ty.kind(Interner)) {
+        (TyKind::FnDef(..), TyKind::Function(_)) => CastKind::Pointer(PointerCast::ReifyFnPointer),
         (TyKind::Scalar(s), TyKind::Scalar(t)) => match (s, t) {
             (chalk_ir::Scalar::Float(_), chalk_ir::Scalar::Float(_)) => CastKind::FloatToFloat,
             (chalk_ir::Scalar::Float(_), _) => CastKind::FloatToInt,
diff --git a/editors/code/.eslintrc.js b/editors/code/.eslintrc.js
index 69f02b4ab5..297e9fa1e5 100644
--- a/editors/code/.eslintrc.js
+++ b/editors/code/.eslintrc.js
@@ -33,14 +33,5 @@ module.exports = {
         "@typescript-eslint/semi": ["error", "always"],
         "@typescript-eslint/no-unnecessary-type-assertion": "error",
         "@typescript-eslint/no-floating-promises": "error",
-
-        "@typescript-eslint/consistent-type-imports": [
-            "error",
-            {
-                prefer: "type-imports",
-                fixStyle: "inline-type-imports",
-            },
-        ],
-        "@typescript-eslint/no-import-type-side-effects": "error",
     },
 };
diff --git a/editors/code/src/ast_inspector.ts b/editors/code/src/ast_inspector.ts
index 2ebfd564ca..fa963d8eb9 100644
--- a/editors/code/src/ast_inspector.ts
+++ b/editors/code/src/ast_inspector.ts
@@ -1,7 +1,7 @@
 import * as vscode from "vscode";
 
-import type { Ctx, Disposable } from "./ctx";
-import { type RustEditor, isRustEditor } from "./util";
+import { Ctx, Disposable } from "./ctx";
+import { RustEditor, isRustEditor } from "./util";
 import { unwrapUndefinable } from "./undefinable";
 
 // FIXME: consider implementing this via the Tree View API?
diff --git a/editors/code/src/bootstrap.ts b/editors/code/src/bootstrap.ts
index 85668a23f3..7b831a8a69 100644
--- a/editors/code/src/bootstrap.ts
+++ b/editors/code/src/bootstrap.ts
@@ -1,8 +1,8 @@
 import * as vscode from "vscode";
 import * as os from "os";
-import type { Config } from "./config";
+import { Config } from "./config";
 import { log, isValidExecutable } from "./util";
-import type { PersistentState } from "./persistent_state";
+import { PersistentState } from "./persistent_state";
 import { exec } from "child_process";
 
 export async function bootstrap(
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 35a114fb04..7b151c804a 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -6,7 +6,7 @@ import * as Is from "vscode-languageclient/lib/common/utils/is";
 import { assert } from "./util";
 import * as diagnostics from "./diagnostics";
 import { WorkspaceEdit } from "vscode";
-import { type Config, prepareVSCodeConfig } from "./config";
+import { Config, prepareVSCodeConfig } from "./config";
 import { randomUUID } from "crypto";
 import { sep as pathSeparator } from "path";
 import { unwrapUndefinable } from "./undefinable";
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 1289fa2ac7..94ec5e99c6 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -3,23 +3,23 @@ import * as lc from "vscode-languageclient";
 import * as ra from "./lsp_ext";
 import * as path from "path";
 
-import { type Ctx, type Cmd, type CtxInit, discoverWorkspace } from "./ctx";
+import { Ctx, Cmd, CtxInit, discoverWorkspace } from "./ctx";
 import { applySnippetWorkspaceEdit, applySnippetTextEdits } from "./snippets";
 import { spawnSync } from "child_process";
-import { type RunnableQuickPick, selectRunnable, createTask, createArgs } from "./run";
+import { RunnableQuickPick, selectRunnable, createTask, createArgs } from "./run";
 import { AstInspector } from "./ast_inspector";
 import {
     isRustDocument,
     isCargoTomlDocument,
     sleep,
     isRustEditor,
-    type RustEditor,
-    type RustDocument,
+    RustEditor,
+    RustDocument,
 } from "./util";
 import { startDebugSession, makeDebugConfig } from "./debug";
-import type { LanguageClient } from "vscode-languageclient/node";
+import { LanguageClient } from "vscode-languageclient/node";
 import { LINKED_COMMANDS } from "./client";
-import type { DependencyId } from "./dependencies_provider";
+import { DependencyId } from "./dependencies_provider";
 import { unwrapUndefinable } from "./undefinable";
 
 export * from "./ast_inspector";
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 7dbc4bdd47..7f4efebb2a 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -2,7 +2,7 @@ import * as Is from "vscode-languageclient/lib/common/utils/is";
 import * as os from "os";
 import * as path from "path";
 import * as vscode from "vscode";
-import type { Env } from "./client";
+import { Env } from "./client";
 import { log } from "./util";
 import { expectNotUndefined, unwrapUndefinable } from "./undefinable";
 
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index 1ae0c96c6e..a72b5391ff 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -1,5 +1,5 @@
 import * as vscode from "vscode";
-import type * as lc from "vscode-languageclient/node";
+import * as lc from "vscode-languageclient/node";
 import * as ra from "./lsp_ext";
 import * as path from "path";
 
@@ -12,19 +12,19 @@ import {
     isRustEditor,
     LazyOutputChannel,
     log,
-    type RustEditor,
+    RustEditor,
 } from "./util";
-import type { ServerStatusParams } from "./lsp_ext";
+import { ServerStatusParams } from "./lsp_ext";
 import {
-    type Dependency,
-    type DependencyFile,
+    Dependency,
+    DependencyFile,
     RustDependenciesProvider,
-    type DependencyId,
+    DependencyId,
 } from "./dependencies_provider";
 import { execRevealDependency } from "./commands";
 import { PersistentState } from "./persistent_state";
 import { bootstrap } from "./bootstrap";
-import type { ExecOptions } from "child_process";
+import { ExecOptions } from "child_process";
 
 // We only support local folders, not eg. Live Share (`vlsl:` scheme), so don't activate if
 // only those are in use. We use "Empty" to represent these scenarios
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts
index 484f547230..29758feafe 100644
--- a/editors/code/src/debug.ts
+++ b/editors/code/src/debug.ts
@@ -1,10 +1,10 @@
 import * as os from "os";
 import * as vscode from "vscode";
 import * as path from "path";
-import type * as ra from "./lsp_ext";
+import * as ra from "./lsp_ext";
 
 import { Cargo, getRustcId, getSysroot } from "./toolchain";
-import type { Ctx } from "./ctx";
+import { Ctx } from "./ctx";
 import { prepareEnv } from "./run";
 import { unwrapUndefinable } from "./undefinable";
 
diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts
index 98d6d7001a..51ba11ecc9 100644
--- a/editors/code/src/dependencies_provider.ts
+++ b/editors/code/src/dependencies_provider.ts
@@ -1,9 +1,9 @@
 import * as vscode from "vscode";
 import * as fspath from "path";
 import * as fs from "fs";
-import type { CtxInit } from "./ctx";
+import { CtxInit } from "./ctx";
 import * as ra from "./lsp_ext";
-import type { FetchDependencyListResult } from "./lsp_ext";
+import { FetchDependencyListResult } from "./lsp_ext";
 import { unwrapUndefinable } from "./undefinable";
 
 export class RustDependenciesProvider
diff --git a/editors/code/src/diagnostics.ts b/editors/code/src/diagnostics.ts
index e573f5a63f..a7e0845a27 100644
--- a/editors/code/src/diagnostics.ts
+++ b/editors/code/src/diagnostics.ts
@@ -1,13 +1,7 @@
 import * as anser from "anser";
 import * as vscode from "vscode";
-import {
-    type ProviderResult,
-    Range,
-    type TextEditorDecorationType,
-    ThemeColor,
-    window,
-} from "vscode";
-import type { Ctx } from "./ctx";
+import { ProviderResult, Range, TextEditorDecorationType, ThemeColor, window } from "vscode";
+import { Ctx } from "./ctx";
 import { unwrapUndefinable } from "./undefinable";
 
 export const URI_SCHEME = "rust-analyzer-diagnostics-view";
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 1121951c07..64aea03394 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -2,7 +2,7 @@ import * as vscode from "vscode";
 import * as lc from "vscode-languageclient/node";
 
 import * as commands from "./commands";
-import { type CommandFactory, Ctx, fetchWorkspace } from "./ctx";
+import { CommandFactory, Ctx, fetchWorkspace } from "./ctx";
 import * as diagnostics from "./diagnostics";
 import { activateTaskProvider } from "./tasks";
 import { setContextValue } from "./util";
diff --git a/editors/code/src/persistent_state.ts b/editors/code/src/persistent_state.ts
index cebd16a3c9..8964a78dc3 100644
--- a/editors/code/src/persistent_state.ts
+++ b/editors/code/src/persistent_state.ts
@@ -1,4 +1,4 @@
-import type * as vscode from "vscode";
+import * as vscode from "vscode";
 import { log } from "./util";
 
 export class PersistentState {
diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts
index 8ec569b603..c20a5487b7 100644
--- a/editors/code/src/run.ts
+++ b/editors/code/src/run.ts
@@ -1,11 +1,11 @@
 import * as vscode from "vscode";
-import type * as lc from "vscode-languageclient";
+import * as lc from "vscode-languageclient";
 import * as ra from "./lsp_ext";
 import * as tasks from "./tasks";
 
-import type { CtxInit } from "./ctx";
+import { CtxInit } from "./ctx";
 import { makeDebugConfig } from "./debug";
-import type { Config, RunnableEnvCfg } from "./config";
+import { Config, RunnableEnvCfg } from "./config";
 import { unwrapUndefinable } from "./undefinable";
 
 const quickPickButtons = [
diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts
index f451e738d5..5199508c82 100644
--- a/editors/code/src/tasks.ts
+++ b/editors/code/src/tasks.ts
@@ -1,6 +1,6 @@
 import * as vscode from "vscode";
 import * as toolchain from "./toolchain";
-import type { Config } from "./config";
+import { Config } from "./config";
 import { log } from "./util";
 import { unwrapUndefinable } from "./undefinable";
 
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts
index 64f4582770..b6240234aa 100644
--- a/editors/code/src/util.ts
+++ b/editors/code/src/util.ts
@@ -1,6 +1,6 @@
 import * as vscode from "vscode";
 import { strict as nativeAssert } from "assert";
-import { exec, type ExecOptions, spawnSync } from "child_process";
+import { exec, ExecOptions, spawnSync } from "child_process";
 import { inspect } from "util";
 
 export function assert(condition: boolean, explanation: string): asserts condition {
diff --git a/lib/lsp-server/Cargo.toml b/lib/lsp-server/Cargo.toml
index 01707d3019..d44b2d4e40 100644
--- a/lib/lsp-server/Cargo.toml
+++ b/lib/lsp-server/Cargo.toml
@@ -9,7 +9,7 @@ edition = "2021"
 [dependencies]
 log = "0.4.17"
 serde_json = "1.0.96"
-serde = { version = "1.0.156", features = ["derive"] }
+serde = "1.0.156"
 crossbeam-channel = "0.5.6"
 
 [dev-dependencies]
diff --git a/lib/lsp-server/src/msg.rs b/lib/lsp-server/src/msg.rs
index 730ad51f42..b241561f9c 100644
--- a/lib/lsp-server/src/msg.rs
+++ b/lib/lsp-server/src/msg.rs
@@ -265,7 +265,7 @@ fn read_msg_text(inp: &mut dyn BufRead) -> io::Result<Option<String>> {
         let header_name = parts.next().unwrap();
         let header_value =
             parts.next().ok_or_else(|| invalid_data!("malformed header: {:?}", buf))?;
-        if header_name.eq_ignore_ascii_case("Content-Length") {
+        if header_name == "Content-Length" {
             size = Some(header_value.parse::<usize>().map_err(invalid_data)?);
         }
     }


Test patch:
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index 8e21272e14..4ca1d8a239 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -1724,6 +1724,18 @@ fn function_pointer() {
         "#,
         5,
     );
+    check_number(
+        r#"
+    fn add2(x: u8) -> u8 {
+        x + 2
+    }
+    const GOAL: u8 = {
+        let plus2 = add2 as fn(u8) -> u8;
+        plus2(3)
+    };
+        "#,
+        5,
+    );
     check_number(
         r#"
     //- minicore: coerce_unsized, index, slice
diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs
index cb8f1b5557..30e87661ab 100644
--- a/crates/hir-ty/src/consteval/tests/intrinsics.rs
+++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs
@@ -510,6 +510,24 @@ fn copy_nonoverlapping() {
     );
 }
 
+#[test]
+fn write_bytes() {
+    check_number(
+        r#"
+        extern "rust-intrinsic" {
+            fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
+        }
+
+        const GOAL: i32 = unsafe {
+            let mut x = 2;
+            write_bytes(&mut x, 5, 1);
+            x
+        };
+        "#,
+        0x05050505,
+    );
+}
+
 #[test]
 fn copy() {
     check_number(
@@ -545,6 +563,20 @@ fn ctpop() {
     );
 }
 
+#[test]
+fn ctlz() {
+    check_number(
+        r#"
+        extern "rust-intrinsic" {
+            pub fn ctlz<T: Copy>(x: T) -> T;
+        }
+
+        const GOAL: u8 = ctlz(0b0001_1100_u8);
+        "#,
+        3,
+    );
+}
+
 #[test]
 fn cttz() {
     check_number(
diff --git a/editors/code/tests/unit/launch_config.test.ts b/editors/code/tests/unit/launch_config.test.ts
index eeb8608a42..0531e064d2 100644
--- a/editors/code/tests/unit/launch_config.test.ts
+++ b/editors/code/tests/unit/launch_config.test.ts
@@ -1,6 +1,6 @@
 import * as assert from "assert";
 import { Cargo } from "../../src/toolchain";
-import type { Context } from ".";
+import { Context } from ".";
 
 export async function getTests(ctx: Context) {
     await ctx.suite("Launch configuration/Lens", (suite) => {
diff --git a/editors/code/tests/unit/runnable_env.test.ts b/editors/code/tests/unit/runnable_env.test.ts
index b1407ce019..b7d59e399d 100644
--- a/editors/code/tests/unit/runnable_env.test.ts
+++ b/editors/code/tests/unit/runnable_env.test.ts
@@ -1,8 +1,8 @@
 import * as assert from "assert";
 import { prepareEnv } from "../../src/run";
-import type { RunnableEnvCfg } from "../../src/config";
-import type { Context } from ".";
-import type * as ra from "../../src/lsp_ext";
+import { RunnableEnvCfg } from "../../src/config";
+import { Context } from ".";
+import * as ra from "../../src/lsp_ext";
 
 function makeRunnable(label: string): ra.Runnable {
     return {
diff --git a/editors/code/tests/unit/settings.test.ts b/editors/code/tests/unit/settings.test.ts
index bafb9569a1..f171f5f74f 100644
--- a/editors/code/tests/unit/settings.test.ts
+++ b/editors/code/tests/unit/settings.test.ts
@@ -1,5 +1,5 @@
 import * as assert from "assert";
-import type { Context } from ".";
+import { Context } from ".";
 import { substituteVariablesInEnv } from "../../src/config";
 
 export async function getTests(ctx: Context) {