Skip to content

Commit

Permalink
Merge branch 'master' into josh/0.63.6
Browse files Browse the repository at this point in the history
  • Loading branch information
JoshuaBatty authored Sep 18, 2024
2 parents e1533dc + 1305b24 commit d92640e
Show file tree
Hide file tree
Showing 27 changed files with 241 additions and 48 deletions.
23 changes: 23 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Configuration for debugging the Sway Language Server (forc-lsp)
// Usage instructions:
// 1. Ensure you've built forc-lsp with debug symbols:
// cargo build -p forc-lsp
// 2. Install the debug version:
// cargo install --path ./forc-plugins/forc-lsp --debug
// 3. Open your Sway project in a separate VSCode window (this starts forc-lsp)
// 4. In the forc-lsp project window, set breakpoints in the code
// 5. Go to Run and Debug view, select "Attach to forc-lsp", and start debugging
// 6. When prompted, select the forc-lsp process
// 7. Debug forc-lsp as it responds to actions in your Sway project
{
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "attach",
"name": "Attach to forc-lsp",
"pid": "${command:pickProcess}",
"program": "${env:HOME}/.cargo/bin/forc-lsp"
}
]
}
2 changes: 2 additions & 0 deletions sway-core/src/engine_threading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ impl Engines {
self.type_engine.clear_program(program_id);
self.decl_engine.clear_program(program_id);
self.parsed_decl_engine.clear_program(program_id);
self.query_engine.clear_program(program_id);
}

/// Removes all data associated with `source_id` from the declaration and type engines.
Expand All @@ -54,6 +55,7 @@ impl Engines {
self.type_engine.clear_module(source_id);
self.decl_engine.clear_module(source_id);
self.parsed_decl_engine.clear_module(source_id);
self.query_engine.clear_module(source_id);
}

/// Helps out some `thing: T` by adding `self` as context.
Expand Down
35 changes: 30 additions & 5 deletions sway-core/src/query_engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{
time::SystemTime,
};
use sway_error::{error::CompileError, warning::CompileWarning};
use sway_types::IdentUnique;
use sway_types::{IdentUnique, ProgramId, SourceId, Spanned};

use crate::{
decl_engine::{DeclId, DeclRef},
Expand Down Expand Up @@ -141,17 +141,18 @@ pub struct FunctionCacheEntry {
#[derive(Debug, Default)]
pub struct QueryEngine {
// We want the below types wrapped in Arcs to optimize cloning from LSP.
programs_cache: Arc<RwLock<ProgramsCacheMap>>,
function_cache: Arc<RwLock<FunctionsCacheMap>>,
programs_cache: CowCache<ProgramsCacheMap>,
pub module_cache: CowCache<ModuleCacheMap>,
// NOTE: Any further AstNodes that are cached need to have garbage collection applied, see clear_module()
function_cache: CowCache<FunctionsCacheMap>,
}

impl Clone for QueryEngine {
fn clone(&self) -> Self {
Self {
programs_cache: self.programs_cache.clone(),
function_cache: self.function_cache.clone(),
programs_cache: CowCache::new(self.programs_cache.read().clone()),
module_cache: CowCache::new(self.module_cache.read().clone()),
function_cache: CowCache::new(self.function_cache.read().clone()),
}
}
}
Expand Down Expand Up @@ -205,6 +206,30 @@ impl QueryEngine {
FunctionCacheEntry { fn_decl },
);
}

/// Removes all data associated with the `source_id` from the function cache.
pub fn clear_module(&mut self, source_id: &SourceId) {
self.function_cache
.write()
.retain(|(ident, _), _| ident.span().source_id().map_or(true, |id| id != source_id));
}

/// Removes all data associated with the `program_id` from the function cache.
pub fn clear_program(&mut self, program_id: &ProgramId) {
self.function_cache.write().retain(|(ident, _), _| {
ident
.span()
.source_id()
.map_or(true, |id| id.program_id() != *program_id)
});
}

/// Commits all changes to their respective caches.
pub fn commit(&self) {
self.programs_cache.commit();
self.module_cache.commit();
self.function_cache.commit();
}
}

/// Thread-safe, copy-on-write cache optimized for LSP operations.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1629,8 +1629,7 @@ impl TypeCheckAnalysis for ty::ImplSelfOrTrait {
ctx.push_nodes_for_impl_trait(self);

// Now lets analyze each impl trait item.
for (i, item) in impl_trait.items.iter().enumerate() {
let _node = ctx.items_node_stack[i];
for item in impl_trait.items.iter() {
item.type_check_analyze(handler, ctx)?;
}

Expand Down
6 changes: 6 additions & 0 deletions sway-core/src/semantic_analysis/namespace/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,12 @@ impl Root {
ty::TyDecl::EnumDecl(enum_ty_decl) => TypeInfo::Enum(enum_ty_decl.decl_id),
ty::TyDecl::TraitTypeDecl(type_decl) => {
let type_decl = engines.de().get_type(&type_decl.decl_id);
if type_decl.ty.is_none() {
return Err(handler.emit_err(CompileError::Internal(
"Trait type declaration has no type",
symbol.span(),
)));
}
(*engines.te().get(type_decl.ty.clone().unwrap().type_id)).clone()
}
_ => {
Expand Down
26 changes: 17 additions & 9 deletions sway-core/src/semantic_analysis/type_check_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1700,27 +1700,35 @@ impl<'a> TypeCheckContext<'a> {
0,
)))
}
(num_type_params, num_type_args) => {
let type_arguments_span = type_arguments
.iter()
.map(|x| x.span.clone())
.reduce(|s1: Span, s2: Span| Span::join(s1, &s2))
.unwrap_or_else(|| value.name().span());
(_, num_type_args) => {
// a trait decl is passed the self type parameter and the corresponding argument
// but it would be confusing for the user if the error reporting mechanism
// reported the number of arguments including the implicit self, hence
// we adjust it below
let adjust_for_trait_decl = value.has_self_type_param() as usize;
let num_type_params = num_type_params - adjust_for_trait_decl;
let non_parent_type_params = value
.type_parameters()
.iter()
.filter(|x| !x.is_from_parent)
.count()
- adjust_for_trait_decl;

let num_type_args = num_type_args - adjust_for_trait_decl;
if num_type_params != num_type_args {
if non_parent_type_params != num_type_args {
let type_arguments_span = type_arguments
.iter()
.map(|x| x.span.clone())
.reduce(|s1: Span, s2: Span| Span::join(s1, &s2))
.unwrap_or_else(|| value.name().span());

return Err(handler.emit_err(make_type_arity_mismatch_error(
value.name().clone(),
type_arguments_span,
num_type_args,
num_type_params,
non_parent_type_params,
)));
}

for type_argument in type_arguments.iter_mut() {
type_argument.type_id = self
.resolve(
Expand Down
9 changes: 7 additions & 2 deletions sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4943,8 +4943,13 @@ pub fn cfg_eval(
}
},
_ => {
// Already checked with `AttributeKind::expected_args_*`
unreachable!("cfg attribute should only have the `target` or the `program_type` argument");
return Err(handler.emit_err(
ConvertParseTreeError::InvalidCfgArg {
span: arg.span(),
value: arg.name.as_str().to_string(),
}
.into(),
));
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions sway-error/src/convert_parse_tree_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ pub enum ConvertParseTreeError {
ExpectedCfgProgramTypeArgValue { span: Span },
#[error("Expected \"true\" or \"false\" for experimental_new_encoding")]
ExpectedExperimentalNewEncodingArgValue { span: Span },
#[error("Unexpected attribute value: \"{value}\" for attribute: \"cfg\"")]
InvalidCfgArg { span: Span, value: String },
}

impl Spanned for ConvertParseTreeError {
Expand Down Expand Up @@ -182,6 +184,7 @@ impl Spanned for ConvertParseTreeError {
ConvertParseTreeError::InvalidCfgProgramTypeArgValue { span, .. } => span.clone(),
ConvertParseTreeError::ExpectedCfgProgramTypeArgValue { span } => span.clone(),
ConvertParseTreeError::ExpectedExperimentalNewEncodingArgValue { span } => span.clone(),
ConvertParseTreeError::InvalidCfgArg { span, .. } => span.clone(),
}
}
}
8 changes: 1 addition & 7 deletions sway-lsp/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,11 @@ impl Default for DiagnosticConfig {
#[serde(rename_all = "camelCase")]
pub struct GarbageCollectionConfig {
pub gc_enabled: bool,
pub gc_frequency: i32,
}

impl Default for GarbageCollectionConfig {
fn default() -> Self {
Self {
gc_enabled: true,
// Garbage collection is fairly expsensive so we default to only clear on every 3rd keystroke.
// Waiting too long to clear can cause a stack overflow to occur.
gc_frequency: 3,
}
Self { gc_enabled: true }
}
}

Expand Down
28 changes: 12 additions & 16 deletions sway-lsp/src/server_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,21 +137,17 @@ impl ServerState {
let session = ctx.session.as_ref().unwrap().clone();
let mut engines_clone = session.engines.read().clone();

if let Some(version) = ctx.version {
// Perform garbage collection at configured intervals if enabled to manage memory usage.
if ctx.gc_options.gc_enabled
&& version % ctx.gc_options.gc_frequency == 0
// Perform garbage collection if enabled to manage memory usage.
if ctx.gc_options.gc_enabled {
// Call this on the engines clone so we don't clear types that are still in use
// and might be needed in the case cancel compilation was triggered.
if let Err(err) =
session.garbage_collect_module(&mut engines_clone, &uri)
{
// Call this on the engines clone so we don't clear types that are still in use
// and might be needed in the case cancel compilation was triggered.
if let Err(err) =
session.garbage_collect_module(&mut engines_clone, &uri)
{
tracing::error!(
"Unable to perform garbage collection: {}",
err.to_string()
);
}
tracing::error!(
"Unable to perform garbage collection: {}",
err.to_string()
);
}
}

Expand Down Expand Up @@ -180,10 +176,10 @@ impl ServerState {
// Because the engines_clone has garbage collection applied. If the workspace AST was reused, we need to keep the old engines
// as the engines_clone might have cleared some types that are still in use.
if metrics.reused_programs == 0 {
// Commit local changes in the module cache to the shared state.
// Commit local changes in the programs, module, and function caches to the shared state.
// This ensures that any modifications made during compilation are preserved
// before we swap the engines.
engines_clone.qe().module_cache.commit();
engines_clone.qe().commit();
// The compiler did not reuse the workspace AST.
// We need to overwrite the old engines with the engines clone.
mem::swap(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
out
target
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "minimal_script"
implicit-std = false

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
script;

struct MyStruct {
field1: u16,
}

fn func(s: MyStruct) -> u16 {
s.field1
}

fn main() {
let x = MyStruct { field1: 10 };
let y = func(x);
}
15 changes: 8 additions & 7 deletions sway-lsp/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,13 +261,6 @@ fn garbage_collection_runner(path: PathBuf) {
run_async!({
setup_panic_hook();
let (mut service, _) = LspService::new(ServerState::new);
// set the garbage collection frequency to 1
service
.inner()
.config
.write()
.garbage_collection
.gc_frequency = 1;
let uri = init_and_open(&mut service, path).await;
let times = 60;

Expand Down Expand Up @@ -302,6 +295,14 @@ fn garbage_collection_paths() {
garbage_collection_runner(p);
}

#[test]
fn garbage_collection_minimal_script() {
let p = sway_workspace_dir()
.join("sway-lsp/tests/fixtures/garbage_collection/minimal_script")
.join("src/main.sw");
garbage_collection_runner(p);
}

#[test]
fn lsp_syncs_with_workspace_edits() {
run_async!({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[[package]]
name = "invalid_cfg_arg"
source = "member"
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
name = "invalid_cfg_arg"
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
implicit-std = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
predicate;
#[cfg(c)] a
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
category = "fail"

# check: $()#[cfg(c)] a
# nextln: $()Unexpected attribute value: "c" for attribute: "cfg" expected value "target" or "program_type" or "experimental_new_encoding"

# check: $()#[cfg(c)] a
# nextln: $()Expected an item.

# check: $()#[cfg(c)] a
# nextln: $()Unexpected attribute value: "c" for attribute: "cfg"
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[[package]]
name = "core"
source = "path+from-root-F62FA9D54ABC8F01"

[[package]]
name = "mismatch_closing_delimiters"
source = "member"
dependencies = ["core"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
license = "Apache-2.0"
name = "mismatch_closing_delimiters"
entry = "main.sw"
implicit-std = false

[dependencies]
core = { path = "../../../../../../sway-lib-core" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
script;

struct Struct{x:u}
impl Struct{
fn w()->f{{(()}}
trait Supertrait{}
impl Supertrait for Struct{)
}

fn s<A>(b:B) where A:t{}fn n(){}

fn main() -> u64 {
0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
category = "fail"

# check: $()fn w()->f{{(()}}
# nextln: $()mismatched delimiters
Loading

0 comments on commit d92640e

Please sign in to comment.