about summary refs log tree commit diff
path: root/compiler/rustc_codegen_ssa/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_codegen_ssa/src')
-rw-r--r--compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs210
1 files changed, 139 insertions, 71 deletions
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
index 25268d9a555..81e905b1b5f 100644
--- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
+++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
@@ -19,8 +19,9 @@ use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathD
 use rustc_middle::ich::NodeIdHashingMode;
 use rustc_middle::ty::layout::IntegerExt;
 use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
-use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
+use rustc_middle::ty::{self, AdtDef, ExistentialProjection, Ty, TyCtxt};
 use rustc_target::abi::{Integer, TagEncoding, Variants};
+use smallvec::SmallVec;
 
 use std::fmt::Write;
 
@@ -33,6 +34,8 @@ pub fn compute_debuginfo_type_name<'tcx>(
     t: Ty<'tcx>,
     qualified: bool,
 ) -> String {
+    let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
+
     let mut result = String::with_capacity(64);
     let mut visited = FxHashSet::default();
     push_debuginfo_type_name(tcx, t, qualified, &mut result, &mut visited);
@@ -41,7 +44,7 @@ pub fn compute_debuginfo_type_name<'tcx>(
 
 // Pushes the name of the type as it should be stored in debuginfo on the
 // `output` String. See also compute_debuginfo_type_name().
-pub fn push_debuginfo_type_name<'tcx>(
+fn push_debuginfo_type_name<'tcx>(
     tcx: TyCtxt<'tcx>,
     t: Ty<'tcx>,
     qualified: bool,
@@ -84,25 +87,14 @@ pub fn push_debuginfo_type_name<'tcx>(
 
             for component_type in component_types {
                 push_debuginfo_type_name(tcx, component_type.expect_ty(), true, output, visited);
-                output.push(',');
-
-                // Natvis does not always like having spaces between parts of the type name
-                // and this causes issues when we need to write a typename in natvis, for example
-                // as part of a cast like the `HashMap` visualizer does.
-                if !cpp_like_names {
-                    output.push(' ');
-                }
+                push_arg_separator(cpp_like_names, output);
             }
             if !component_types.is_empty() {
-                output.pop();
-
-                if !cpp_like_names {
-                    output.pop();
-                }
+                pop_arg_separator(output);
             }
 
             if cpp_like_names {
-                push_close_angle_bracket(tcx, output);
+                push_close_angle_bracket(cpp_like_names, output);
             } else {
                 output.push(')');
             }
@@ -124,7 +116,7 @@ pub fn push_debuginfo_type_name<'tcx>(
             push_debuginfo_type_name(tcx, inner_type, qualified, output, visited);
 
             if cpp_like_names {
-                push_close_angle_bracket(tcx, output);
+                push_close_angle_bracket(cpp_like_names, output);
             }
         }
         ty::Ref(_, inner_type, mutbl) => {
@@ -150,7 +142,7 @@ pub fn push_debuginfo_type_name<'tcx>(
             push_debuginfo_type_name(tcx, inner_type, qualified, output, visited);
 
             if cpp_like_names && !is_slice_or_str {
-                push_close_angle_bracket(tcx, output);
+                push_close_angle_bracket(cpp_like_names, output);
             }
         }
         ty::Array(inner_type, len) => {
@@ -182,69 +174,97 @@ pub fn push_debuginfo_type_name<'tcx>(
             push_debuginfo_type_name(tcx, inner_type, true, output, visited);
 
             if cpp_like_names {
-                push_close_angle_bracket(tcx, output);
+                push_close_angle_bracket(cpp_like_names, output);
             } else {
                 output.push(']');
             }
         }
         ty::Dynamic(ref trait_data, ..) => {
-            if cpp_like_names {
+            let auto_traits: SmallVec<[DefId; 4]> = trait_data.auto_traits().collect();
+
+            let has_enclosing_parens = if cpp_like_names {
                 output.push_str("dyn$<");
+                false
             } else {
-                output.push_str("dyn ");
-            }
+                if trait_data.len() > 1 && auto_traits.len() != 0 {
+                    // We need enclosing parens because there is more than one trait
+                    output.push_str("(dyn ");
+                    true
+                } else {
+                    output.push_str("dyn ");
+                    false
+                }
+            };
 
             if let Some(principal) = trait_data.principal() {
                 let principal =
                     tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), principal);
                 push_item_name(tcx, principal.def_id, qualified, output);
-                push_generic_params_internal(tcx, principal.substs, output, visited);
-            } else {
-                // The auto traits come ordered by `DefPathHash`, which guarantees stability if the
-                // environment is stable (e.g., incremental builds) but not otherwise (e.g.,
-                // updated compiler version, different target).
-                //
-                // To avoid that causing instabilities in test output, sort the auto-traits
-                // alphabetically.
-                let mut auto_traits: Vec<_> = trait_data
-                    .iter()
-                    .filter_map(|predicate| {
-                        match tcx.normalize_erasing_late_bound_regions(
-                            ty::ParamEnv::reveal_all(),
-                            predicate,
-                        ) {
-                            ty::ExistentialPredicate::AutoTrait(def_id) => {
-                                let mut name = String::new();
-                                push_item_name(tcx, def_id, true, &mut name);
-                                Some(name)
-                            }
-                            _ => None,
-                        }
+                let principal_has_generic_params =
+                    push_generic_params_internal(tcx, principal.substs, output, visited);
+
+                let projection_bounds: SmallVec<[_; 4]> = trait_data
+                    .projection_bounds()
+                    .map(|bound| {
+                        let ExistentialProjection { item_def_id, ty, .. } = bound.skip_binder();
+                        (item_def_id, ty)
                     })
                     .collect();
-                auto_traits.sort();
 
-                for name in auto_traits {
-                    output.push_str(&name);
+                if projection_bounds.len() != 0 {
+                    if principal_has_generic_params {
+                        // push_generic_params_internal() above added a `>` but we actually
+                        // want to add more items to that list, so remove that again.
+                        pop_close_angle_bracket(output);
+                    }
 
-                    if cpp_like_names {
-                        output.push_str(", ");
-                    } else {
-                        output.push_str(" + ");
+                    for (item_def_id, ty) in projection_bounds {
+                        push_arg_separator(cpp_like_names, output);
+
+                        if cpp_like_names {
+                            output.push_str("assoc$<");
+                            push_item_name(tcx, item_def_id, false, output);
+                            push_arg_separator(cpp_like_names, output);
+                            push_debuginfo_type_name(tcx, ty, true, output, visited);
+                            push_close_angle_bracket(cpp_like_names, output);
+                        } else {
+                            push_item_name(tcx, item_def_id, false, output);
+                            output.push('=');
+                            push_debuginfo_type_name(tcx, ty, true, output, visited);
+                        }
                     }
+
+                    push_close_angle_bracket(cpp_like_names, output);
                 }
 
-                // Remove the trailing joining characters. For cpp_like_names
-                // this is `, ` otherwise ` + `.
-                output.pop();
-                output.pop();
-                if !cpp_like_names {
-                    output.pop();
+                if auto_traits.len() != 0 {
+                    push_auto_trait_separator(cpp_like_names, output);
                 }
             }
 
+            if auto_traits.len() != 0 {
+                let mut auto_traits: SmallVec<[String; 4]> = auto_traits
+                    .into_iter()
+                    .map(|def_id| {
+                        let mut name = String::with_capacity(20);
+                        push_item_name(tcx, def_id, true, &mut name);
+                        name
+                    })
+                    .collect();
+                auto_traits.sort_unstable();
+
+                for auto_trait in auto_traits {
+                    output.push_str(&auto_trait);
+                    push_auto_trait_separator(cpp_like_names, output);
+                }
+
+                pop_auto_trait_separator(output);
+            }
+
             if cpp_like_names {
-                push_close_angle_bracket(tcx, output);
+                push_close_angle_bracket(cpp_like_names, output);
+            } else if has_enclosing_parens {
+                output.push(')');
             }
         }
         ty::FnDef(..) | ty::FnPtr(_) => {
@@ -296,10 +316,9 @@ pub fn push_debuginfo_type_name<'tcx>(
             if !sig.inputs().is_empty() {
                 for &parameter_type in sig.inputs() {
                     push_debuginfo_type_name(tcx, parameter_type, true, output, visited);
-                    output.push_str(", ");
+                    push_arg_separator(cpp_like_names, output);
                 }
-                output.pop();
-                output.pop();
+                pop_arg_separator(output);
             }
 
             if sig.c_variadic {
@@ -405,7 +424,25 @@ pub fn push_debuginfo_type_name<'tcx>(
                 output.push_str(&format!(", {}", variant));
             }
         }
-        push_close_angle_bracket(tcx, output);
+        push_close_angle_bracket(true, output);
+    }
+
+    const NON_CPP_AUTO_TRAIT_SEPARATOR: &str = " + ";
+
+    fn push_auto_trait_separator(cpp_like_names: bool, output: &mut String) {
+        if cpp_like_names {
+            push_arg_separator(cpp_like_names, output);
+        } else {
+            output.push_str(NON_CPP_AUTO_TRAIT_SEPARATOR);
+        }
+    }
+
+    fn pop_auto_trait_separator(output: &mut String) {
+        if output.ends_with(NON_CPP_AUTO_TRAIT_SEPARATOR) {
+            output.truncate(output.len() - NON_CPP_AUTO_TRAIT_SEPARATOR.len());
+        } else {
+            pop_arg_separator(output);
+        }
     }
 }
 
@@ -466,13 +503,15 @@ fn push_generic_params_internal<'tcx>(
     substs: SubstsRef<'tcx>,
     output: &mut String,
     visited: &mut FxHashSet<Ty<'tcx>>,
-) {
+) -> bool {
     if substs.non_erasable_generics().next().is_none() {
-        return;
+        return false;
     }
 
     debug_assert_eq!(substs, tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substs));
 
+    let cpp_like_names = cpp_like_names(tcx);
+
     output.push('<');
 
     for type_parameter in substs.non_erasable_generics() {
@@ -486,13 +525,12 @@ fn push_generic_params_internal<'tcx>(
             other => bug!("Unexpected non-erasable generic: {:?}", other),
         }
 
-        output.push_str(", ");
+        push_arg_separator(cpp_like_names, output);
     }
+    pop_arg_separator(output);
+    push_close_angle_bracket(cpp_like_names, output);
 
-    output.pop();
-    output.pop();
-
-    push_close_angle_bracket(tcx, output);
+    true
 }
 
 fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: &'tcx ty::Const<'tcx>, output: &mut String) {
@@ -541,20 +579,50 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: &'tcx ty::Const<'tcx>, output:
 }
 
 pub fn push_generic_params<'tcx>(tcx: TyCtxt<'tcx>, substs: SubstsRef<'tcx>, output: &mut String) {
+    let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
     let mut visited = FxHashSet::default();
     push_generic_params_internal(tcx, substs, output, &mut visited);
 }
 
-fn push_close_angle_bracket<'tcx>(tcx: TyCtxt<'tcx>, output: &mut String) {
+fn push_close_angle_bracket(cpp_like_names: bool, output: &mut String) {
     // MSVC debugger always treats `>>` as a shift, even when parsing templates,
     // so add a space to avoid confusion.
-    if cpp_like_names(tcx) && output.ends_with('>') {
+    if cpp_like_names && output.ends_with('>') {
         output.push(' ')
     };
 
     output.push('>');
 }
 
+fn pop_close_angle_bracket(output: &mut String) {
+    assert!(output.ends_with('>'), "'output' does not end with '>': {}", output);
+    output.pop();
+    if output.ends_with(' ') {
+        output.pop();
+    }
+}
+
+fn push_arg_separator(cpp_like_names: bool, output: &mut String) {
+    // Natvis does not always like having spaces between parts of the type name
+    // and this causes issues when we need to write a typename in natvis, for example
+    // as part of a cast like the `HashMap` visualizer does.
+    if cpp_like_names {
+        output.push(',');
+    } else {
+        output.push_str(", ");
+    };
+}
+
+fn pop_arg_separator(output: &mut String) {
+    if output.ends_with(' ') {
+        output.pop();
+    }
+
+    assert!(output.ends_with(','));
+
+    output.pop();
+}
+
 fn cpp_like_names(tcx: TyCtxt<'_>) -> bool {
     tcx.sess.target.is_like_msvc
 }