Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions sway-ast/src/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ pub const ERROR_TYPE_ATTRIBUTE_NAME: &str = "error_type";
pub const ERROR_ATTRIBUTE_NAME: &str = "error";
pub const ERROR_M_ARG_NAME: &str = "m";

// Backtracing.
pub const TRACE_ATTRIBUTE_NAME: &str = "trace";
pub const TRACE_NEVER_ARG_NAME: &str = "never";
pub const TRACE_ALWAYS_ARG_NAME: &str = "always";

pub const KNOWN_ATTRIBUTE_NAMES: &[&str] = &[
STORAGE_ATTRIBUTE_NAME,
DOC_COMMENT_ATTRIBUTE_NAME,
Expand Down
9 changes: 7 additions & 2 deletions sway-core/src/ir_generation/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,8 @@ fn compile_fn(
let type_engine = engines.te();
let decl_engine = engines.de();

let inline_opt = ast_fn_decl.inline();
let inline = ast_fn_decl.inline();
let trace = ast_fn_decl.trace();
let ty::TyFunctionDecl {
name,
body,
Expand Down Expand Up @@ -661,10 +662,14 @@ fn compile_fn(
let test_decl_index_md_idx = md_mgr.test_decl_index_to_md(context, decl_index);
metadata = md_combine(context, &metadata, &test_decl_index_md_idx);
}
if let Some(inline) = inline_opt {
if let Some(inline) = inline {
let inline_md_idx = md_mgr.inline_to_md(context, inline);
metadata = md_combine(context, &metadata, &inline_md_idx);
}
if let Some(trace) = trace {
let trace_md_idx = md_mgr.trace_to_md(context, trace);
metadata = md_combine(context, &metadata, &trace_md_idx);
}

let func = Function::new(
context,
Expand Down
2 changes: 2 additions & 0 deletions sway-core/src/language/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod module;
pub mod parsed;
pub mod programs;
mod purity;
mod trace;
pub mod ty;
mod visibility;

Expand All @@ -19,4 +20,5 @@ pub use literal::*;
pub use module::*;
pub use programs::*;
pub use purity::*;
pub use trace::*;
pub use visibility::*;
6 changes: 6 additions & 0 deletions sway-core/src/language/trace.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/// The trace of a function suggests to the compiler whether or not a function should be backtraced.
#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
pub enum Trace {
Always,
Never,
}
6 changes: 5 additions & 1 deletion sway-core/src/language/ty/declaration/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
language::{
parsed::{self, FunctionDeclaration, FunctionDeclarationKind},
ty::*,
CallPath, Inline, Purity, Visibility,
CallPath, Inline, Purity, Trace, Visibility,
},
semantic_analysis::TypeCheckContext,
transform::{self, AttributeKind},
Expand Down Expand Up @@ -582,6 +582,10 @@ impl TyFunctionDecl {
self.attributes.inline()
}

pub fn trace(&self) -> Option<Trace> {
self.attributes.trace()
}

pub fn is_fallback(&self) -> bool {
self.attributes.has_any_of_kind(AttributeKind::Fallback)
}
Expand Down
60 changes: 57 additions & 3 deletions sway-core/src/metadata.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
decl_engine::DeclId,
language::{ty::TyFunctionDecl, CallPath, Inline, Purity},
language::{ty::TyFunctionDecl, CallPath, Inline, Purity, Trace},
};

use sway_ir::{Context, MetadataIndex, Metadatum, Value};
Expand All @@ -21,7 +21,7 @@ pub(crate) struct MetadataManager {
// We want to be able to store more then one `Span` per `MetadataIndex`.
// E.g., storing the span of the function name, and the whole function declaration.
// The spans differ then by the tag property of their `Metadatum::Struct`.
// We could cache all such spans in a single `HashMap` where the key would be (Span, tag).
// We could cache all such spans in a single `HashMap` where the key would be `(Span, tag)`.
// But since the vast majority of stored spans will be tagged with the generic "span" tag,
// and only a few elements will have additional spans in their `MetadataIndex`, it is
// more efficient to provide two separate caches, one for the spans tagged with "span",
Expand All @@ -33,13 +33,15 @@ pub(crate) struct MetadataManager {
md_file_loc_cache: HashMap<MetadataIndex, (Arc<PathBuf>, Source)>,
md_purity_cache: HashMap<MetadataIndex, Purity>,
md_inline_cache: HashMap<MetadataIndex, Inline>,
md_trace_cache: HashMap<MetadataIndex, Trace>,
md_test_decl_index_cache: HashMap<MetadataIndex, DeclId<TyFunctionDecl>>,

span_md_cache: HashMap<Span, MetadataIndex>,
tagged_span_md_cache: HashMap<(Span, &'static str), MetadataIndex>,
file_loc_md_cache: HashMap<SourceId, MetadataIndex>,
purity_md_cache: HashMap<Purity, MetadataIndex>,
inline_md_cache: HashMap<Inline, MetadataIndex>,
trace_md_cache: HashMap<Trace, MetadataIndex>,
test_decl_index_md_cache: HashMap<DeclId<TyFunctionDecl>, MetadataIndex>,
}

Expand Down Expand Up @@ -206,6 +208,36 @@ impl MetadataManager {
})
}

// TODO: (BACKTRACING) Remove `#[allow(dead_code)]` once the backtracing is
// implemented in the IR compilation.
#[allow(dead_code)]
pub(crate) fn md_to_trace(
&mut self,
context: &Context,
md_idx: Option<MetadataIndex>,
) -> Option<Trace> {
Self::for_each_md_idx(context, md_idx, |md_idx| {
self.md_trace_cache.get(&md_idx).copied().or_else(|| {
// Create a new trace and save it in the cache.
md_idx
.get_content(context)
.unwrap_struct("trace", 1)
.and_then(|fields| fields[0].unwrap_string())
.and_then(|trace_str| {
let trace = match trace_str {
"always" => Some(Trace::Always),
"never" => Some(Trace::Never),
_otherwise => None,
}?;

self.md_trace_cache.insert(md_idx, trace);

Some(trace)
})
})
})
}

fn md_to_file_location(
&mut self,
context: &Context,
Expand Down Expand Up @@ -356,7 +388,6 @@ impl MetadataManager {
})
}

/// Inserts Inline information into metadata.
pub(crate) fn inline_to_md(
&mut self,
context: &mut Context,
Expand Down Expand Up @@ -385,6 +416,29 @@ impl MetadataManager {
)
}

pub(crate) fn trace_to_md(
&mut self,
context: &mut Context,
trace: Trace,
) -> Option<MetadataIndex> {
Some(self.trace_md_cache.get(&trace).copied().unwrap_or_else(|| {
// Create new metadatum.
let field = match trace {
Trace::Always => "always",
Trace::Never => "never",
};
let md_idx = MetadataIndex::new_struct(
context,
"trace",
vec![Metadatum::String(field.to_owned())],
);

self.trace_md_cache.insert(trace, md_idx);

md_idx
}))
}

fn file_location_to_md(
&mut self,
context: &mut Context,
Expand Down
42 changes: 40 additions & 2 deletions sway-core/src/transform/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ use sway_error::{
use sway_features::Feature;
use sway_types::{Ident, Span, Spanned};

use crate::language::{Inline, Purity};
use crate::language::{Inline, Purity, Trace};

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct AttributeArg {
Expand Down Expand Up @@ -354,6 +354,7 @@ pub enum AttributeKind {
Fallback,
ErrorType,
Error,
Trace,
}

/// Denotes if an [ItemTraitItem] belongs to an ABI or to a trait.
Expand Down Expand Up @@ -384,6 +385,7 @@ impl AttributeKind {
FALLBACK_ATTRIBUTE_NAME => AttributeKind::Fallback,
ERROR_TYPE_ATTRIBUTE_NAME => AttributeKind::ErrorType,
ERROR_ATTRIBUTE_NAME => AttributeKind::Error,
TRACE_ATTRIBUTE_NAME => AttributeKind::Trace,
_ => AttributeKind::Unknown,
}
}
Expand Down Expand Up @@ -412,6 +414,7 @@ impl AttributeKind {
Fallback => false,
ErrorType => false,
Error => false,
Trace => false,
}
}
}
Expand Down Expand Up @@ -440,7 +443,7 @@ impl Attribute {
DocComment => Multiplicity::exactly(1),
// `storage(read, write)`.
Storage => Multiplicity::between(1, 2),
// `inline(always)`.
// `inline(never)` or `inline(always)`.
Inline => Multiplicity::exactly(1),
// `test`, `test(should_revert)`.
Test => Multiplicity::at_most(1),
Expand All @@ -452,6 +455,8 @@ impl Attribute {
Fallback => Multiplicity::zero(),
ErrorType => Multiplicity::zero(),
Error => Multiplicity::exactly(1),
// `trace(never)` or `trace(always)`.
Trace => Multiplicity::exactly(1),
}
}

Expand Down Expand Up @@ -507,6 +512,7 @@ impl Attribute {
Fallback => None,
ErrorType => None,
Error => MustBeIn(vec![ERROR_M_ARG_NAME]),
Trace => MustBeIn(vec![TRACE_ALWAYS_ARG_NAME, TRACE_NEVER_ARG_NAME]),
}
}

Expand All @@ -530,6 +536,7 @@ impl Attribute {
ErrorType => No,
// `error(msg = "msg")`.
Error => Yes,
Trace => No,
}
}

Expand All @@ -550,6 +557,7 @@ impl Attribute {
Fallback => false,
ErrorType => false,
Error => false,
Trace => false,
}
}

Expand Down Expand Up @@ -603,6 +611,7 @@ impl Attribute {
Fallback => matches!(item_kind, ItemKind::Fn(_)),
ErrorType => matches!(item_kind, ItemKind::Enum(_)),
Error => false,
Trace => matches!(item_kind, ItemKind::Fn(_)),
}
}

Expand All @@ -628,6 +637,7 @@ impl Attribute {
Fallback => false,
ErrorType => false,
Error => struct_or_enum_field == StructOrEnumField::EnumField,
Trace => false,
}
}

Expand All @@ -653,6 +663,9 @@ impl Attribute {
Fallback => false,
ErrorType => false,
Error => false,
// Functions in the trait or ABI interface surface cannot be marked as traced
// because they don't have implementation.
Trace => false,
}
}

Expand All @@ -675,6 +688,7 @@ impl Attribute {
Fallback => false,
ErrorType => false,
Error => false,
Trace => matches!(item, ItemImplItem::Fn(..)),
}
}

Expand All @@ -696,6 +710,7 @@ impl Attribute {
Fallback => false,
ErrorType => false,
Error => false,
Trace => true,
}
}

Expand All @@ -715,6 +730,7 @@ impl Attribute {
Fallback => false,
ErrorType => false,
Error => false,
Trace => false,
}
}

Expand All @@ -733,6 +749,7 @@ impl Attribute {
Fallback => false,
ErrorType => false,
Error => false,
Trace => false,
}
}

Expand Down Expand Up @@ -784,6 +801,7 @@ impl Attribute {
Fallback => vec!["\"fallback\" attribute can only annotate module functions in a contract module."],
ErrorType => vec!["\"error_type\" attribute can only annotate enums."],
Error => vec!["\"error\" attribute can only annotate enum variants of enums annotated with the \"error_type\" attribute."],
Trace => vec!["\"trace\" attribute can only annotate functions that can panic."],
};

if help.is_empty() && target_friendly_name.starts_with("module kind") {
Expand Down Expand Up @@ -955,6 +973,26 @@ impl Attributes {
}
}

/// Returns the value of the `#[trace]` [Attribute], or `None` if the
/// [Attributes] does not contain any `#[trace]` attributes.
pub fn trace(&self) -> Option<Trace> {
// `trace` attribute can be applied only once (`AttributeMultiplicity::Single`),
// and can have exactly one argument, otherwise an error is emitted.
// Last-wins approach.
match self
.of_kind(AttributeKind::Trace)
.last()?
.args
.last()?
.name
.as_str()
{
TRACE_NEVER_ARG_NAME => Some(Trace::Never),
TRACE_ALWAYS_ARG_NAME => Some(Trace::Always),
_ => None,
}
}

/// Returns the value of the `#[storage]` [Attribute], or [Purity::Pure] if the
/// [Attributes] does not contain any `#[storage]` attributes.
pub fn purity(&self) -> Purity {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod deprecated_attr;
mod fallback_attr;
mod error_type_attr;
mod error_attr;
mod trace_attr;

/// Outer doc comment supports multiple.
/// Outer doc comment supports multiple.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
library;

struct S { }

impl S {
#[trace(always)]
fn ok_1() {
panic "Panics for tracing purposes.";
}

#[trace(never)]
fn ok_1() {
panic "Panics for tracing purposes.";
}

#[trace(alwys)]
#[trace(unknown_arg)]
fn not_ok() {
panic "Panics for tracing purposes.";
}
}
Loading
Loading