From 737499593db6d7702de3bf9d0070ec3f8e65d71e Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Mon, 10 Feb 2020 22:52:30 +0200 Subject: [PATCH] rustc_codegen_llvm: expose DILocation to rustc_codegen_ssa. --- compiler/rustc_codegen_llvm/src/builder.rs | 1 + compiler/rustc_codegen_llvm/src/common.rs | 1 + .../src/debuginfo/create_scope_map.rs | 10 +- .../rustc_codegen_llvm/src/debuginfo/mod.rs | 94 ++++++++++---- .../src/debuginfo/source_loc.rs | 61 --------- .../rustc_codegen_ssa/src/mir/debuginfo.rs | 120 ++++++++---------- .../rustc_codegen_ssa/src/traits/backend.rs | 1 + .../rustc_codegen_ssa/src/traits/debuginfo.rs | 7 +- compiler/rustc_codegen_ssa/src/traits/mod.rs | 1 + 9 files changed, 133 insertions(+), 163 deletions(-) delete mode 100644 compiler/rustc_codegen_llvm/src/debuginfo/source_loc.rs diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 491191d058cf..978ad37c0f39 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -56,6 +56,7 @@ impl BackendTypes for Builder<'_, 'll, 'tcx> { type Funclet = as BackendTypes>::Funclet; type DIScope = as BackendTypes>::DIScope; + type DILocation = as BackendTypes>::DILocation; type DIVariable = as BackendTypes>::DIVariable; } diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 2cd745ec420f..7633dd0600d5 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -88,6 +88,7 @@ impl BackendTypes for CodegenCx<'ll, 'tcx> { type Funclet = Funclet<'ll>; type DIScope = &'ll llvm::debuginfo::DIScope; + type DILocation = &'ll llvm::debuginfo::DILocation; type DIVariable = &'ll llvm::debuginfo::DIVariable; } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs index 11855675ccfd..f6b7f257f2f3 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs @@ -49,7 +49,7 @@ fn make_mir_scope( debug_context: &mut FunctionDebugContext<&'ll DIScope>, scope: SourceScope, ) { - if debug_context.scopes[scope].is_valid() { + if debug_context.scopes[scope].dbg_scope.is_some() { return; } @@ -61,7 +61,7 @@ fn make_mir_scope( // The root is the function itself. let loc = cx.lookup_debug_loc(mir.span.lo()); debug_context.scopes[scope] = DebugScope { - scope_metadata: Some(fn_dbg_scope), + dbg_scope: Some(fn_dbg_scope), file_start_pos: loc.file.start_pos, file_end_pos: loc.file.end_pos, }; @@ -78,17 +78,17 @@ fn make_mir_scope( let loc = cx.lookup_debug_loc(scope_data.span.lo()); let file_metadata = file_metadata(cx, &loc.file); - let scope_metadata = unsafe { + let dbg_scope = unsafe { Some(llvm::LLVMRustDIBuilderCreateLexicalBlock( DIB(cx), - parent_scope.scope_metadata.unwrap(), + parent_scope.dbg_scope.unwrap(), file_metadata, loc.line.unwrap_or(UNKNOWN_LINE_NUMBER), loc.col.unwrap_or(UNKNOWN_COLUMN_NUMBER), )) }; debug_context.scopes[scope] = DebugScope { - scope_metadata, + dbg_scope, file_start_pos: loc.file.start_pos, file_end_pos: loc.file.end_pos, }; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index c940656b2561..7b416f1f6adb 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -3,7 +3,8 @@ mod doc; use rustc_codegen_ssa::mir::debuginfo::VariableKind::*; -use self::metadata::{file_metadata, type_metadata, TypeMap, UNKNOWN_LINE_NUMBER}; +use self::metadata::{file_metadata, type_metadata, TypeMap}; +use self::metadata::{UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER}; use self::namespace::mangled_name_of_instance; use self::type_names::compute_debuginfo_type_name; use self::utils::{create_DIArray, is_node_local_to_unit, DIB}; @@ -13,7 +14,8 @@ use crate::builder::Builder; use crate::common::CodegenCx; use crate::llvm; use crate::llvm::debuginfo::{ - DIArray, DIBuilder, DIFile, DIFlags, DILexicalBlock, DISPFlags, DIScope, DIType, DIVariable, + DIArray, DIBuilder, DIFile, DIFlags, DILexicalBlock, DILocation, DISPFlags, DIScope, DIType, + DIVariable, }; use crate::value::Value; @@ -21,6 +23,7 @@ use rustc_codegen_ssa::debuginfo::type_names; use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind}; use rustc_codegen_ssa::traits::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::sync::Lrc; use rustc_hir::def_id::{DefId, DefIdMap, LOCAL_CRATE}; use rustc_index::vec::IndexVec; use rustc_middle::mir; @@ -29,7 +32,7 @@ use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TypeFoldable}; use rustc_session::config::{self, DebugInfo}; use rustc_span::symbol::Symbol; -use rustc_span::{self, BytePos, Span}; +use rustc_span::{self, BytePos, Pos, SourceFile, SourceFileAndLine, Span}; use rustc_target::abi::{LayoutOf, Primitive, Size}; use libc::c_uint; @@ -41,7 +44,6 @@ mod create_scope_map; pub mod gdb; pub mod metadata; mod namespace; -mod source_loc; mod utils; pub use self::create_scope_map::compute_mir_scopes; @@ -141,14 +143,11 @@ impl DebugInfoBuilderMethods for Builder<'a, 'll, 'tcx> { fn dbg_var_addr( &mut self, dbg_var: &'ll DIVariable, - scope_metadata: &'ll DIScope, + dbg_loc: &'ll DILocation, variable_alloca: Self::Value, direct_offset: Size, indirect_offsets: &[Size], - span: Span, ) { - let cx = self.cx(); - // Convert the direct and indirect offsets to address ops. // FIXME(eddyb) use `const`s instead of getting the values via FFI, // the values should match the ones in the DWARF standard anyway. @@ -168,14 +167,10 @@ impl DebugInfoBuilderMethods for Builder<'a, 'll, 'tcx> { } } - // FIXME(eddyb) maybe this information could be extracted from `dbg_var`, - // to avoid having to pass it down in both places? - // NB: `var` doesn't seem to know about the column, so that's a limitation. - let dbg_loc = cx.create_debug_loc(scope_metadata, span); unsafe { // FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`. llvm::LLVMRustDIBuilderInsertDeclareAtEnd( - DIB(cx), + DIB(self.cx()), variable_alloca, dbg_var, addr_ops.as_ptr(), @@ -186,16 +181,13 @@ impl DebugInfoBuilderMethods for Builder<'a, 'll, 'tcx> { } } - fn set_source_location(&mut self, scope: &'ll DIScope, span: Span) { - debug!("set_source_location: {}", self.sess().source_map().span_to_string(span)); - - let dbg_loc = self.cx().create_debug_loc(scope, span); - + fn set_dbg_loc(&mut self, dbg_loc: &'ll DILocation) { unsafe { let dbg_loc_as_llval = llvm::LLVMRustMetadataAsValue(self.cx().llcx, dbg_loc); llvm::LLVMSetCurrentDebugLocation(self.llbuilder, dbg_loc_as_llval); } } + fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) { gdb::insert_reference_to_gdb_debug_scripts_section_global(self) } @@ -224,6 +216,49 @@ impl DebugInfoBuilderMethods for Builder<'a, 'll, 'tcx> { } } +/// A source code location used to generate debug information. +// FIXME(eddyb) rename this to better indicate it's a duplicate of +// `rustc_span::Loc` rather than `DILocation`, perhaps by making +// `lookup_char_pos` return the right information instead. +pub struct DebugLoc { + /// Information about the original source file. + pub file: Lrc, + /// The (1-based) line number. + pub line: Option, + /// The (1-based) column number. + pub col: Option, +} + +impl CodegenCx<'ll, '_> { + /// Looks up debug source information about a `BytePos`. + // FIXME(eddyb) rename this to better indicate it's a duplicate of + // `lookup_char_pos` rather than `dbg_loc`, perhaps by making + // `lookup_char_pos` return the right information instead. + pub fn lookup_debug_loc(&self, pos: BytePos) -> DebugLoc { + let (file, line, col) = match self.sess().source_map().lookup_line(pos) { + Ok(SourceFileAndLine { sf: file, line }) => { + let line_pos = file.line_begin_pos(pos); + + // Use 1-based indexing. + let line = (line + 1) as u32; + let col = (pos - line_pos).to_u32() + 1; + + (file, Some(line), Some(col)) + } + Err(file) => (file, None, None), + }; + + // For MSVC, omit the column number. + // Otherwise, emit it. This mimics clang behaviour. + // See discussion in https://github.com/rust-lang/rust/issues/42921 + if self.sess().target.options.is_like_msvc { + DebugLoc { file, line, col: None } + } else { + DebugLoc { file, line, col } + } + } +} + impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn create_function_debug_context( &self, @@ -237,12 +272,9 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { } // Initialize fn debug context (including scopes). - // FIXME(eddyb) figure out a way to not need `Option` for `scope_metadata`. - let empty_scope = DebugScope { - scope_metadata: None, - file_start_pos: BytePos(0), - file_end_pos: BytePos(0), - }; + // FIXME(eddyb) figure out a way to not need `Option` for `dbg_scope`. + let empty_scope = + DebugScope { dbg_scope: None, file_start_pos: BytePos(0), file_end_pos: BytePos(0) }; let mut fn_debug_context = FunctionDebugContext { scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes) }; @@ -505,6 +537,20 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { } } + fn dbg_loc(&self, scope: &'ll DIScope, span: Span) -> &'ll DILocation { + let DebugLoc { line, col, .. } = self.lookup_debug_loc(span.lo()); + + unsafe { + llvm::LLVMRustDIBuilderCreateDebugLocation( + utils::debug_context(self).llcontext, + line.unwrap_or(UNKNOWN_LINE_NUMBER), + col.unwrap_or(UNKNOWN_COLUMN_NUMBER), + scope, + None, + ) + } + } + fn create_vtable_metadata(&self, ty: Ty<'tcx>, vtable: Self::Value) { metadata::create_vtable_metadata(self, ty, vtable) } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/source_loc.rs b/compiler/rustc_codegen_llvm/src/debuginfo/source_loc.rs deleted file mode 100644 index f1d9b8653bcb..000000000000 --- a/compiler/rustc_codegen_llvm/src/debuginfo/source_loc.rs +++ /dev/null @@ -1,61 +0,0 @@ -use super::metadata::{UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER}; -use super::utils::debug_context; - -use crate::common::CodegenCx; -use crate::llvm; -use crate::llvm::debuginfo::{DILocation, DIScope}; -use rustc_codegen_ssa::traits::*; - -use rustc_data_structures::sync::Lrc; -use rustc_span::{BytePos, Pos, SourceFile, SourceFileAndLine, Span}; - -/// A source code location used to generate debug information. -pub struct DebugLoc { - /// Information about the original source file. - pub file: Lrc, - /// The (1-based) line number. - pub line: Option, - /// The (1-based) column number. - pub col: Option, -} - -impl CodegenCx<'ll, '_> { - /// Looks up debug source information about a `BytePos`. - pub fn lookup_debug_loc(&self, pos: BytePos) -> DebugLoc { - let (file, line, col) = match self.sess().source_map().lookup_line(pos) { - Ok(SourceFileAndLine { sf: file, line }) => { - let line_pos = file.line_begin_pos(pos); - - // Use 1-based indexing. - let line = (line + 1) as u32; - let col = (pos - line_pos).to_u32() + 1; - - (file, Some(line), Some(col)) - } - Err(file) => (file, None, None), - }; - - // For MSVC, omit the column number. - // Otherwise, emit it. This mimics clang behaviour. - // See discussion in https://github.com/rust-lang/rust/issues/42921 - if self.sess().target.options.is_like_msvc { - DebugLoc { file, line, col: None } - } else { - DebugLoc { file, line, col } - } - } - - pub fn create_debug_loc(&self, scope: &'ll DIScope, span: Span) -> &'ll DILocation { - let DebugLoc { line, col, .. } = self.lookup_debug_loc(span.lo()); - - unsafe { - llvm::LLVMRustDIBuilderCreateDebugLocation( - debug_context(self).llcontext, - line.unwrap_or(UNKNOWN_LINE_NUMBER), - col.unwrap_or(UNKNOWN_COLUMN_NUMBER), - scope, - None, - ) - } - } -} diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 09b6ba9a2ef6..ddb9372cf8f7 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -37,71 +37,58 @@ pub struct PerLocalVarDebugInfo<'tcx, D> { #[derive(Clone, Copy, Debug)] pub struct DebugScope { - pub scope_metadata: Option, + // FIXME(eddyb) this should never be `None`, after initialization. + pub dbg_scope: Option, // Start and end offsets of the file to which this DIScope belongs. // These are used to quickly determine whether some span refers to the same file. pub file_start_pos: BytePos, pub file_end_pos: BytePos, } -impl DebugScope { - pub fn is_valid(&self) -> bool { - self.scope_metadata.is_some() - } -} - impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn set_debug_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) { - let (scope, span) = self.debug_loc(source_info); - bx.set_span(span); - if let Some(scope) = scope { - bx.set_source_location(scope, span); + bx.set_span(source_info.span); + if let Some(dbg_loc) = self.dbg_loc(source_info) { + bx.set_dbg_loc(dbg_loc); } } - pub fn debug_loc(&self, source_info: mir::SourceInfo) -> (Option, Span) { + fn dbg_loc(&self, source_info: mir::SourceInfo) -> Option { + let (scope, span) = self.dbg_scope_and_span(source_info)?; + Some(self.cx.dbg_loc(scope, span)) + } + + fn dbg_scope_and_span(&self, source_info: mir::SourceInfo) -> Option<(Bx::DIScope, Span)> { // Bail out if debug info emission is not enabled. - match self.debug_context { - None => return (None, source_info.span), - Some(_) => {} - } + let debug_context = self.debug_context.as_ref()?; + let scope = &debug_context.scopes[source_info.scope]; // In order to have a good line stepping behavior in debugger, we overwrite debug // locations of macro expansions with that of the outermost expansion site // (unless the crate is being compiled with `-Z debug-macros`). - if !source_info.span.from_expansion() || self.cx.sess().opts.debugging_opts.debug_macros { - let scope = self.scope_metadata_for_loc(source_info.scope, source_info.span.lo()); - (scope, source_info.span) - } else { + let mut span = source_info.span; + if span.from_expansion() && !self.cx.sess().opts.debugging_opts.debug_macros { // Walk up the macro expansion chain until we reach a non-expanded span. // We also stop at the function body level because no line stepping can occur // at the level above that. - let span = rustc_span::hygiene::walk_chain(source_info.span, self.mir.span.ctxt()); - let scope = self.scope_metadata_for_loc(source_info.scope, span.lo()); // Use span of the outermost expansion site, while keeping the original lexical scope. - (scope, span) + span = rustc_span::hygiene::walk_chain(span, self.mir.span.ctxt()); } - } - // DILocations inherit source file name from the parent DIScope. Due to macro expansions - // it may so happen that the current span belongs to a different file than the DIScope - // corresponding to span's containing source scope. If so, we need to create a DIScope - // "extension" into that file. - fn scope_metadata_for_loc( - &self, - scope_id: mir::SourceScope, - pos: BytePos, - ) -> Option { - let debug_context = self.debug_context.as_ref()?; - let scope_metadata = debug_context.scopes[scope_id].scope_metadata?; - if pos < debug_context.scopes[scope_id].file_start_pos - || pos >= debug_context.scopes[scope_id].file_end_pos - { + // FIXME(eddyb) this should never be `None`. + let mut dbg_scope = scope.dbg_scope?; + + // DILocations inherit source file name from the parent DIScope. Due to macro expansions + // it may so happen that the current span belongs to a different file than the DIScope + // corresponding to span's containing source scope. If so, we need to create a DIScope + // "extension" into that file. + let pos = span.lo(); + if pos < scope.file_start_pos || pos >= scope.file_end_pos { let sm = self.cx.sess().source_map(); - Some(self.cx.extend_scope_to_file(scope_metadata, &sm.lookup_char_pos(pos).file)) - } else { - Some(scope_metadata) + dbg_scope = self.cx.extend_scope_to_file(dbg_scope, &sm.lookup_char_pos(pos).file); } + + Some((dbg_scope, span)) } /// Apply debuginfo and/or name, after creating the `alloca` for a local, @@ -142,17 +129,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { let name = kw::Invalid; let decl = &self.mir.local_decls[local]; - let (scope, span) = if full_debug_info { - self.debug_loc(decl.source_info) - } else { - (None, decl.source_info.span) - }; - let dbg_var = scope.map(|scope| { - // FIXME(eddyb) is this `+ 1` needed at all? - let kind = VariableKind::ArgumentVariable(arg_index + 1); + let dbg_var = if full_debug_info { + self.dbg_scope_and_span(decl.source_info).map(|(scope, span)| { + // FIXME(eddyb) is this `+ 1` needed at all? + let kind = VariableKind::ArgumentVariable(arg_index + 1); - self.cx.create_dbg_var(name, self.monomorphize(&decl.ty), scope, kind, span) - }); + self.cx.create_dbg_var(name, self.monomorphize(&decl.ty), scope, kind, span) + }) + } else { + None + }; Some(PerLocalVarDebugInfo { name, @@ -233,6 +219,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let vars = vars.iter().copied().chain(fallback_var); for var in vars { + let dbg_var = match var.dbg_var { + Some(dbg_var) => dbg_var, + None => continue, + }; + let dbg_loc = match self.dbg_loc(var.source_info) { + Some(dbg_loc) => dbg_loc, + None => continue, + }; + let mut layout = base.layout; let mut direct_offset = Size::ZERO; // FIXME(eddyb) use smallvec here. @@ -269,19 +264,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - let (scope, span) = self.debug_loc(var.source_info); - if let Some(scope) = scope { - if let Some(dbg_var) = var.dbg_var { - bx.dbg_var_addr( - dbg_var, - scope, - base.llval, - direct_offset, - &indirect_offsets, - span, - ); - } - } + bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets); } } @@ -305,12 +288,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls); for var in &self.mir.var_debug_info { - let (scope, span) = if full_debug_info { - self.debug_loc(var.source_info) - } else { - (None, var.source_info.span) - }; - let dbg_var = scope.map(|scope| { + let scope_and_span = + if full_debug_info { self.dbg_scope_and_span(var.source_info) } else { None }; + let dbg_var = scope_and_span.map(|(scope, span)| { let place = var.place; let var_ty = self.monomorphized_place_ty(place.as_ref()); let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 3fb189e19844..b9c555c2eb06 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -34,6 +34,7 @@ pub trait BackendTypes { // FIXME(eddyb) find a common convention for all of the debuginfo-related // names (choose between `Dbg`, `Debug`, `DebugInfo`, `DI` etc.). type DIScope: Copy; + type DILocation: Copy; type DIVariable: Copy; } diff --git a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs index 3d2e0ac7be2f..a7588eb4d14c 100644 --- a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs @@ -30,6 +30,8 @@ pub trait DebugInfoMethods<'tcx>: BackendTypes { maybe_definition_llfn: Option, ) -> Self::DIScope; + fn dbg_loc(&self, scope: Self::DIScope, span: Span) -> Self::DILocation; + fn extend_scope_to_file( &self, scope_metadata: Self::DIScope, @@ -55,14 +57,13 @@ pub trait DebugInfoBuilderMethods: BackendTypes { fn dbg_var_addr( &mut self, dbg_var: Self::DIVariable, - scope_metadata: Self::DIScope, + dbg_loc: Self::DILocation, variable_alloca: Self::Value, direct_offset: Size, // NB: each offset implies a deref (i.e. they're steps in a pointer chain). indirect_offsets: &[Size], - span: Span, ); - fn set_source_location(&mut self, scope: Self::DIScope, span: Span); + fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation); fn insert_reference_to_gdb_debug_scripts_section_global(&mut self); fn set_var_name(&mut self, value: Self::Value, name: &str); } diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index 82518b7f0c38..8ada6c10479d 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -95,6 +95,7 @@ pub trait HasCodegen<'tcx>: Type = Self::Type, Funclet = Self::Funclet, DIScope = Self::DIScope, + DILocation = Self::DILocation, DIVariable = Self::DIVariable, >; }