about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-01-19 14:37:58 +0000
committerbors <bors@rust-lang.org>2024-01-19 14:37:58 +0000
commit04edfa1e5af0212553ba4e0d3ee2692eb15a38c9 (patch)
tree5313f3ef1687323d23d8877cea9c5daf89af6a74
parent03d4a6dd9a53ee847674251abe2ae6e13bb24476 (diff)
parent46e38318a225f493436c15d6f13494549dc7f20c (diff)
Auto merge of #16388 - Veykril:fn-abi, r=Veykril
internal: Record FnAbi

This unfortunately breaks our lub coercions, so will need to look into fixing that first, though I am not sure what is going wrong where...

Stubbed some stuff out for the time being.
-rw-r--r--crates/hir-def/src/hir/type_ref.rs22
-rw-r--r--crates/hir-def/src/item_tree/lower.rs16
-rw-r--r--crates/hir-def/src/pretty.rs7
-rw-r--r--crates/hir-ty/src/chalk_db.rs6
-rw-r--r--crates/hir-ty/src/chalk_ext.rs6
-rw-r--r--crates/hir-ty/src/display.rs39
-rw-r--r--crates/hir-ty/src/infer.rs6
-rw-r--r--crates/hir-ty/src/infer/closure.rs10
-rw-r--r--crates/hir-ty/src/infer/coerce.rs4
-rw-r--r--crates/hir-ty/src/infer/expr.rs12
-rw-r--r--crates/hir-ty/src/interner.rs6
-rw-r--r--crates/hir-ty/src/lib.rs160
-rw-r--r--crates/hir-ty/src/lower.rs17
-rw-r--r--crates/hir-ty/src/tests/coercion.rs1
-rw-r--r--crates/hir-ty/src/tests/macros.rs4
-rw-r--r--crates/hir-ty/src/tests/patterns.rs12
-rw-r--r--crates/hir-ty/src/tests/regression.rs30
-rw-r--r--crates/hir-ty/src/tests/simple.rs54
-rw-r--r--crates/hir-ty/src/tests/traits.rs26
-rw-r--r--crates/syntax/src/ast/node_ext.rs6
20 files changed, 329 insertions, 115 deletions
diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs
index 935a8ebad16..17ff736aae4 100644
--- a/crates/hir-def/src/hir/type_ref.rs
+++ b/crates/hir-def/src/hir/type_ref.rs
@@ -10,7 +10,7 @@ use hir_expand::{
     AstId,
 };
 use intern::Interned;
-use syntax::ast::{self, HasName};
+use syntax::ast::{self, HasName, IsString};
 
 use crate::{
     builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
@@ -120,7 +120,12 @@ pub enum TypeRef {
     Array(Box<TypeRef>, ConstRef),
     Slice(Box<TypeRef>),
     /// A fn pointer. Last element of the vector is the return type.
-    Fn(Vec<(Option<Name>, TypeRef)>, bool /*varargs*/, bool /*is_unsafe*/),
+    Fn(
+        Vec<(Option<Name>, TypeRef)>,
+        bool,                  /*varargs*/
+        bool,                  /*is_unsafe*/
+        Option<Interned<str>>, /* abi */
+    ),
     ImplTrait(Vec<Interned<TypeBound>>),
     DynTrait(Vec<Interned<TypeBound>>),
     Macro(AstId<ast::MacroCall>),
@@ -225,8 +230,17 @@ impl TypeRef {
                 } else {
                     Vec::new()
                 };
+                fn lower_abi(abi: ast::Abi) -> Interned<str> {
+                    match abi.abi_string() {
+                        Some(tok) => Interned::new_str(tok.text_without_quotes()),
+                        // `extern` default to be `extern "C"`.
+                        _ => Interned::new_str("C"),
+                    }
+                }
+
+                let abi = inner.abi().map(lower_abi);
                 params.push((None, ret_ty));
-                TypeRef::Fn(params, is_varargs, inner.unsafe_token().is_some())
+                TypeRef::Fn(params, is_varargs, inner.unsafe_token().is_some(), abi)
             }
             // for types are close enough for our purposes to the inner type for now...
             ast::Type::ForType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
@@ -260,7 +274,7 @@ impl TypeRef {
         fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
             f(type_ref);
             match type_ref {
-                TypeRef::Fn(params, _, _) => {
+                TypeRef::Fn(params, _, _, _) => {
                     params.iter().for_each(|(_, param_type)| go(param_type, f))
                 }
                 TypeRef::Tuple(types) => types.iter().for_each(|t| go(t, f)),
diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs
index f6086ed6d9b..b500f56b6c1 100644
--- a/crates/hir-def/src/item_tree/lower.rs
+++ b/crates/hir-def/src/item_tree/lower.rs
@@ -3,7 +3,7 @@
 use std::collections::hash_map::Entry;
 
 use hir_expand::{ast_id_map::AstIdMap, span_map::SpanMapRef, HirFileId};
-use syntax::ast::{self, HasModuleItem, HasTypeBounds};
+use syntax::ast::{self, HasModuleItem, HasTypeBounds, IsString};
 
 use crate::{
     generics::{GenericParams, GenericParamsCollector, TypeParamData, TypeParamProvenance},
@@ -721,16 +721,10 @@ enum HasImplicitSelf {
 }
 
 fn lower_abi(abi: ast::Abi) -> Interned<str> {
-    // FIXME: Abi::abi() -> Option<SyntaxToken>?
-    match abi.syntax().last_token() {
-        Some(tok) if tok.kind() == SyntaxKind::STRING => {
-            // FIXME: Better way to unescape?
-            Interned::new_str(tok.text().trim_matches('"'))
-        }
-        _ => {
-            // `extern` default to be `extern "C"`.
-            Interned::new_str("C")
-        }
+    match abi.abi_string() {
+        Some(tok) => Interned::new_str(tok.text_without_quotes()),
+        // `extern` default to be `extern "C"`.
+        _ => Interned::new_str("C"),
     }
 }
 
diff --git a/crates/hir-def/src/pretty.rs b/crates/hir-def/src/pretty.rs
index 8e0df469701..d3135bba965 100644
--- a/crates/hir-def/src/pretty.rs
+++ b/crates/hir-def/src/pretty.rs
@@ -192,12 +192,17 @@ pub(crate) fn print_type_ref(
             print_type_ref(db, elem, buf)?;
             write!(buf, "]")?;
         }
-        TypeRef::Fn(args_and_ret, varargs, is_unsafe) => {
+        TypeRef::Fn(args_and_ret, varargs, is_unsafe, abi) => {
             let ((_, return_type), args) =
                 args_and_ret.split_last().expect("TypeRef::Fn is missing return type");
             if *is_unsafe {
                 write!(buf, "unsafe ")?;
             }
+            if let Some(abi) = abi {
+                buf.write_str("extern ")?;
+                buf.write_str(abi)?;
+                buf.write_char(' ')?;
+            }
             write!(buf, "fn(")?;
             for (i, (_, typeref)) in args.iter().enumerate() {
                 if i != 0 {
diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs
index f475c1c178d..4d509f20d01 100644
--- a/crates/hir-ty/src/chalk_db.rs
+++ b/crates/hir-ty/src/chalk_db.rs
@@ -819,7 +819,11 @@ pub(crate) fn fn_def_datum_query(
     };
     let datum = FnDefDatum {
         id: fn_def_id,
-        sig: chalk_ir::FnSig { abi: (), safety: chalk_ir::Safety::Safe, variadic: sig.is_varargs },
+        sig: chalk_ir::FnSig {
+            abi: sig.abi,
+            safety: chalk_ir::Safety::Safe,
+            variadic: sig.is_varargs,
+        },
         binders: chalk_ir::Binders::new(binders, bound),
     };
     Arc::new(datum)
diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs
index d6d0b34934a..795a5996912 100644
--- a/crates/hir-ty/src/chalk_ext.rs
+++ b/crates/hir-ty/src/chalk_ext.rs
@@ -202,11 +202,7 @@ impl TyExt for Ty {
     fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig> {
         match self.kind(Interner) {
             TyKind::Function(fn_ptr) => Some(CallableSig::from_fn_ptr(fn_ptr)),
-            TyKind::FnDef(def, parameters) => {
-                let callable_def = db.lookup_intern_callable_def((*def).into());
-                let sig = db.callable_item_signature(callable_def);
-                Some(sig.substitute(Interner, parameters))
-            }
+            TyKind::FnDef(def, parameters) => Some(CallableSig::from_def(db, *def, parameters)),
             TyKind::Closure(.., substs) => ClosureSubst(substs).sig_ty().callable_sig(db),
             _ => None,
         }
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index 750e1597279..a630db05de2 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -8,7 +8,7 @@ use std::{
 };
 
 use base_db::CrateId;
-use chalk_ir::{BoundVar, TyKind};
+use chalk_ir::{BoundVar, Safety, TyKind};
 use hir_def::{
     data::adt::VariantData,
     db::DefDatabase,
@@ -41,7 +41,7 @@ use crate::{
     primitive, to_assoc_type_id,
     utils::{self, detect_variant_from_bytes, generics, ClosureSubst},
     AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue,
-    DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives,
+    DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives,
     MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar,
     Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyExt, WhereClause,
 };
@@ -879,16 +879,27 @@ impl HirDisplay for Ty {
                     // function pointer type.
                     return sig.hir_fmt(f);
                 }
+                if let Safety::Unsafe = sig.safety {
+                    write!(f, "unsafe ")?;
+                }
+                if !matches!(sig.abi, FnAbi::Rust) {
+                    f.write_str("extern \"")?;
+                    f.write_str(sig.abi.as_str())?;
+                    f.write_str("\" ")?;
+                }
 
-                f.start_location_link(def.into());
                 match def {
                     CallableDefId::FunctionId(ff) => {
-                        write!(f, "fn {}", db.function_data(ff).name.display(f.db.upcast()))?
+                        write!(f, "fn ")?;
+                        f.start_location_link(def.into());
+                        write!(f, "{}", db.function_data(ff).name.display(f.db.upcast()))?
                     }
                     CallableDefId::StructId(s) => {
+                        f.start_location_link(def.into());
                         write!(f, "{}", db.struct_data(s).name.display(f.db.upcast()))?
                     }
                     CallableDefId::EnumVariantId(e) => {
+                        f.start_location_link(def.into());
                         write!(f, "{}", db.enum_variant_data(e).name.display(f.db.upcast()))?
                     }
                 };
@@ -1316,9 +1327,19 @@ fn hir_fmt_generics(
 
 impl HirDisplay for CallableSig {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
+        let CallableSig { params_and_return: _, is_varargs, safety, abi: _ } = *self;
+        if let Safety::Unsafe = safety {
+            write!(f, "unsafe ")?;
+        }
+        // FIXME: Enable this when the FIXME on FnAbi regarding PartialEq is fixed.
+        // if !matches!(abi, FnAbi::Rust) {
+        //     f.write_str("extern \"")?;
+        //     f.write_str(abi.as_str())?;
+        //     f.write_str("\" ")?;
+        // }
         write!(f, "fn(")?;
         f.write_joined(self.params(), ", ")?;
-        if self.is_varargs {
+        if is_varargs {
             if self.params().is_empty() {
                 write!(f, "...")?;
             } else {
@@ -1683,11 +1704,15 @@ impl HirDisplay for TypeRef {
                 inner.hir_fmt(f)?;
                 write!(f, "]")?;
             }
-            &TypeRef::Fn(ref parameters, is_varargs, is_unsafe) => {
-                // FIXME: Function pointer qualifiers.
+            &TypeRef::Fn(ref parameters, is_varargs, is_unsafe, ref abi) => {
                 if is_unsafe {
                     write!(f, "unsafe ")?;
                 }
+                if let Some(abi) = abi {
+                    f.write_str("extern \"")?;
+                    f.write_str(abi)?;
+                    f.write_str("\" ")?;
+                }
                 write!(f, "fn(")?;
                 if let Some(((_, return_type), function_parameters)) = parameters.split_last() {
                     for index in 0..function_parameters.len() {
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index c3ef6c9b35d..0d89269b325 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -1453,10 +1453,10 @@ impl Expectation {
         match self {
             Expectation::HasType(ety) => {
                 let ety = table.resolve_ty_shallow(ety);
-                if !ety.is_ty_var() {
-                    Expectation::HasType(ety)
-                } else {
+                if ety.is_ty_var() {
                     Expectation::None
+                } else {
+                    Expectation::HasType(ety)
                 }
             }
             Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety.clone()),
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index 4ec82865acf..d6d021340d1 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -27,8 +27,8 @@ use crate::{
     static_lifetime, to_chalk_trait_id,
     traits::FnTrait,
     utils::{self, generics, Generics},
-    Adjust, Adjustment, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy, FnPointer, FnSig,
-    Interner, Substitution, Ty, TyExt,
+    Adjust, Adjustment, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy, FnAbi, FnPointer,
+    FnSig, Interner, Substitution, Ty, TyExt,
 };
 
 use super::{Expectation, InferenceContext};
@@ -98,7 +98,11 @@ impl InferenceContext<'_> {
                     cov_mark::hit!(dyn_fn_param_informs_call_site_closure_signature);
                     return Some(FnPointer {
                         num_binders: bound.len(Interner),
-                        sig: FnSig { abi: (), safety: chalk_ir::Safety::Safe, variadic: false },
+                        sig: FnSig {
+                            abi: FnAbi::RustCall,
+                            safety: chalk_ir::Safety::Safe,
+                            variadic: false,
+                        },
                         substitution: FnSubst(Substitution::from_iter(Interner, sig_tys)),
                     });
                 }
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs
index 8e7e62c4961..61638c43d9c 100644
--- a/crates/hir-ty/src/infer/coerce.rs
+++ b/crates/hir-ty/src/infer/coerce.rs
@@ -24,7 +24,7 @@ use crate::{
     },
     static_lifetime,
     utils::ClosureSubst,
-    Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner, Solution,
+    Canonical, DomainGoal, FnAbi, FnPointer, FnSig, Guidance, InEnvironment, Interner, Solution,
     Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
 };
 
@@ -691,7 +691,7 @@ fn coerce_closure_fn_ty(closure_substs: &Substitution, safety: chalk_ir::Safety)
     match closure_sig.kind(Interner) {
         TyKind::Function(fn_ty) => TyKind::Function(FnPointer {
             num_binders: fn_ty.num_binders,
-            sig: FnSig { safety, ..fn_ty.sig },
+            sig: FnSig { safety, abi: FnAbi::Rust, variadic: fn_ty.sig.variadic },
             substitution: fn_ty.substitution.clone(),
         })
         .intern(Interner),
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index a655d1614a2..bf64795a4be 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -39,9 +39,9 @@ use crate::{
     static_lifetime, to_chalk_trait_id,
     traits::FnTrait,
     utils::{generics, Generics},
-    Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst,
-    Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt,
-    TyKind,
+    Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnAbi, FnPointer, FnSig,
+    FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder,
+    TyExt, TyKind,
 };
 
 use super::{
@@ -224,7 +224,11 @@ impl InferenceContext<'_> {
 
                 let sig_ty = TyKind::Function(FnPointer {
                     num_binders: 0,
-                    sig: FnSig { abi: (), safety: chalk_ir::Safety::Safe, variadic: false },
+                    sig: FnSig {
+                        abi: FnAbi::RustCall,
+                        safety: chalk_ir::Safety::Safe,
+                        variadic: false,
+                    },
                     substitution: FnSubst(
                         Substitution::from_iter(Interner, sig_tys.iter().cloned())
                             .shifted_in(Interner),
diff --git a/crates/hir-ty/src/interner.rs b/crates/hir-ty/src/interner.rs
index 3d16fec7901..7f994783c11 100644
--- a/crates/hir-ty/src/interner.rs
+++ b/crates/hir-ty/src/interner.rs
@@ -3,8 +3,8 @@
 
 use crate::{
     chalk_db, tls, AliasTy, CanonicalVarKind, CanonicalVarKinds, ClosureId, Const, ConstData,
-    ConstScalar, Constraint, Constraints, FnDefId, GenericArg, GenericArgData, Goal, GoalData,
-    Goals, InEnvironment, Lifetime, LifetimeData, OpaqueTy, OpaqueTyId, ProgramClause,
+    ConstScalar, Constraint, Constraints, FnAbi, FnDefId, GenericArg, GenericArgData, Goal,
+    GoalData, Goals, InEnvironment, Lifetime, LifetimeData, OpaqueTy, OpaqueTyId, ProgramClause,
     ProgramClauseData, ProgramClauses, ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses,
     Substitution, Ty, TyData, TyKind, VariableKind, VariableKinds,
 };
@@ -71,7 +71,7 @@ impl chalk_ir::interner::Interner for Interner {
     type DefId = InternId;
     type InternedAdtId = hir_def::AdtId;
     type Identifier = TypeAliasId;
-    type FnAbi = ();
+    type FnAbi = FnAbi;
 
     fn debug_adt_id(
         type_kind_id: chalk_db::AdtId,
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index a50e1b1c076..61055583fc0 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -351,10 +351,156 @@ pub struct CallableSig {
     params_and_return: Arc<[Ty]>,
     is_varargs: bool,
     safety: Safety,
+    abi: FnAbi,
 }
 
 has_interner!(CallableSig);
 
+#[derive(Debug, Copy, Clone, Eq)]
+pub enum FnAbi {
+    Aapcs,
+    AapcsUnwind,
+    AmdgpuKernel,
+    AvrInterrupt,
+    AvrNonBlockingInterrupt,
+    C,
+    CCmseNonsecureCall,
+    CDecl,
+    CDeclUnwind,
+    CUnwind,
+    Efiapi,
+    Fastcall,
+    FastcallUnwind,
+    Msp430Interrupt,
+    PlatformIntrinsic,
+    PtxKernel,
+    RiscvInterruptM,
+    RiscvInterruptS,
+    Rust,
+    RustCall,
+    RustCold,
+    RustIntrinsic,
+    Stdcall,
+    StdcallUnwind,
+    System,
+    SystemUnwind,
+    Sysv64,
+    Sysv64Unwind,
+    Thiscall,
+    ThiscallUnwind,
+    Unadjusted,
+    Vectorcall,
+    VectorcallUnwind,
+    Wasm,
+    Win64,
+    Win64Unwind,
+    X86Interrupt,
+    Unknown,
+}
+
+impl PartialEq for FnAbi {
+    fn eq(&self, _other: &Self) -> bool {
+        // FIXME: Proper equality breaks `coercion::two_closures_lub` test
+        true
+    }
+}
+
+impl Hash for FnAbi {
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        // Required because of the FIXME above and due to us implementing `Eq`, without this
+        // we would break the `Hash` + `Eq` contract
+        core::mem::discriminant(&Self::Unknown).hash(state);
+    }
+}
+
+impl FnAbi {
+    pub fn from_str(s: &str) -> FnAbi {
+        match s {
+            "aapcs-unwind" => FnAbi::AapcsUnwind,
+            "aapcs" => FnAbi::Aapcs,
+            "amdgpu-kernel" => FnAbi::AmdgpuKernel,
+            "avr-interrupt" => FnAbi::AvrInterrupt,
+            "avr-non-blocking-interrupt" => FnAbi::AvrNonBlockingInterrupt,
+            "C-cmse-nonsecure-call" => FnAbi::CCmseNonsecureCall,
+            "C-unwind" => FnAbi::CUnwind,
+            "C" => FnAbi::C,
+            "cdecl-unwind" => FnAbi::CDeclUnwind,
+            "cdecl" => FnAbi::CDecl,
+            "efiapi" => FnAbi::Efiapi,
+            "fastcall-unwind" => FnAbi::FastcallUnwind,
+            "fastcall" => FnAbi::Fastcall,
+            "msp430-interrupt" => FnAbi::Msp430Interrupt,
+            "platform-intrinsic" => FnAbi::PlatformIntrinsic,
+            "ptx-kernel" => FnAbi::PtxKernel,
+            "riscv-interrupt-m" => FnAbi::RiscvInterruptM,
+            "riscv-interrupt-s" => FnAbi::RiscvInterruptS,
+            "rust-call" => FnAbi::RustCall,
+            "rust-cold" => FnAbi::RustCold,
+            "rust-intrinsic" => FnAbi::RustIntrinsic,
+            "Rust" => FnAbi::Rust,
+            "stdcall-unwind" => FnAbi::StdcallUnwind,
+            "stdcall" => FnAbi::Stdcall,
+            "system-unwind" => FnAbi::SystemUnwind,
+            "system" => FnAbi::System,
+            "sysv64-unwind" => FnAbi::Sysv64Unwind,
+            "sysv64" => FnAbi::Sysv64,
+            "thiscall-unwind" => FnAbi::ThiscallUnwind,
+            "thiscall" => FnAbi::Thiscall,
+            "unadjusted" => FnAbi::Unadjusted,
+            "vectorcall-unwind" => FnAbi::VectorcallUnwind,
+            "vectorcall" => FnAbi::Vectorcall,
+            "wasm" => FnAbi::Wasm,
+            "win64-unwind" => FnAbi::Win64Unwind,
+            "win64" => FnAbi::Win64,
+            "x86-interrupt" => FnAbi::X86Interrupt,
+            _ => FnAbi::Unknown,
+        }
+    }
+
+    pub fn as_str(self) -> &'static str {
+        match self {
+            FnAbi::Aapcs => "aapcs",
+            FnAbi::AapcsUnwind => "aapcs-unwind",
+            FnAbi::AmdgpuKernel => "amdgpu-kernel",
+            FnAbi::AvrInterrupt => "avr-interrupt",
+            FnAbi::AvrNonBlockingInterrupt => "avr-non-blocking-interrupt",
+            FnAbi::C => "C",
+            FnAbi::CCmseNonsecureCall => "C-cmse-nonsecure-call",
+            FnAbi::CDecl => "C-decl",
+            FnAbi::CDeclUnwind => "cdecl-unwind",
+            FnAbi::CUnwind => "C-unwind",
+            FnAbi::Efiapi => "efiapi",
+            FnAbi::Fastcall => "fastcall",
+            FnAbi::FastcallUnwind => "fastcall-unwind",
+            FnAbi::Msp430Interrupt => "msp430-interrupt",
+            FnAbi::PlatformIntrinsic => "platform-intrinsic",
+            FnAbi::PtxKernel => "ptx-kernel",
+            FnAbi::RiscvInterruptM => "riscv-interrupt-m",
+            FnAbi::RiscvInterruptS => "riscv-interrupt-s",
+            FnAbi::Rust => "Rust",
+            FnAbi::RustCall => "rust-call",
+            FnAbi::RustCold => "rust-cold",
+            FnAbi::RustIntrinsic => "rust-intrinsic",
+            FnAbi::Stdcall => "stdcall",
+            FnAbi::StdcallUnwind => "stdcall-unwind",
+            FnAbi::System => "system",
+            FnAbi::SystemUnwind => "system-unwind",
+            FnAbi::Sysv64 => "sysv64",
+            FnAbi::Sysv64Unwind => "sysv64-unwind",
+            FnAbi::Thiscall => "thiscall",
+            FnAbi::ThiscallUnwind => "thiscall-unwind",
+            FnAbi::Unadjusted => "unadjusted",
+            FnAbi::Vectorcall => "vectorcall",
+            FnAbi::VectorcallUnwind => "vectorcall-unwind",
+            FnAbi::Wasm => "wasm",
+            FnAbi::Win64 => "win64",
+            FnAbi::Win64Unwind => "win64-unwind",
+            FnAbi::X86Interrupt => "x86-interrupt",
+            FnAbi::Unknown => "unknown-abi",
+        }
+    }
+}
+
 /// A polymorphic function signature.
 pub type PolyFnSig = Binders<CallableSig>;
 
@@ -364,11 +510,17 @@ impl CallableSig {
         ret: Ty,
         is_varargs: bool,
         safety: Safety,
+        abi: FnAbi,
     ) -> CallableSig {
         params.push(ret);
-        CallableSig { params_and_return: params.into(), is_varargs, safety }
+        CallableSig { params_and_return: params.into(), is_varargs, safety, abi }
     }
 
+    pub fn from_def(db: &dyn HirDatabase, def: FnDefId, substs: &Substitution) -> CallableSig {
+        let callable_def = db.lookup_intern_callable_def(def.into());
+        let sig = db.callable_item_signature(callable_def);
+        sig.substitute(Interner, substs)
+    }
     pub fn from_fn_ptr(fn_ptr: &FnPointer) -> CallableSig {
         CallableSig {
             // FIXME: what to do about lifetime params? -> return PolyFnSig
@@ -385,13 +537,14 @@ impl CallableSig {
             ),
             is_varargs: fn_ptr.sig.variadic,
             safety: fn_ptr.sig.safety,
+            abi: fn_ptr.sig.abi,
         }
     }
 
     pub fn to_fn_ptr(&self) -> FnPointer {
         FnPointer {
             num_binders: 0,
-            sig: FnSig { abi: (), safety: self.safety, variadic: self.is_varargs },
+            sig: FnSig { abi: self.abi, safety: self.safety, variadic: self.is_varargs },
             substitution: FnSubst(Substitution::from_iter(
                 Interner,
                 self.params_and_return.iter().cloned(),
@@ -420,6 +573,7 @@ impl TypeFoldable<Interner> for CallableSig {
             params_and_return: folded.into(),
             is_varargs: self.is_varargs,
             safety: self.safety,
+            abi: self.abi,
         })
     }
 }
@@ -704,7 +858,7 @@ pub fn callable_sig_from_fnonce(
     let params =
         args_ty.as_tuple()?.iter(Interner).map(|it| it.assert_ty_ref(Interner)).cloned().collect();
 
-    Some(CallableSig::from_params_and_return(params, ret_ty, false, Safety::Safe))
+    Some(CallableSig::from_params_and_return(params, ret_ty, false, Safety::Safe, FnAbi::RustCall))
 }
 
 struct PlaceholderCollector<'db> {
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 2d43fffa6a2..46f5830ef7d 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -58,7 +58,7 @@ use crate::{
         InTypeConstIdMetadata,
     },
     AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy,
-    FnPointer, FnSig, FnSubst, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy,
+    FnAbi, FnPointer, FnSig, FnSubst, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy,
     QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits,
     Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
 };
@@ -279,14 +279,14 @@ impl<'a> TyLoweringContext<'a> {
                     .intern(Interner)
             }
             TypeRef::Placeholder => TyKind::Error.intern(Interner),
-            &TypeRef::Fn(ref params, variadic, is_unsafe) => {
+            &TypeRef::Fn(ref params, variadic, is_unsafe, ref abi) => {
                 let substs = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
                     Substitution::from_iter(Interner, params.iter().map(|(_, tr)| ctx.lower_ty(tr)))
                 });
                 TyKind::Function(FnPointer {
                     num_binders: 0, // FIXME lower `for<'a> fn()` correctly
                     sig: FnSig {
-                        abi: (),
+                        abi: abi.as_deref().map_or(FnAbi::Rust, FnAbi::from_str),
                         safety: if is_unsafe { Safety::Unsafe } else { Safety::Safe },
                         variadic,
                     },
@@ -1675,6 +1675,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
         ret,
         data.is_varargs(),
         if data.has_unsafe_kw() { Safety::Unsafe } else { Safety::Safe },
+        data.abi.as_deref().map_or(FnAbi::Rust, FnAbi::from_str),
     );
     make_binders(db, &generics, sig)
 }
@@ -1719,7 +1720,10 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS
         .with_type_param_mode(ParamLoweringMode::Variable);
     let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>();
     let (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders();
-    Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe))
+    Binders::new(
+        binders,
+        CallableSig::from_params_and_return(params, ret, false, Safety::Safe, FnAbi::RustCall),
+    )
 }
 
 /// Build the type of a tuple struct constructor.
@@ -1749,7 +1753,10 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId)
     let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>();
     let (ret, binders) =
         type_for_adt(db, def.lookup(db.upcast()).parent.into()).into_value_and_skipped_binders();
-    Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe))
+    Binders::new(
+        binders,
+        CallableSig::from_params_and_return(params, ret, false, Safety::Safe, FnAbi::RustCall),
+    )
 }
 
 /// Build the type of a tuple enum variant constructor.
diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs
index f0202471678..d56b15b9b74 100644
--- a/crates/hir-ty/src/tests/coercion.rs
+++ b/crates/hir-ty/src/tests/coercion.rs
@@ -574,6 +574,7 @@ fn two_closures_lub() {
         r#"
 fn foo(c: i32) {
     let add = |a: i32, b: i32| a + b;
+            //^^^^^^^^^^^^^^^^^^^^^^ impl Fn(i32, i32) -> i32
     let sub = |a, b| a - b;
             //^^^^^^^^^^^^ impl Fn(i32, i32) -> i32
     if c > 42 { add } else { sub };
diff --git a/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs
index 622b4f56d4f..b0a9361f1c5 100644
--- a/crates/hir-ty/src/tests/macros.rs
+++ b/crates/hir-ty/src/tests/macros.rs
@@ -64,7 +64,7 @@ fn infer_macros_expanded() {
         "#,
         expect![[r#"
             !0..17 '{Foo(v...,2,])}': Foo
-            !1..4 'Foo': Foo({unknown}) -> Foo
+            !1..4 'Foo': extern "rust-call" Foo({unknown}) -> Foo
             !1..16 'Foo(vec![1,2,])': Foo
             !5..15 'vec![1,2,]': {unknown}
             155..181 '{     ...,2); }': ()
@@ -97,7 +97,7 @@ fn infer_legacy_textual_scoped_macros_expanded() {
         "#,
         expect![[r#"
             !0..17 '{Foo(v...,2,])}': Foo
-            !1..4 'Foo': Foo({unknown}) -> Foo
+            !1..4 'Foo': extern "rust-call" Foo({unknown}) -> Foo
             !1..16 'Foo(vec![1,2,])': Foo
             !5..15 'vec![1,2,]': {unknown}
             194..250 '{     ...,2); }': ()
diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs
index 548f782f4f2..06900730822 100644
--- a/crates/hir-ty/src/tests/patterns.rs
+++ b/crates/hir-ty/src/tests/patterns.rs
@@ -210,13 +210,13 @@ fn infer_pattern_match_ergonomics() {
             37..41 'A(n)': A<i32>
             39..40 'n': &i32
             44..49 '&A(1)': &A<i32>
-            45..46 'A': A<i32>(i32) -> A<i32>
+            45..46 'A': extern "rust-call" A<i32>(i32) -> A<i32>
             45..49 'A(1)': A<i32>
             47..48 '1': i32
             59..63 'A(n)': A<i32>
             61..62 'n': &mut i32
             66..75 '&mut A(1)': &mut A<i32>
-            71..72 'A': A<i32>(i32) -> A<i32>
+            71..72 'A': extern "rust-call" A<i32>(i32) -> A<i32>
             71..75 'A(1)': A<i32>
             73..74 '1': i32
         "#]],
@@ -531,18 +531,18 @@ impl Foo {
             56..64 'Self(s,)': Foo
             61..62 's': &usize
             67..75 '&Foo(0,)': &Foo
-            68..71 'Foo': Foo(usize) -> Foo
+            68..71 'Foo': extern "rust-call" Foo(usize) -> Foo
             68..75 'Foo(0,)': Foo
             72..73 '0': usize
             89..97 'Self(s,)': Foo
             94..95 's': &mut usize
             100..112 '&mut Foo(0,)': &mut Foo
-            105..108 'Foo': Foo(usize) -> Foo
+            105..108 'Foo': extern "rust-call" Foo(usize) -> Foo
             105..112 'Foo(0,)': Foo
             109..110 '0': usize
             126..134 'Self(s,)': Foo
             131..132 's': usize
-            137..140 'Foo': Foo(usize) -> Foo
+            137..140 'Foo': extern "rust-call" Foo(usize) -> Foo
             137..144 'Foo(0,)': Foo
             141..142 '0': usize
         "#]],
@@ -916,7 +916,7 @@ fn foo(foo: Foo) {
             48..51 'foo': Foo
             62..84 'const ... 32) }': Foo
             68..84 '{ Foo(... 32) }': Foo
-            70..73 'Foo': Foo(usize) -> Foo
+            70..73 'Foo': extern "rust-call" Foo(usize) -> Foo
             70..82 'Foo(15 + 32)': Foo
             74..76 '15': usize
             74..81 '15 + 32': usize
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index b0532ddcb3b..2ad9a7fe525 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -644,7 +644,7 @@ fn issue_4953() {
         "#,
         expect![[r#"
             58..72 '{ Self(0i64) }': Foo
-            60..64 'Self': Foo(i64) -> Foo
+            60..64 'Self': extern "rust-call" Foo(i64) -> Foo
             60..70 'Self(0i64)': Foo
             65..69 '0i64': i64
         "#]],
@@ -658,7 +658,7 @@ fn issue_4953() {
         "#,
         expect![[r#"
             64..78 '{ Self(0i64) }': Foo<i64>
-            66..70 'Self': Foo<i64>(i64) -> Foo<i64>
+            66..70 'Self': extern "rust-call" Foo<i64>(i64) -> Foo<i64>
             66..76 'Self(0i64)': Foo<i64>
             71..75 '0i64': i64
         "#]],
@@ -858,7 +858,7 @@ fn main() {
             94..96 '{}': ()
             109..160 '{     ...10); }': ()
             119..120 's': S<i32>
-            123..124 'S': S<i32>() -> S<i32>
+            123..124 'S': extern "rust-call" S<i32>() -> S<i32>
             123..126 'S()': S<i32>
             132..133 's': S<i32>
             132..144 's.g(|_x| {})': ()
@@ -1689,18 +1689,18 @@ fn main() {
 }
         "#,
         expect![[r#"
-        27..85 '{     ...1,); }': ()
-        37..48 'S(.., a, b)': S
-        43..44 'a': usize
-        46..47 'b': {unknown}
-        51..52 'S': S(usize) -> S
-        51..55 'S(1)': S
-        53..54 '1': usize
-        65..75 '(.., a, b)': (i32, {unknown})
-        70..71 'a': i32
-        73..74 'b': {unknown}
-        78..82 '(1,)': (i32,)
-        79..80 '1': i32
+            27..85 '{     ...1,); }': ()
+            37..48 'S(.., a, b)': S
+            43..44 'a': usize
+            46..47 'b': {unknown}
+            51..52 'S': extern "rust-call" S(usize) -> S
+            51..55 'S(1)': S
+            53..54 '1': usize
+            65..75 '(.., a, b)': (i32, {unknown})
+            70..71 'a': i32
+            73..74 'b': {unknown}
+            78..82 '(1,)': (i32,)
+            79..80 '1': i32
         "#]],
     );
 }
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index f9ab6d8dc8d..84747822826 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -236,14 +236,14 @@ fn test() {
         expect![[r#"
             71..153 '{     ...a.c; }': ()
             81..82 'c': C
-            85..86 'C': C(usize) -> C
+            85..86 'C': extern "rust-call" C(usize) -> C
             85..89 'C(1)': C
             87..88 '1': usize
             95..96 'B': B
             106..107 'a': A
             113..132 'A { b:...C(1) }': A
             120..121 'B': B
-            126..127 'C': C(usize) -> C
+            126..127 'C': extern "rust-call" C(usize) -> C
             126..130 'C(1)': C
             128..129 '1': usize
             138..139 'a': A
@@ -303,14 +303,14 @@ unsafe fn baz(u: MyUnion) {
             71..89 'MyUnio...o: 0 }': MyUnion
             86..87 '0': u32
             95..113 'unsafe...(u); }': ()
-            104..107 'baz': fn baz(MyUnion)
+            104..107 'baz': unsafe fn baz(MyUnion)
             104..110 'baz(u)': ()
             108..109 'u': MyUnion
             122..123 'u': MyUnion
             126..146 'MyUnio... 0.0 }': MyUnion
             141..144 '0.0': f32
             152..170 'unsafe...(u); }': ()
-            161..164 'baz': fn baz(MyUnion)
+            161..164 'baz': unsafe fn baz(MyUnion)
             161..167 'baz(u)': ()
             165..166 'u': MyUnion
             188..189 'u': MyUnion
@@ -625,12 +625,12 @@ impl E {
             86..107 '{     ...     }': ()
             96..100 'Self': S1
             134..158 '{     ...     }': ()
-            144..148 'Self': S2(isize) -> S2
+            144..148 'Self': extern "rust-call" S2(isize) -> S2
             144..151 'Self(1)': S2
             149..150 '1': isize
             184..230 '{     ...     }': ()
             194..202 'Self::V1': E
-            212..220 'Self::V2': V2(u32) -> E
+            212..220 'Self::V2': extern "rust-call" V2(u32) -> E
             212..223 'Self::V2(1)': E
             221..222 '1': u32
         "#]],
@@ -856,11 +856,11 @@ fn test() {
             256..277 'A::foo...42))))': &i32
             263..276 '&&B(B(A(42)))': &&B<B<A<i32>>>
             264..276 '&B(B(A(42)))': &B<B<A<i32>>>
-            265..266 'B': B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>>
+            265..266 'B': extern "rust-call" B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>>
             265..276 'B(B(A(42)))': B<B<A<i32>>>
-            267..268 'B': B<A<i32>>(A<i32>) -> B<A<i32>>
+            267..268 'B': extern "rust-call" B<A<i32>>(A<i32>) -> B<A<i32>>
             267..275 'B(A(42))': B<A<i32>>
-            269..270 'A': A<i32>(i32) -> A<i32>
+            269..270 'A': extern "rust-call" A<i32>(i32) -> A<i32>
             269..274 'A(42)': A<i32>
             271..273 '42': i32
         "#]],
@@ -910,16 +910,16 @@ fn test(a: A<i32>) {
             253..254 'a': A<i32>
             264..310 '{     ...))); }': ()
             274..275 't': &i32
-            278..279 'A': A<i32>(*mut i32) -> A<i32>
+            278..279 'A': extern "rust-call" A<i32>(*mut i32) -> A<i32>
             278..292 'A(0 as *mut _)': A<i32>
             278..307 'A(0 as...B(a)))': &i32
             280..281 '0': i32
             280..291 '0 as *mut _': *mut i32
             297..306 '&&B(B(a))': &&B<B<A<i32>>>
             298..306 '&B(B(a))': &B<B<A<i32>>>
-            299..300 'B': B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>>
+            299..300 'B': extern "rust-call" B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>>
             299..306 'B(B(a))': B<B<A<i32>>>
-            301..302 'B': B<A<i32>>(A<i32>) -> B<A<i32>>
+            301..302 'B': extern "rust-call" B<A<i32>>(A<i32>) -> B<A<i32>>
             301..305 'B(a)': B<A<i32>>
             303..304 'a': A<i32>
         "#]],
@@ -1273,16 +1273,16 @@ fn infer_tuple_struct_generics() {
         "#,
         expect![[r#"
             75..183 '{     ...one; }': ()
-            81..82 'A': A<i32>(i32) -> A<i32>
+            81..82 'A': extern "rust-call" A<i32>(i32) -> A<i32>
             81..86 'A(42)': A<i32>
             83..85 '42': i32
-            92..93 'A': A<u128>(u128) -> A<u128>
+            92..93 'A': extern "rust-call" A<u128>(u128) -> A<u128>
             92..101 'A(42u128)': A<u128>
             94..100 '42u128': u128
-            107..111 'Some': Some<&str>(&str) -> Option<&str>
+            107..111 'Some': extern "rust-call" Some<&str>(&str) -> Option<&str>
             107..116 'Some("x")': Option<&str>
             112..115 '"x"': &str
-            122..134 'Option::Some': Some<&str>(&str) -> Option<&str>
+            122..134 'Option::Some': extern "rust-call" Some<&str>(&str) -> Option<&str>
             122..139 'Option...e("x")': Option<&str>
             135..138 '"x"': &str
             145..149 'None': Option<{unknown}>
@@ -1568,7 +1568,7 @@ fn infer_type_alias() {
             204..207 'z.y': i8
             298..362 '{     ... &e; }': ()
             308..309 'e': Enum
-            312..325 'm::Alias::Foo': Foo(u8) -> Enum
+            312..325 'm::Alias::Foo': extern "rust-call" Foo(u8) -> Enum
             312..328 'm::Ali...Foo(0)': Enum
             326..327 '0': u8
             338..354 'm::Ali...Foo(x)': Enum
@@ -2184,10 +2184,10 @@ fn main() {
             103..231 '{     ... }); }': ()
             109..161 'async ...     }': impl Future<Output = Result<(), ()>>
             125..139 'return Err(())': !
-            132..135 'Err': Err<(), ()>(()) -> Result<(), ()>
+            132..135 'Err': extern "rust-call" Err<(), ()>(()) -> Result<(), ()>
             132..139 'Err(())': Result<(), ()>
             136..138 '()': ()
-            149..151 'Ok': Ok<(), ()>(()) -> Result<(), ()>
+            149..151 'Ok': extern "rust-call" Ok<(), ()>(()) -> Result<(), ()>
             149..155 'Ok(())': Result<(), ()>
             152..154 '()': ()
             167..171 'test': fn test<(), (), impl Fn() -> impl Future<Output = Result<(), ()>>, impl Future<Output = Result<(), ()>>>(impl Fn() -> impl Future<Output = Result<(), ()>>)
@@ -2195,10 +2195,10 @@ fn main() {
             172..227 '|| asy...     }': impl Fn() -> impl Future<Output = Result<(), ()>>
             175..227 'async ...     }': impl Future<Output = Result<(), ()>>
             191..205 'return Err(())': !
-            198..201 'Err': Err<(), ()>(()) -> Result<(), ()>
+            198..201 'Err': extern "rust-call" Err<(), ()>(()) -> Result<(), ()>
             198..205 'Err(())': Result<(), ()>
             202..204 '()': ()
-            215..217 'Ok': Ok<(), ()>(()) -> Result<(), ()>
+            215..217 'Ok': extern "rust-call" Ok<(), ()>(()) -> Result<(), ()>
             215..221 'Ok(())': Result<(), ()>
             218..220 '()': ()
         "#]],
@@ -2227,7 +2227,7 @@ fn infer_generic_from_later_assignment() {
             94..127 '{     ...     }': ()
             104..107 'end': Option<bool>
             104..120 'end = ...(true)': ()
-            110..114 'Some': Some<bool>(bool) -> Option<bool>
+            110..114 'Some': extern "rust-call" Some<bool>(bool) -> Option<bool>
             110..120 'Some(true)': Option<bool>
             115..119 'true': bool
         "#]],
@@ -2262,7 +2262,7 @@ fn infer_loop_break_with_val() {
             111..121 'break None': !
             117..121 'None': Option<bool>
             142..158 'break ...(true)': !
-            148..152 'Some': Some<bool>(bool) -> Option<bool>
+            148..152 'Some': extern "rust-call" Some<bool>(bool) -> Option<bool>
             148..158 'Some(true)': Option<bool>
             153..157 'true': bool
         "#]],
@@ -2509,7 +2509,7 @@ fn generic_default_in_struct_literal() {
             254..281 'OtherT...1i32 }': OtherThing<i32>
             275..279 '1i32': i32
             291..292 'b': OtherThing<i32>
-            295..310 'OtherThing::Two': Two<i32>(i32) -> OtherThing<i32>
+            295..310 'OtherThing::Two': extern "rust-call" Two<i32>(i32) -> OtherThing<i32>
             295..316 'OtherT...(1i32)': OtherThing<i32>
             311..315 '1i32': i32
         "#]],
@@ -2984,7 +2984,7 @@ fn f() {
         expect![[r#"
             72..166 '{     ...   } }': ()
             78..164 'match ...     }': ()
-            84..92 'Foo::Bar': Bar(i32) -> Foo
+            84..92 'Foo::Bar': extern "rust-call" Bar(i32) -> Foo
             84..95 'Foo::Bar(3)': Foo
             93..94 '3': i32
             106..119 'Qux::Bar(bar)': Foo
@@ -3043,9 +3043,9 @@ fn main() {
             322..324 '{}': Foo<T>
             338..559 '{     ...r(); }': ()
             348..353 'boxed': Box<Foo<i32>>
-            356..359 'Box': Box<Foo<i32>>(Foo<i32>) -> Box<Foo<i32>>
+            356..359 'Box': extern "rust-call" Box<Foo<i32>>(Foo<i32>) -> Box<Foo<i32>>
             356..371 'Box(Foo(0_i32))': Box<Foo<i32>>
-            360..363 'Foo': Foo<i32>(i32) -> Foo<i32>
+            360..363 'Foo': extern "rust-call" Foo<i32>(i32) -> Foo<i32>
             360..370 'Foo(0_i32)': Foo<i32>
             364..369 '0_i32': i32
             382..386 'bad1': &i32
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index d270328605a..e4756ee9e29 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -522,7 +522,7 @@ fn test() -> u64 {
         expect![[r#"
             37..86 '{     ... a.1 }': u64
             47..48 'a': S
-            51..52 'S': S(i32, u64) -> S
+            51..52 'S': extern "rust-call" S(i32, u64) -> S
             51..58 'S(4, 6)': S
             53..54 '4': i32
             56..57 '6': u64
@@ -548,7 +548,7 @@ fn test() -> u64 {
         expect![[r#"
             43..108 '{     ...0(2) }': u64
             53..54 'a': S
-            57..58 'S': S(fn(u32) -> u64) -> S
+            57..58 'S': extern "rust-call" S(fn(u32) -> u64) -> S
             57..74 'S(|i| ...s u64)': S
             59..73 '|i| 2*i as u64': impl Fn(u32) -> u64
             60..61 'i': u32
@@ -1026,7 +1026,7 @@ fn test(x: impl Trait<u64>, y: &impl Trait<u32>) {
             201..202 'x': impl Trait<u64>
             208..209 'y': &impl Trait<u32>
             219..220 'z': S<u16>
-            223..224 'S': S<u16>(u16) -> S<u16>
+            223..224 'S': extern "rust-call" S<u16>(u16) -> S<u16>
             223..227 'S(1)': S<u16>
             225..226 '1': u16
             233..236 'bar': fn bar(S<u16>)
@@ -1339,7 +1339,7 @@ fn foo<const C: u8, T>() -> (impl FnOnce(&str, T), impl Trait<u8>) {
             142..147 'input': &str
             149..150 't': T
             152..154 '{}': ()
-            156..159 'Bar': Bar<u8>(u8) -> Bar<u8>
+            156..159 'Bar': extern "rust-call" Bar<u8>(u8) -> Bar<u8>
             156..162 'Bar(C)': Bar<u8>
             160..161 'C': u8
         "#]],
@@ -1958,7 +1958,7 @@ fn test() {
             118..120 '{}': ()
             136..255 '{     ... 1); }': ()
             146..147 'x': Option<u32>
-            150..162 'Option::Some': Some<u32>(u32) -> Option<u32>
+            150..162 'Option::Some': extern "rust-call" Some<u32>(u32) -> Option<u32>
             150..168 'Option...(1u32)': Option<u32>
             163..167 '1u32': u32
             174..175 'x': Option<u32>
@@ -2514,7 +2514,7 @@ fn test() -> impl Trait<i32> {
             178..180 '{}': ()
             213..309 '{     ...t()) }': S<i32>
             223..225 's1': S<u32>
-            228..229 'S': S<u32>(u32) -> S<u32>
+            228..229 'S': extern "rust-call" S<u32>(u32) -> S<u32>
             228..240 'S(default())': S<u32>
             230..237 'default': fn default<u32>() -> u32
             230..239 'default()': u32
@@ -2524,11 +2524,11 @@ fn test() -> impl Trait<i32> {
             263..264 'x': i32
             272..275 'bar': fn bar<i32>(S<i32>) -> i32
             272..289 'bar(S(...lt()))': i32
-            276..277 'S': S<i32>(i32) -> S<i32>
+            276..277 'S': extern "rust-call" S<i32>(i32) -> S<i32>
             276..288 'S(default())': S<i32>
             278..285 'default': fn default<i32>() -> i32
             278..287 'default()': i32
-            295..296 'S': S<i32>(i32) -> S<i32>
+            295..296 'S': extern "rust-call" S<i32>(i32) -> S<i32>
             295..307 'S(default())': S<i32>
             297..304 'default': fn default<i32>() -> i32
             297..306 'default()': i32
@@ -2758,7 +2758,7 @@ fn main() {
             1036..1041 'x > 0': bool
             1040..1041 '0': i32
             1042..1060 '{ Some...u32) }': Option<u32>
-            1044..1048 'Some': Some<u32>(u32) -> Option<u32>
+            1044..1048 'Some': extern "rust-call" Some<u32>(u32) -> Option<u32>
             1044..1058 'Some(x as u32)': Option<u32>
             1049..1050 'x': i32
             1049..1057 'x as u32': u32
@@ -2894,9 +2894,9 @@ fn test() {
             175..185 'foo.test()': bool
             191..194 'bar': fn bar<{unknown}>({unknown}) -> {unknown}
             191..201 'bar.test()': bool
-            207..213 'Struct': Struct(usize) -> Struct
+            207..213 'Struct': extern "rust-call" Struct(usize) -> Struct
             207..220 'Struct.test()': bool
-            226..239 'Enum::Variant': Variant(usize) -> Enum
+            226..239 'Enum::Variant': extern "rust-call" Variant(usize) -> Enum
             226..246 'Enum::...test()': bool
         "#]],
     );
@@ -3475,12 +3475,12 @@ fn main(){
             95..99 'self': Wrapper
             101..104 'rhs': u32
             122..150 '{     ...     }': Wrapper
-            132..139 'Wrapper': Wrapper(u32) -> Wrapper
+            132..139 'Wrapper': extern "rust-call" Wrapper(u32) -> Wrapper
             132..144 'Wrapper(rhs)': Wrapper
             140..143 'rhs': u32
             162..248 '{     ...um;  }': ()
             172..179 'wrapped': Wrapper
-            182..189 'Wrapper': Wrapper(u32) -> Wrapper
+            182..189 'Wrapper': extern "rust-call" Wrapper(u32) -> Wrapper
             182..193 'Wrapper(10)': Wrapper
             190..192 '10': u32
             203..206 'num': u32
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index f7c7327776f..ce01ee1c359 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -53,6 +53,12 @@ fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> {
     }
 }
 
+impl ast::Abi {
+    pub fn abi_string(&self) -> Option<ast::String> {
+        support::token(&self.syntax, SyntaxKind::STRING).and_then(ast::String::cast)
+    }
+}
+
 impl ast::HasModuleItem for ast::StmtList {}
 
 impl ast::BlockExpr {