Move linkage type check to HIR analysis and fix semantics issues.

This ensures that the error is printed even for unused variables,
as well as unifying the handling between the LLVM and GCC backends.

This also fixes unusual behavior around exported Rust-defined variables
with linkage attributes. With the previous behavior, it appears to be
impossible to define such a variable such that it can actually be imported
and used by another crate. This is because on the importing side, the
variable is required to be a pointer, but on the exporting side, the
type checker rejects static variables of pointer type because they do
not implement `Sync`. Even if it were possible to import such a type, it
appears that code generation on the importing side would add an unexpected
additional level of pointer indirection, which would break type safety.

This highlighted that the semantics of linkage on Rust-defined variables
is different to linkage on foreign items. As such, we now model the
difference with two different codegen attributes: linkage for Rust-defined
variables, and import_linkage for foreign items.

This change gives semantics to the test
src/test/ui/linkage-attr/auxiliary/def_illtyped_external.rs which was
previously expected to fail to compile. Therefore, convert it into a
test that is expected to successfully compile.

The update to the GCC backend is speculative and untested.
This commit is contained in:
Peter Collingbourne 2022-11-23 18:13:30 -08:00
parent e1d819583f
commit 5873ebeef3
19 changed files with 56 additions and 85 deletions

View file

@ -8,13 +8,11 @@ use rustc_middle::mir::mono::MonoItem;
use rustc_middle::ty::{self, Instance, Ty};
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::mir::interpret::{self, ConstAllocation, ErrorHandled, Scalar as InterpScalar, read_target_uint};
use rustc_span::Span;
use rustc_span::def_id::DefId;
use rustc_target::abi::{self, Align, HasDataLayout, Primitive, Size, WrappingRange};
use crate::base;
use crate::context::CodegenCx;
use crate::errors::LinkageConstOrMutType;
use crate::type_of::LayoutGccExt;
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
@ -239,12 +237,12 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
}
Node::ForeignItem(&hir::ForeignItem {
span,
span: _,
kind: hir::ForeignItemKind::Static(..),
..
}) => {
let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
check_and_apply_linkage(&self, &fn_attrs, ty, sym, span)
check_and_apply_linkage(&self, &fn_attrs, ty, sym)
}
item => bug!("get_static: expected static, found {:?}", item),
@ -257,8 +255,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
//debug!("get_static: sym={} item_attr={:?}", sym, self.tcx.item_attrs(def_id));
let attrs = self.tcx.codegen_fn_attrs(def_id);
let span = self.tcx.def_span(def_id);
let global = check_and_apply_linkage(&self, &attrs, ty, sym, span);
let global = check_and_apply_linkage(&self, &attrs, ty, sym);
let needs_dll_storage_attr = false; // TODO(antoyo)
@ -355,24 +352,12 @@ pub fn codegen_static_initializer<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, def_id
Ok((const_alloc_to_gcc(cx, alloc), alloc))
}
fn check_and_apply_linkage<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, attrs: &CodegenFnAttrs, ty: Ty<'tcx>, sym: &str, span: Span) -> LValue<'gcc> {
fn check_and_apply_linkage<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, attrs: &CodegenFnAttrs, ty: Ty<'tcx>, sym: &str) -> LValue<'gcc> {
let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL);
let llty = cx.layout_of(ty).gcc_type(cx, true);
if let Some(linkage) = attrs.linkage {
// If this is a static with a linkage specified, then we need to handle
// it a little specially. The typesystem prevents things like &T and
// extern "C" fn() from being non-null, so we can't just declare a
// static and call it a day. Some linkages (like weak) will make it such
// that the static actually has a null value.
let llty2 =
if let ty::RawPtr(ref mt) = ty.kind() {
cx.layout_of(mt.ty).gcc_type(cx, true)
}
else {
cx.sess().emit_fatal(LinkageConstOrMutType { span: span })
};
if let Some(linkage) = attrs.import_linkage {
// Declare a symbol `foo` with the desired linkage.
let global1 = cx.declare_global_with_linkage(&sym, llty2, base::global_linkage_to_gcc(linkage));
let global1 = cx.declare_global_with_linkage(&sym, cx.type_i8(), base::global_linkage_to_gcc(linkage));
// Declare an internal global `extern_with_linkage_foo` which
// is initialized with the address of `foo`. If `foo` is

View file

@ -211,13 +211,6 @@ pub(crate) struct InvalidMonomorphizationUnsupportedOperation<'a> {
pub in_elem: Ty<'a>,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_linkage_const_or_mut_type)]
pub(crate) struct LinkageConstOrMutType {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_lto_not_supported)]
pub(crate) struct LTONotSupported;

View file

@ -1,7 +1,7 @@
use crate::base;
use crate::common::{self, CodegenCx};
use crate::debuginfo;
use crate::errors::{InvalidMinimumAlignment, LinkageConstOrMutType, SymbolAlreadyDefined};
use crate::errors::{InvalidMinimumAlignment, SymbolAlreadyDefined};
use crate::llvm::{self, True};
use crate::llvm_util;
use crate::type_::Type;
@ -162,22 +162,12 @@ fn check_and_apply_linkage<'ll, 'tcx>(
def_id: DefId,
) -> &'ll Value {
let llty = cx.layout_of(ty).llvm_type(cx);
if let Some(linkage) = attrs.linkage {
if let Some(linkage) = attrs.import_linkage {
debug!("get_static: sym={} linkage={:?}", sym, linkage);
// If this is a static with a linkage specified, then we need to handle
// it a little specially. The typesystem prevents things like &T and
// extern "C" fn() from being non-null, so we can't just declare a
// static and call it a day. Some linkages (like weak) will make it such
// that the static actually has a null value.
let llty2 = if let ty::RawPtr(ref mt) = ty.kind() {
cx.layout_of(mt.ty).llvm_type(cx)
} else {
cx.sess().emit_fatal(LinkageConstOrMutType { span: cx.tcx.def_span(def_id) })
};
unsafe {
// Declare a symbol `foo` with the desired linkage.
let g1 = cx.declare_global(sym, llty2);
let g1 = cx.declare_global(sym, cx.type_i8());
llvm::LLVMRustSetLinkage(g1, base::linkage_to_llvm(linkage));
// Declare an internal global `extern_with_linkage_foo` which
@ -195,7 +185,7 @@ fn check_and_apply_linkage<'ll, 'tcx>(
})
});
llvm::LLVMRustSetLinkage(g2, llvm::Linkage::InternalLinkage);
llvm::LLVMSetInitializer(g2, g1);
llvm::LLVMSetInitializer(g2, cx.const_ptrcast(g1, llty));
g2
}
} else if cx.tcx.sess.target.arch == "x86" &&

View file

@ -61,13 +61,6 @@ pub(crate) struct InvalidMinimumAlignment {
pub err: String,
}
#[derive(Diagnostic)]
#[diag(codegen_llvm_linkage_const_or_mut_type)]
pub(crate) struct LinkageConstOrMutType {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_llvm_sanitizer_memtag_requires_mte)]
pub(crate) struct SanitizerMemtagRequiresMte;

View file

@ -1,6 +1,3 @@
codegen_gcc_linkage_const_or_mut_type =
must have type `*const T` or `*mut T` due to `#[linkage]` attribute
codegen_gcc_unwinding_inline_asm =
GCC backend does not support unwinding from inline asm

View file

@ -23,9 +23,6 @@ codegen_llvm_branch_protection_requires_aarch64 =
codegen_llvm_invalid_minimum_alignment =
invalid minimum global alignment: {$err}
codegen_llvm_linkage_const_or_mut_type =
must have type `*const T` or `*mut T` due to `#[linkage]` attribute
codegen_llvm_sanitizer_memtag_requires_mte =
`-Zsanitizer=memtag` requires `-Ctarget-feature=+mte`

View file

@ -113,3 +113,6 @@ hir_analysis_const_bound_for_non_const_trait =
hir_analysis_self_in_impl_self =
`Self` is not valid in the self type of an impl block
.note = replace `Self` with a different type
hir_analysis_linkage_type =
must have type `*const T` or `*mut T` due to `#[linkage]` attribute

View file

@ -1,4 +1,5 @@
use crate::check::intrinsicck::InlineAsmCtxt;
use crate::errors::LinkageType;
use super::compare_method::check_type_bounds;
use super::compare_method::{compare_impl_method, compare_ty_impl};
@ -478,6 +479,17 @@ fn check_opaque_meets_bounds<'tcx>(
let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
}
fn check_static_linkage<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
if tcx.codegen_fn_attrs(def_id).import_linkage.is_some() {
if match tcx.type_of(def_id).kind() {
ty::RawPtr(_) => false,
_ => true,
} {
tcx.sess.emit_err(LinkageType { span: tcx.def_span(def_id) });
}
}
}
fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
debug!(
"check_item_type(it.def_id={:?}, it.name={})",
@ -490,6 +502,7 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
tcx.ensure().typeck(id.owner_id.def_id);
maybe_check_static_with_link_section(tcx, id.owner_id.def_id);
check_static_inhabited(tcx, id.owner_id.def_id);
check_static_linkage(tcx, id.owner_id.def_id);
}
DefKind::Const => {
tcx.ensure().typeck(id.owner_id.def_id);
@ -627,6 +640,7 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
}
hir::ForeignItemKind::Static(..) => {
check_static_inhabited(tcx, def_id);
check_static_linkage(tcx, def_id);
}
_ => {}
}

View file

@ -1814,7 +1814,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
);
} else if attr.has_name(sym::linkage) {
if let Some(val) = attr.value_str() {
codegen_fn_attrs.linkage = Some(linkage_by_name(tcx, did, val.as_str()));
let linkage = Some(linkage_by_name(tcx, did, val.as_str()));
if tcx.is_foreign_item(did) {
codegen_fn_attrs.import_linkage = linkage;
} else {
codegen_fn_attrs.linkage = linkage;
}
}
} else if attr.has_name(sym::link_section) {
if let Some(val) = attr.value_str() {

View file

@ -285,3 +285,10 @@ pub struct SelfInImplSelf {
#[note]
pub note: (),
}
#[derive(Diagnostic)]
#[diag(hir_analysis_linkage_type)]
pub(crate) struct LinkageType {
#[primary_span]
pub span: Span,
}

View file

@ -26,8 +26,10 @@ pub struct CodegenFnAttrs {
/// The `#[target_feature(enable = "...")]` attribute and the enabled
/// features (only enabled features are supported right now).
pub target_features: Vec<Symbol>,
/// The `#[linkage = "..."]` attribute and the value we found.
/// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found.
pub linkage: Option<Linkage>,
/// The `#[linkage = "..."]` attribute on foreign items and the value we found.
pub import_linkage: Option<Linkage>,
/// The `#[link_section = "..."]` attribute, or what executable section this
/// should be placed in.
pub link_section: Option<Symbol>,
@ -113,6 +115,7 @@ impl CodegenFnAttrs {
link_ordinal: None,
target_features: vec![],
linkage: None,
import_linkage: None,
link_section: None,
no_sanitize: SanitizerSet::empty(),
instruction_set: None,

View file

@ -1,5 +1,5 @@
extern "C" {
#[linkage = "extern_weak"] static foo: isize;
#[linkage = "extern_weak"] static foo: *mut isize;
//~^ ERROR: the `linkage` attribute is experimental and not portable
}

View file

@ -1,7 +1,7 @@
error[E0658]: the `linkage` attribute is experimental and not portable across platforms
--> $DIR/feature-gate-linkage.rs:2:5
|
LL | #[linkage = "extern_weak"] static foo: isize;
LL | #[linkage = "extern_weak"] static foo: *mut isize;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #29603 <https://github.com/rust-lang/rust/issues/29603> for more information

View file

@ -0,0 +1,8 @@
// build-pass
// aux-build:def_external.rs
extern crate def_external as dep;
fn main() {
println!("{:p}", &dep::EXTERN);
}

View file

@ -1,11 +0,0 @@
// rust-lang/rust#59548: We used to ICE when trying to use a static
// with a type that violated its own `#[linkage]`.
// build-fail
// aux-build:def_illtyped_external.rs
extern crate def_illtyped_external as dep;
fn main() {
println!("{:p}", &dep::EXTERN);
}

View file

@ -1,8 +0,0 @@
error: must have type `*const T` or `*mut T` due to `#[linkage]` attribute
--> $DIR/auxiliary/def_illtyped_external.rs:5:1
|
LL | pub static EXTERN: u32 = 0;
| ^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error

View file

@ -1,9 +1,4 @@
// FIXME https://github.com/rust-lang/rust/issues/59774
// build-fail
// normalize-stderr-test "thread.*panicked.*Metadata module not compiled.*\n" -> ""
// normalize-stderr-test "note:.*RUST_BACKTRACE=1.*\n" -> ""
// ignore-sgx no weak linkages permitted
// check-fail
#![feature(linkage)]

View file

@ -1,5 +1,5 @@
error: must have type `*const T` or `*mut T` due to `#[linkage]` attribute
--> $DIR/linkage2.rs:12:5
--> $DIR/linkage2.rs:7:5
|
LL | static foo: i32;
| ^^^^^^^^^^^^^^^