diff --git a/.travis.yml b/.travis.yml index ea405413e78f4..88bd901e6b33d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,9 +12,10 @@ matrix: fast_finish: true include: # Linux builders, all docker images - - env: IMAGE=android DEPLOY=1 + - env: IMAGE=arm-android - env: IMAGE=cross DEPLOY=1 - env: IMAGE=linux-tested-targets DEPLOY=1 + - env: IMAGE=dist-android DEPLOY=1 - env: IMAGE=dist-arm-linux DEPLOY=1 - env: IMAGE=dist-armv7-aarch64-linux DEPLOY=1 - env: IMAGE=dist-freebsd DEPLOY=1 diff --git a/mk/main.mk b/mk/main.mk index 5230e32656e15..1563acfc29458 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -18,7 +18,7 @@ CFG_RELEASE_NUM=1.16.0 # An optional number to put after the label, e.g. '.2' -> '-beta.2' # NB Make sure it starts with a dot to conform to semver pre-release # versions (section 9) -CFG_PRERELEASE_VERSION=.2 +CFG_PRERELEASE_VERSION=.3 ifeq ($(CFG_RELEASE_CHANNEL),stable) # This is the normal semver version string, e.g. "0.12.0", "0.12.0-nightly" diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 85e8dbce1a955..bc8341102421b 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -438,14 +438,14 @@ def main(): rb.use_vendored_sources = '\nvendor = true' in rb.config_toml or \ 'CFG_ENABLE_VENDOR' in rb.config_mk - if 'SUDO_USER' in os.environ: - if os.environ['USER'] != os.environ['SUDO_USER']: + if 'SUDO_USER' in os.environ and not rb.use_vendored_sources: + if os.environ.get('USER') != os.environ['SUDO_USER']: rb.use_vendored_sources = True print('info: looks like you are running this command under `sudo`') print(' and so in order to preserve your $HOME this will now') print(' use vendored sources by default. Note that if this') print(' does not work you should run a normal build first') - print(' before running a command like `sudo make intall`') + print(' before running a command like `sudo make install`') if rb.use_vendored_sources: if not os.path.exists('.cargo'): diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index 8e79c2d27d195..bc439d6f7826d 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -198,10 +198,6 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake "); } } - - if target.contains("arm-linux-android") { - need_cmd("adb".as_ref()); - } } for host in build.flags.host.iter() { diff --git a/src/ci/docker/arm-android/Dockerfile b/src/ci/docker/arm-android/Dockerfile new file mode 100644 index 0000000000000..4c89ce12531b4 --- /dev/null +++ b/src/ci/docker/arm-android/Dockerfile @@ -0,0 +1,46 @@ +FROM ubuntu:16.04 + +RUN dpkg --add-architecture i386 && \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + g++ \ + make \ + file \ + curl \ + ca-certificates \ + python2.7 \ + git \ + cmake \ + unzip \ + expect \ + openjdk-9-jre \ + sudo \ + libstdc++6:i386 \ + xz-utils \ + libssl-dev \ + pkg-config + +WORKDIR /android/ +ENV PATH=$PATH:/android/ndk-arm-9/bin:/android/sdk/tools:/android/sdk/platform-tools + +COPY install-ndk.sh install-sdk.sh accept-licenses.sh /android/ +RUN sh /android/install-ndk.sh +RUN sh /android/install-sdk.sh + +RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \ + dpkg -i dumb-init_*.deb && \ + rm dumb-init_*.deb + +COPY start-emulator.sh /android/ + +ENTRYPOINT ["/usr/bin/dumb-init", "--", "/android/start-emulator.sh"] + +RUN curl -o /usr/local/bin/sccache \ + https://s3.amazonaws.com/rust-lang-ci/rust-ci-mirror/2017-02-24-sccache-x86_64-unknown-linux-gnu && \ + chmod +x /usr/local/bin/sccache + +ENV RUST_CONFIGURE_ARGS \ + --target=arm-linux-androideabi \ + --arm-linux-androideabi-ndk=/android/ndk-arm-9 + +ENV SCRIPT python2.7 ../x.py test --target arm-linux-androideabi diff --git a/src/ci/docker/android/accept-licenses.sh b/src/ci/docker/arm-android/accept-licenses.sh similarity index 100% rename from src/ci/docker/android/accept-licenses.sh rename to src/ci/docker/arm-android/accept-licenses.sh diff --git a/src/ci/docker/arm-android/install-ndk.sh b/src/ci/docker/arm-android/install-ndk.sh new file mode 100644 index 0000000000000..389ec062110e0 --- /dev/null +++ b/src/ci/docker/arm-android/install-ndk.sh @@ -0,0 +1,33 @@ +#!/bin/sh +# Copyright 2016 The Rust Project Developers. See the COPYRIGHT +# file at the top-level directory of this distribution and at +# http://rust-lang.org/COPYRIGHT. +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +set -ex + +cpgdb() { + cp android-ndk-r11c/prebuilt/linux-x86_64/bin/gdb /android/$1/bin/$2-gdb + cp android-ndk-r11c/prebuilt/linux-x86_64/bin/gdb-orig /android/$1/bin/gdb-orig + cp -r android-ndk-r11c/prebuilt/linux-x86_64/share /android/$1/share +} + +# Prep the Android NDK +# +# See https://github.com/servo/servo/wiki/Building-for-Android +curl -O https://dl.google.com/android/repository/android-ndk-r11c-linux-x86_64.zip +unzip -q android-ndk-r11c-linux-x86_64.zip +bash android-ndk-r11c/build/tools/make-standalone-toolchain.sh \ + --platform=android-9 \ + --toolchain=arm-linux-androideabi-4.9 \ + --install-dir=/android/ndk-arm-9 \ + --ndk-dir=/android/android-ndk-r11c \ + --arch=arm +cpgdb ndk-arm-9 arm-linux-androideabi + +rm -rf ./android-ndk-r11c-linux-x86_64.zip ./android-ndk-r11c diff --git a/src/ci/docker/android/install-sdk.sh b/src/ci/docker/arm-android/install-sdk.sh similarity index 100% rename from src/ci/docker/android/install-sdk.sh rename to src/ci/docker/arm-android/install-sdk.sh diff --git a/src/ci/docker/android/start-emulator.sh b/src/ci/docker/arm-android/start-emulator.sh similarity index 100% rename from src/ci/docker/android/start-emulator.sh rename to src/ci/docker/arm-android/start-emulator.sh diff --git a/src/ci/docker/android/Dockerfile b/src/ci/docker/dist-android/Dockerfile similarity index 75% rename from src/ci/docker/android/Dockerfile rename to src/ci/docker/dist-android/Dockerfile index e3748af501fb8..739cd6196e0b1 100644 --- a/src/ci/docker/android/Dockerfile +++ b/src/ci/docker/dist-android/Dockerfile @@ -21,17 +21,13 @@ RUN dpkg --add-architecture i386 && \ WORKDIR /android/ ENV PATH=$PATH:/android/ndk-arm-9/bin:/android/sdk/tools:/android/sdk/platform-tools -COPY install-ndk.sh install-sdk.sh accept-licenses.sh /android/ +COPY install-ndk.sh /android/ RUN sh /android/install-ndk.sh -RUN sh /android/install-sdk.sh RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \ dpkg -i dumb-init_*.deb && \ rm dumb-init_*.deb - -COPY start-emulator.sh /android/ - -ENTRYPOINT ["/usr/bin/dumb-init", "--", "/android/start-emulator.sh"] +ENTRYPOINT ["/usr/bin/dumb-init", "--"] ENV SCCACHE_DIGEST=7237e38e029342fa27b7ac25412cb9d52554008b12389727320bd533fd7f05b6a96d55485f305caf95e5c8f5f97c3313e10012ccad3e752aba2518f3522ba783 RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | \ @@ -49,8 +45,4 @@ ENV RUST_CONFIGURE_ARGS \ --i686-linux-android-ndk=/android/ndk-x86-9 \ --aarch64-linux-android-ndk=/android/ndk-aarch64 -# Just a smoke test in dist to see if this works for now, we should expand this -# to all the targets above eventually. -ENV SCRIPT \ - python2.7 ../x.py test --target arm-linux-androideabi && \ - python2.7 ../x.py dist --target $TARGETS +ENV SCRIPT python2.7 ../x.py dist --target $TARGETS diff --git a/src/ci/docker/android/install-ndk.sh b/src/ci/docker/dist-android/install-ndk.sh similarity index 84% rename from src/ci/docker/android/install-ndk.sh rename to src/ci/docker/dist-android/install-ndk.sh index 418ce69c5b1e5..19c1b94e784c8 100644 --- a/src/ci/docker/android/install-ndk.sh +++ b/src/ci/docker/dist-android/install-ndk.sh @@ -11,12 +11,6 @@ set -ex -cpgdb() { - cp android-ndk-r11c/prebuilt/linux-x86_64/bin/gdb /android/$1/bin/$2-gdb - cp android-ndk-r11c/prebuilt/linux-x86_64/bin/gdb-orig /android/$1/bin/gdb-orig - cp -r android-ndk-r11c/prebuilt/linux-x86_64/share /android/$1/share -} - # Prep the Android NDK # # See https://github.com/servo/servo/wiki/Building-for-Android @@ -28,7 +22,6 @@ bash android-ndk-r11c/build/tools/make-standalone-toolchain.sh \ --install-dir=/android/ndk-arm-9 \ --ndk-dir=/android/android-ndk-r11c \ --arch=arm -cpgdb ndk-arm-9 arm-linux-androideabi bash android-ndk-r11c/build/tools/make-standalone-toolchain.sh \ --platform=android-21 \ --toolchain=aarch64-linux-android-4.9 \ diff --git a/src/librustc/infer/error_reporting.rs b/src/librustc/infer/error_reporting.rs index 2489a6a6c7a63..1fb5a5d69719c 100644 --- a/src/librustc/infer/error_reporting.rs +++ b/src/librustc/infer/error_reporting.rs @@ -65,9 +65,6 @@ use super::region_inference::ConcreteFailure; use super::region_inference::SubSupConflict; use super::region_inference::GenericBoundFailure; use super::region_inference::GenericKind; -use super::region_inference::ProcessedErrors; -use super::region_inference::ProcessedErrorOrigin; -use super::region_inference::SameRegions; use hir::map as hir_map; use hir; @@ -78,12 +75,12 @@ use infer; use middle::region; use traits::{ObligationCause, ObligationCauseCode}; use ty::{self, TyCtxt, TypeFoldable}; -use ty::{Region, ReFree}; +use ty::Region; use ty::error::TypeError; use std::fmt; -use syntax::ast; use syntax_pos::{Pos, Span}; +use syntax::ast; use errors::DiagnosticBuilder; impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { @@ -256,8 +253,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // try to pre-process the errors, which will group some of them // together into a `ProcessedErrors` group: - let processed_errors = self.process_errors(errors); - let errors = processed_errors.as_ref().unwrap_or(errors); + let errors = self.process_errors(errors); debug!("report_region_errors: {} errors after preprocessing", errors.len()); @@ -279,13 +275,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { sub_origin, sub_r, sup_origin, sup_r); } - - ProcessedErrors(ref origins, - ref same_regions) => { - if !same_regions.is_empty() { - self.report_processed_errors(origins); - } - } } } } @@ -301,202 +290,31 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // duplicates that will be unhelpful to the end-user. But // obviously it never weeds out ALL errors. fn process_errors(&self, errors: &Vec>) - -> Option>> { + -> Vec> { debug!("process_errors()"); - let mut origins = Vec::new(); - - // we collect up ConcreteFailures and SubSupConflicts that are - // relating free-regions bound on the fn-header and group them - // together into this vector - let mut same_regions = Vec::new(); - - // here we put errors that we will not be able to process nicely - let mut other_errors = Vec::new(); - - // we collect up GenericBoundFailures in here. - let mut bound_failures = Vec::new(); - - for error in errors { - // Check whether we can process this error into some other - // form; if not, fall through. - match *error { - ConcreteFailure(ref origin, sub, sup) => { - debug!("processing ConcreteFailure"); - if let SubregionOrigin::CompareImplMethodObligation { .. } = *origin { - // When comparing an impl method against a - // trait method, it is not helpful to suggest - // changes to the impl method. This is - // because the impl method signature is being - // checked using the trait's environment, so - // usually the changes we suggest would - // actually have to be applied to the *trait* - // method (and it's not clear that the trait - // method is even under the user's control). - } else if let Some(same_frs) = free_regions_from_same_fn(self.tcx, sub, sup) { - origins.push( - ProcessedErrorOrigin::ConcreteFailure( - origin.clone(), - sub, - sup)); - append_to_same_regions(&mut same_regions, &same_frs); - continue; - } - } - SubSupConflict(ref var_origin, ref sub_origin, sub, ref sup_origin, sup) => { - debug!("processing SubSupConflict sub: {:?} sup: {:?}", sub, sup); - match (sub_origin, sup_origin) { - (&SubregionOrigin::CompareImplMethodObligation { .. }, _) => { - // As above, when comparing an impl method - // against a trait method, it is not helpful - // to suggest changes to the impl method. - } - (_, &SubregionOrigin::CompareImplMethodObligation { .. }) => { - // See above. - } - _ => { - if let Some(same_frs) = free_regions_from_same_fn(self.tcx, sub, sup) { - origins.push( - ProcessedErrorOrigin::VariableFailure( - var_origin.clone())); - append_to_same_regions(&mut same_regions, &same_frs); - continue; - } - } - } - } - GenericBoundFailure(ref origin, ref kind, region) => { - bound_failures.push((origin.clone(), kind.clone(), region)); - continue; - } - ProcessedErrors(..) => { - bug!("should not encounter a `ProcessedErrors` yet: {:?}", error) - } - } - - // No changes to this error. - other_errors.push(error.clone()); - } - - // ok, let's pull together the errors, sorted in an order that - // we think will help user the best - let mut processed_errors = vec![]; - - // first, put the processed errors, if any - if !same_regions.is_empty() { - let common_scope_id = same_regions[0].scope_id; - for sr in &same_regions { - // Since ProcessedErrors is used to reconstruct the function - // declaration, we want to make sure that they are, in fact, - // from the same scope - if sr.scope_id != common_scope_id { - debug!("returning empty result from process_errors because - {} != {}", sr.scope_id, common_scope_id); - return None; - } - } - assert!(origins.len() > 0); - let pe = ProcessedErrors(origins, same_regions); - debug!("errors processed: {:?}", pe); - processed_errors.push(pe); - } - - // next, put the other misc errors - processed_errors.extend(other_errors); - - // finally, put the `T: 'a` errors, but only if there were no - // other errors. otherwise, these have a very high rate of - // being unhelpful in practice. This is because they are - // basically secondary checks that test the state of the - // region graph after the rest of inference is done, and the - // other kinds of errors indicate that the region constraint - // graph is internally inconsistent, so these test results are - // likely to be meaningless. - if processed_errors.is_empty() { - for (origin, kind, region) in bound_failures { - processed_errors.push(GenericBoundFailure(origin, kind, region)); - } - } - - // we should always wind up with SOME errors, unless there were no - // errors to start - assert!(if errors.len() > 0 {processed_errors.len() > 0} else {true}); - - return Some(processed_errors); - - #[derive(Debug)] - struct FreeRegionsFromSameFn { - sub_fr: ty::FreeRegion, - sup_fr: ty::FreeRegion, - scope_id: ast::NodeId - } - - impl FreeRegionsFromSameFn { - fn new(sub_fr: ty::FreeRegion, - sup_fr: ty::FreeRegion, - scope_id: ast::NodeId) - -> FreeRegionsFromSameFn { - FreeRegionsFromSameFn { - sub_fr: sub_fr, - sup_fr: sup_fr, - scope_id: scope_id - } - } - } - fn free_regions_from_same_fn<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - sub: &'tcx Region, - sup: &'tcx Region) - -> Option { - debug!("free_regions_from_same_fn(sub={:?}, sup={:?})", sub, sup); - let (scope_id, fr1, fr2) = match (sub, sup) { - (&ReFree(fr1), &ReFree(fr2)) => { - if fr1.scope != fr2.scope { - return None - } - assert!(fr1.scope == fr2.scope); - (fr1.scope.node_id(&tcx.region_maps), fr1, fr2) - }, - _ => return None - }; - let parent = tcx.hir.get_parent(scope_id); - let parent_node = tcx.hir.find(parent); - match parent_node { - Some(node) => match node { - hir_map::NodeItem(item) => match item.node { - hir::ItemFn(..) => { - Some(FreeRegionsFromSameFn::new(fr1, fr2, scope_id)) - }, - _ => None - }, - hir_map::NodeImplItem(..) | - hir_map::NodeTraitItem(..) => { - Some(FreeRegionsFromSameFn::new(fr1, fr2, scope_id)) - }, - _ => None - }, - None => { - debug!("no parent node of scope_id {}", scope_id); - None - } - } - } + // We want to avoid reporting generic-bound failures if we can + // avoid it: these have a very high rate of being unhelpful in + // practice. This is because they are basically secondary + // checks that test the state of the region graph after the + // rest of inference is done, and the other kinds of errors + // indicate that the region constraint graph is internally + // inconsistent, so these test results are likely to be + // meaningless. + // + // Therefore, we filter them out of the list unless they are + // the only thing in the list. + + let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e { + ConcreteFailure(..) => false, + SubSupConflict(..) => false, + GenericBoundFailure(..) => true, + }; - fn append_to_same_regions(same_regions: &mut Vec, - same_frs: &FreeRegionsFromSameFn) { - debug!("append_to_same_regions(same_regions={:?}, same_frs={:?})", - same_regions, same_frs); - let scope_id = same_frs.scope_id; - let (sub_fr, sup_fr) = (same_frs.sub_fr, same_frs.sup_fr); - for sr in same_regions.iter_mut() { - if sr.contains(&sup_fr.bound_region) && scope_id == sr.scope_id { - sr.push(sub_fr.bound_region); - return - } - } - same_regions.push(SameRegions { - scope_id: scope_id, - regions: vec![sub_fr.bound_region, sup_fr.bound_region] - }) + if errors.iter().all(|e| is_bound_failure(e)) { + errors.clone() + } else { + errors.iter().filter(|&e| !is_bound_failure(e)).cloned().collect() } } @@ -1040,20 +858,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { err.emit(); } - fn report_processed_errors(&self, - origins: &[ProcessedErrorOrigin<'tcx>]) { - for origin in origins.iter() { - let mut err = match *origin { - ProcessedErrorOrigin::VariableFailure(ref var_origin) => - self.report_inference_failure(var_origin.clone()), - ProcessedErrorOrigin::ConcreteFailure(ref sr_origin, sub, sup) => - self.report_concrete_failure(sr_origin.clone(), sub, sup), - }; - - err.emit(); - } - } - pub fn issue_32330_warnings(&self, span: Span, issue32330s: &[ty::Issue32330]) { for issue32330 in issue32330s { match *issue32330 { diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs index af6f2c50e72fc..0bb9e2c7fa15c 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -24,7 +24,7 @@ use rustc_data_structures::graph::{self, Direction, NodeIndex, OUTGOING}; use rustc_data_structures::unify::{self, UnificationTable}; use middle::free_region::FreeRegionMap; use ty::{self, Ty, TyCtxt}; -use ty::{BoundRegion, Region, RegionVid}; +use ty::{Region, RegionVid}; use ty::{ReEmpty, ReStatic, ReFree, ReEarlyBound, ReErased}; use ty::{ReLateBound, ReScope, ReVar, ReSkolemized, BrFresh}; @@ -171,13 +171,6 @@ pub enum RegionResolutionError<'tcx> { &'tcx Region, SubregionOrigin<'tcx>, &'tcx Region), - - /// For subsets of `ConcreteFailure` and `SubSupConflict`, we can derive - /// more specific errors message by suggesting to the user where they - /// should put a lifetime. In those cases we process and put those errors - /// into `ProcessedErrors` before we do any reporting. - ProcessedErrors(Vec>, - Vec), } #[derive(Clone, Debug)] @@ -186,33 +179,6 @@ pub enum ProcessedErrorOrigin<'tcx> { VariableFailure(RegionVariableOrigin), } -/// SameRegions is used to group regions that we think are the same and would -/// like to indicate so to the user. -/// For example, the following function -/// ``` -/// struct Foo { bar: i32 } -/// fn foo2<'a, 'b>(x: &'a Foo) -> &'b i32 { -/// &x.bar -/// } -/// ``` -/// would report an error because we expect 'a and 'b to match, and so we group -/// 'a and 'b together inside a SameRegions struct -#[derive(Clone, Debug)] -pub struct SameRegions { - pub scope_id: ast::NodeId, - pub regions: Vec, -} - -impl SameRegions { - pub fn contains(&self, other: &BoundRegion) -> bool { - self.regions.contains(other) - } - - pub fn push(&mut self, other: BoundRegion) { - self.regions.push(other); - } -} - pub type CombineMap<'tcx> = FxHashMap, RegionVid>; pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 0e8e1921de700..3cd7517baa53f 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -202,7 +202,9 @@ impl<'tcx> cmt_<'tcx> { Categorization::Downcast(ref cmt, _) => { if let Categorization::Local(_) = cmt.cat { if let ty::TyAdt(def, _) = self.ty.sty { - return def.struct_variant().find_field_named(name).map(|x| x.did); + if def.is_struct() { + return def.struct_variant().find_field_named(name).map(|x| x.did); + } } None } else { diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index 94b2ba58c9aa5..5476e99c9459c 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -196,6 +196,28 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { } }).clone() } + + fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { + if self.tcx.sess.features.borrow().never_type { + ty.is_uninhabited_from(self.module, self.tcx) + } else { + false + } + } + + fn is_variant_uninhabited(&self, + variant: &'tcx ty::VariantDef, + substs: &'tcx ty::subst::Substs<'tcx>) -> bool + { + if self.tcx.sess.features.borrow().never_type { + let forest = variant.uninhabited_from( + &mut FxHashSet::default(), self.tcx, substs, AdtKind::Enum + ); + forest.contains(self.tcx, self.module) + } else { + false + } + } } #[derive(Clone, Debug, PartialEq)] @@ -379,48 +401,32 @@ impl<'tcx> Witness<'tcx> { fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, pcx: PatternContext<'tcx>) -> Vec { - let check_inhabited = cx.tcx.sess.features.borrow().never_type; debug!("all_constructors({:?})", pcx.ty); match pcx.ty.sty { ty::TyBool => [true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(), ty::TySlice(ref sub_ty) => { - if sub_ty.is_uninhabited_from(cx.module, cx.tcx) - && check_inhabited - { + if cx.is_uninhabited(sub_ty) { vec![Slice(0)] } else { (0..pcx.max_slice_length+1).map(|length| Slice(length)).collect() } } ty::TyArray(ref sub_ty, length) => { - if length == 0 || !(sub_ty.is_uninhabited_from(cx.module, cx.tcx) - && check_inhabited) - { - vec![Slice(length)] - } else { + if length > 0 && cx.is_uninhabited(sub_ty) { vec![] + } else { + vec![Slice(length)] } } ty::TyAdt(def, substs) if def.is_enum() && def.variants.len() != 1 => { - def.variants.iter().filter_map(|v| { - let mut visited = FxHashSet::default(); - let forest = v.uninhabited_from(&mut visited, - cx.tcx, substs, - AdtKind::Enum); - if forest.contains(cx.tcx, cx.module) - && check_inhabited - { - None - } else { - Some(Variant(v.did)) - } - }).collect() + def.variants.iter() + .filter(|v| !cx.is_variant_uninhabited(v, substs)) + .map(|v| Variant(v.did)) + .collect() } _ => { - if pcx.ty.is_uninhabited_from(cx.module, cx.tcx) - && check_inhabited - { + if cx.is_uninhabited(pcx.ty) { vec![] } else { vec![Single] @@ -564,7 +570,6 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, assert!(rows.iter().all(|r| r.len() == v.len())); - let pcx = PatternContext { ty: rows.iter().map(|r| r[0].ty).find(|ty| !ty.references_error()) .unwrap_or(v[0].ty), @@ -590,7 +595,6 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, let missing_ctors: Vec = all_ctors.iter().filter(|c| { !used_ctors.contains(*c) }).cloned().collect(); - debug!("missing_ctors = {:?}", missing_ctors); // `missing_ctors` is the set of constructors from the same type as the // first column of `matrix` that are matched only by wildcard patterns @@ -599,8 +603,23 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, // Therefore, if there is some pattern that is unmatched by `matrix`, // it will still be unmatched if the first constructor is replaced by // any of the constructors in `missing_ctors` - - if missing_ctors.is_empty() { + // + // However, if our scrutinee is *privately* an empty enum, we + // must treat it as though it had an "unknown" constructor (in + // that case, all other patterns obviously can't be variants) + // to avoid exposing its emptyness. See the `match_privately_empty` + // test for details. + // + // FIXME: currently the only way I know of something can + // be a privately-empty enum is when the never_type + // feature flag is not present, so this is only + // needed for that case. + + let is_privately_empty = + all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty); + debug!("missing_ctors={:?} is_privately_empty={:?}", missing_ctors, + is_privately_empty); + if missing_ctors.is_empty() && !is_privately_empty { all_ctors.into_iter().map(|c| { is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness) }).find(|result| result.is_useful()).unwrap_or(NotUseful) @@ -649,6 +668,7 @@ fn is_useful_specialized<'p, 'a:'p, 'tcx: 'a>( lty: Ty<'tcx>, witness: WitnessPreference) -> Usefulness<'tcx> { + debug!("is_useful_specialized({:?}, {:?}, {:?})", v, ctor, lty); let sub_pat_tys = constructor_sub_pattern_tys(cx, &ctor, lty); let wild_patterns_owned: Vec<_> = sub_pat_tys.iter().map(|ty| { Pattern { @@ -754,7 +774,19 @@ fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>, ty::TyRef(_, ref ty_and_mut) => vec![ty_and_mut.ty], ty::TyAdt(adt, substs) => { adt.variants[ctor.variant_index_for_adt(adt)].fields.iter().map(|field| { - field.ty(cx.tcx, substs) + let is_visible = adt.is_enum() + || field.vis.is_accessible_from(cx.module, cx.tcx); + if is_visible { + field.ty(cx.tcx, substs) + } else { + // Treat all non-visible fields as nil. They + // can't appear in any other pattern from + // this match (because they are private), + // so their type does not matter - but + // we don't want to know they are + // uninhabited. + cx.tcx.mk_nil() + } }).collect() } _ => vec![], diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 6f33b4fad769f..9b30946c0bebb 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -177,6 +177,31 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { // Fourth, check for unreachable arms. check_arms(cx, &inlined_arms, source); + // Then, if the match has no arms, check whether the scrutinee + // is uninhabited. + let pat_ty = self.tables.node_id_to_type(scrut.id); + let module = self.tcx.hir.local_def_id(self.tcx.hir.get_module_parent(scrut.id)); + if inlined_arms.is_empty() { + let scrutinee_is_uninhabited = if self.tcx.sess.features.borrow().never_type { + pat_ty.is_uninhabited_from(module, self.tcx) + } else { + self.conservative_is_uninhabited(pat_ty) + }; + if !scrutinee_is_uninhabited { + // We know the type is inhabited, so this must be wrong + let mut err = create_e0004(self.tcx.sess, scrut.span, + format!("non-exhaustive patterns: type {} \ + is non-empty", + pat_ty)); + span_help!(&mut err, scrut.span, + "Please ensure that all possible cases are being handled; \ + possibly adding wildcards or more match arms."); + err.emit(); + } + // If the type *is* uninhabited, it's vacuously exhaustive + return; + } + let matrix: Matrix = inlined_arms .iter() .filter(|&&(_, guard)| guard.is_none()) @@ -188,6 +213,15 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { }) } + fn conservative_is_uninhabited(&self, scrutinee_ty: Ty<'tcx>) -> bool { + // "rustc-1.0-style" uncontentious uninhabitableness check + match scrutinee_ty.sty { + ty::TyNever => true, + ty::TyAdt(def, _) => def.variants.is_empty(), + _ => false + } + } + fn check_irrefutable(&self, pat: &Pat, is_fn_arg: bool) { let origin = if is_fn_arg { "function argument" diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs index c3b9a56ac9778..f7850780f5b6a 100644 --- a/src/librustc_trans/adt.rs +++ b/src/librustc_trans/adt.rs @@ -56,6 +56,8 @@ use monomorphize; use type_::Type; use type_of; +use mir::lvalue::Alignment; + /// Given an enum, struct, closure, or tuple, extracts fields. /// Treats closures as a struct with one variant. /// `empty_if_no_variants` is a switch to deal with empty enums. @@ -279,6 +281,7 @@ pub fn trans_get_discr<'a, 'tcx>( bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, scrutinee: ValueRef, + alignment: Alignment, cast_to: Option, range_assert: bool ) -> ValueRef { @@ -292,11 +295,12 @@ pub fn trans_get_discr<'a, 'tcx>( let val = match *l { layout::CEnum { discr, min, max, .. } => { - load_discr(bcx, discr, scrutinee, min, max, range_assert) + load_discr(bcx, discr, scrutinee, alignment, min, max, range_assert) } layout::General { discr, .. } => { let ptr = bcx.struct_gep(scrutinee, 0); - load_discr(bcx, discr, ptr, 0, def.variants.len() as u64 - 1, + load_discr(bcx, discr, ptr, alignment, + 0, def.variants.len() as u64 - 1, range_assert) } layout::Univariant { .. } | layout::UntaggedUnion { .. } => C_u8(bcx.ccx, 0), @@ -305,10 +309,10 @@ pub fn trans_get_discr<'a, 'tcx>( let llptrty = type_of::sizing_type_of(bcx.ccx, monomorphize::field_ty(bcx.tcx(), substs, &def.variants[nndiscr as usize].fields[0])); - bcx.icmp(cmp, bcx.load(scrutinee), C_null(llptrty)) + bcx.icmp(cmp, bcx.load(scrutinee, alignment.to_align()), C_null(llptrty)) } layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { - struct_wrapped_nullable_bitdiscr(bcx, nndiscr, discrfield, scrutinee) + struct_wrapped_nullable_bitdiscr(bcx, nndiscr, discrfield, scrutinee, alignment) }, _ => bug!("{} is not an enum", t) }; @@ -322,17 +326,19 @@ fn struct_wrapped_nullable_bitdiscr( bcx: &Builder, nndiscr: u64, discrfield: &layout::FieldPath, - scrutinee: ValueRef + scrutinee: ValueRef, + alignment: Alignment, ) -> ValueRef { let llptrptr = bcx.gepi(scrutinee, &discrfield.iter().map(|f| *f as usize).collect::>()[..]); - let llptr = bcx.load(llptrptr); + let llptr = bcx.load(llptrptr, alignment.to_align()); let cmp = if nndiscr == 0 { IntEQ } else { IntNE }; bcx.icmp(cmp, llptr, C_null(val_ty(llptr))) } /// Helper for cases where the discriminant is simply loaded. -fn load_discr(bcx: &Builder, ity: layout::Integer, ptr: ValueRef, min: u64, max: u64, +fn load_discr(bcx: &Builder, ity: layout::Integer, ptr: ValueRef, + alignment: Alignment, min: u64, max: u64, range_assert: bool) -> ValueRef { let llty = Type::from_integer(bcx.ccx, ity); @@ -348,11 +354,12 @@ fn load_discr(bcx: &Builder, ity: layout::Integer, ptr: ValueRef, min: u64, max: // rejected by the LLVM verifier (it would mean either an // empty set, which is impossible, or the entire range of the // type, which is pointless). - bcx.load(ptr) + bcx.load(ptr, alignment.to_align()) } else { // llvm::ConstantRange can deal with ranges that wrap around, // so an overflow on (max + 1) is fine. - bcx.load_range_assert(ptr, min, max.wrapping_add(1), /* signed: */ True) + bcx.load_range_assert(ptr, min, max.wrapping_add(1), /* signed: */ True, + alignment.to_align()) } } diff --git a/src/librustc_trans/asm.rs b/src/librustc_trans/asm.rs index c95d414701876..12e4e57964f98 100644 --- a/src/librustc_trans/asm.rs +++ b/src/librustc_trans/asm.rs @@ -20,6 +20,8 @@ use builder::Builder; use rustc::hir; use rustc::ty::Ty; +use mir::lvalue::Alignment; + use std::ffi::CString; use syntax::ast::AsmDialect; use libc::{c_uint, c_char}; @@ -38,7 +40,7 @@ pub fn trans_inline_asm<'a, 'tcx>( let mut indirect_outputs = vec![]; for (i, (out, &(val, ty))) in ia.outputs.iter().zip(&outputs).enumerate() { let val = if out.is_rw || out.is_indirect { - Some(base::load_ty(bcx, val, ty)) + Some(base::load_ty(bcx, val, Alignment::Packed, ty)) } else { None }; diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 9bd19d5bbb3e4..5312cc60b0916 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -90,6 +90,8 @@ use rustc::hir; use rustc::ty::layout::{self, Layout}; use syntax::ast; +use mir::lvalue::Alignment; + pub struct StatRecorder<'a, 'tcx: 'a> { ccx: &'a CrateContext<'a, 'tcx>, name: Option, @@ -250,25 +252,25 @@ pub fn unsize_thin_ptr<'a, 'tcx>( /// Coerce `src`, which is a reference to a value of type `src_ty`, /// to a value of type `dst_ty` and store the result in `dst` pub fn coerce_unsized_into<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, - src: ValueRef, - src_ty: Ty<'tcx>, - dst: ValueRef, - dst_ty: Ty<'tcx>) { + src: &LvalueRef<'tcx>, + dst: &LvalueRef<'tcx>) { + let src_ty = src.ty.to_ty(bcx.tcx()); + let dst_ty = dst.ty.to_ty(bcx.tcx()); let coerce_ptr = || { let (base, info) = if common::type_is_fat_ptr(bcx.ccx, src_ty) { // fat-ptr to fat-ptr unsize preserves the vtable // i.e. &'a fmt::Debug+Send => &'a fmt::Debug // So we need to pointercast the base to ensure // the types match up. - let (base, info) = load_fat_ptr(bcx, src, src_ty); + let (base, info) = load_fat_ptr(bcx, src.llval, src.alignment, src_ty); let llcast_ty = type_of::fat_ptr_base_ty(bcx.ccx, dst_ty); let base = bcx.pointercast(base, llcast_ty); (base, info) } else { - let base = load_ty(bcx, src, src_ty); + let base = load_ty(bcx, src.llval, src.alignment, src_ty); unsize_thin_ptr(bcx, base, src_ty, dst_ty) }; - store_fat_ptr(bcx, base, info, dst, dst_ty); + store_fat_ptr(bcx, base, info, dst.llval, dst.alignment, dst_ty); }; match (&src_ty.sty, &dst_ty.sty) { (&ty::TyRef(..), &ty::TyRef(..)) | @@ -290,21 +292,22 @@ pub fn coerce_unsized_into<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, monomorphize::field_ty(bcx.tcx(), substs_b, f) }); - let src = LvalueRef::new_sized_ty(src, src_ty); - let dst = LvalueRef::new_sized_ty(dst, dst_ty); - let iter = src_fields.zip(dst_fields).enumerate(); for (i, (src_fty, dst_fty)) in iter { if type_is_zero_size(bcx.ccx, dst_fty) { continue; } - let src_f = src.trans_field_ptr(bcx, i); - let dst_f = dst.trans_field_ptr(bcx, i); + let (src_f, src_f_align) = src.trans_field_ptr(bcx, i); + let (dst_f, dst_f_align) = dst.trans_field_ptr(bcx, i); if src_fty == dst_fty { memcpy_ty(bcx, dst_f, src_f, src_fty, None); } else { - coerce_unsized_into(bcx, src_f, src_fty, dst_f, dst_fty); + coerce_unsized_into( + bcx, + &LvalueRef::new_sized_ty(src_f, src_fty, src_f_align), + &LvalueRef::new_sized_ty(dst_f, dst_fty, dst_f_align) + ); } } } @@ -399,7 +402,8 @@ pub fn call_assume<'a, 'tcx>(b: &Builder<'a, 'tcx>, val: ValueRef) { /// Helper for loading values from memory. Does the necessary conversion if the in-memory type /// differs from the type used for SSA values. Also handles various special cases where the type /// gives us better information about what we are loading. -pub fn load_ty<'a, 'tcx>(b: &Builder<'a, 'tcx>, ptr: ValueRef, t: Ty<'tcx>) -> ValueRef { +pub fn load_ty<'a, 'tcx>(b: &Builder<'a, 'tcx>, ptr: ValueRef, + alignment: Alignment, t: Ty<'tcx>) -> ValueRef { let ccx = b.ccx; if type_is_zero_size(ccx, t) { return C_undef(type_of::type_of(ccx, t)); @@ -419,29 +423,31 @@ pub fn load_ty<'a, 'tcx>(b: &Builder<'a, 'tcx>, ptr: ValueRef, t: Ty<'tcx>) -> V } if t.is_bool() { - b.trunc(b.load_range_assert(ptr, 0, 2, llvm::False), Type::i1(ccx)) + b.trunc(b.load_range_assert(ptr, 0, 2, llvm::False, alignment.to_align()), + Type::i1(ccx)) } else if t.is_char() { // a char is a Unicode codepoint, and so takes values from 0 // to 0x10FFFF inclusive only. - b.load_range_assert(ptr, 0, 0x10FFFF + 1, llvm::False) + b.load_range_assert(ptr, 0, 0x10FFFF + 1, llvm::False, alignment.to_align()) } else if (t.is_region_ptr() || t.is_box()) && !common::type_is_fat_ptr(ccx, t) { - b.load_nonnull(ptr) + b.load_nonnull(ptr, alignment.to_align()) } else { - b.load(ptr) + b.load(ptr, alignment.to_align()) } } /// Helper for storing values in memory. Does the necessary conversion if the in-memory type /// differs from the type used for SSA values. -pub fn store_ty<'a, 'tcx>(cx: &Builder<'a, 'tcx>, v: ValueRef, dst: ValueRef, t: Ty<'tcx>) { +pub fn store_ty<'a, 'tcx>(cx: &Builder<'a, 'tcx>, v: ValueRef, dst: ValueRef, + dst_align: Alignment, t: Ty<'tcx>) { debug!("store_ty: {:?} : {:?} <- {:?}", Value(dst), t, Value(v)); if common::type_is_fat_ptr(cx.ccx, t) { let lladdr = cx.extract_value(v, abi::FAT_PTR_ADDR); let llextra = cx.extract_value(v, abi::FAT_PTR_EXTRA); - store_fat_ptr(cx, lladdr, llextra, dst, t); + store_fat_ptr(cx, lladdr, llextra, dst, dst_align, t); } else { - cx.store(from_immediate(cx, v), dst, None); + cx.store(from_immediate(cx, v), dst, dst_align.to_align()); } } @@ -449,24 +455,25 @@ pub fn store_fat_ptr<'a, 'tcx>(cx: &Builder<'a, 'tcx>, data: ValueRef, extra: ValueRef, dst: ValueRef, + dst_align: Alignment, _ty: Ty<'tcx>) { // FIXME: emit metadata - cx.store(data, get_dataptr(cx, dst), None); - cx.store(extra, get_meta(cx, dst), None); + cx.store(data, get_dataptr(cx, dst), dst_align.to_align()); + cx.store(extra, get_meta(cx, dst), dst_align.to_align()); } pub fn load_fat_ptr<'a, 'tcx>( - b: &Builder<'a, 'tcx>, src: ValueRef, t: Ty<'tcx> + b: &Builder<'a, 'tcx>, src: ValueRef, alignment: Alignment, t: Ty<'tcx> ) -> (ValueRef, ValueRef) { let ptr = get_dataptr(b, src); let ptr = if t.is_region_ptr() || t.is_box() { - b.load_nonnull(ptr) + b.load_nonnull(ptr, alignment.to_align()) } else { - b.load(ptr) + b.load(ptr, alignment.to_align()) }; // FIXME: emit metadata on `meta`. - let meta = b.load(get_meta(b, src)); + let meta = b.load(get_meta(b, src), alignment.to_align()); (ptr, meta) } @@ -633,7 +640,7 @@ pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, bcx.alloca(fn_ty.ret.memory_ty(ccx), "sret_slot") }; // Can return unsized value - let mut dest_val = LvalueRef::new_sized_ty(dest, sig.output()); + let mut dest_val = LvalueRef::new_sized_ty(dest, sig.output(), Alignment::AbiAligned); dest_val.ty = LvalueTy::Downcast { adt_def: sig.output().ty_adt_def().unwrap(), substs: substs, @@ -642,7 +649,7 @@ pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let mut llarg_idx = fn_ty.ret.is_indirect() as usize; let mut arg_idx = 0; for (i, arg_ty) in sig.inputs().iter().enumerate() { - let lldestptr = dest_val.trans_field_ptr(&bcx, i); + let (lldestptr, _) = dest_val.trans_field_ptr(&bcx, i); let arg = &fn_ty.args[arg_idx]; arg_idx += 1; if common::type_is_fat_ptr(bcx.ccx, arg_ty) { @@ -662,14 +669,12 @@ pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } if let Some(cast_ty) = fn_ty.ret.cast { - let load = bcx.load(bcx.pointercast(dest, cast_ty.ptr_to())); - let llalign = llalign_of_min(ccx, fn_ty.ret.ty); - unsafe { - llvm::LLVMSetAlignment(load, llalign); - } - bcx.ret(load) + bcx.ret(bcx.load( + bcx.pointercast(dest, cast_ty.ptr_to()), + Some(llalign_of_min(ccx, fn_ty.ret.ty)) + )); } else { - bcx.ret(bcx.load(dest)) + bcx.ret(bcx.load(dest, None)) } } else { bcx.ret_void(); diff --git a/src/librustc_trans/builder.rs b/src/librustc_trans/builder.rs index cf7f3e9501d1a..0abf9773b62e3 100644 --- a/src/librustc_trans/builder.rs +++ b/src/librustc_trans/builder.rs @@ -19,9 +19,8 @@ use machine::llalign_of_pref; use type_::Type; use value::Value; use libc::{c_uint, c_char}; -use rustc::ty::{Ty, TyCtxt, TypeFoldable}; +use rustc::ty::TyCtxt; use rustc::session::Session; -use type_of; use std::borrow::Cow; use std::ffi::CString; @@ -486,11 +485,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { builder.dynamic_alloca(ty, name) } - pub fn alloca_ty(&self, ty: Ty<'tcx>, name: &str) -> ValueRef { - assert!(!ty.has_param_types()); - self.alloca(type_of::type_of(self.ccx, ty), name) - } - pub fn dynamic_alloca(&self, ty: Type, name: &str) -> ValueRef { self.count_insn("alloca"); unsafe { @@ -511,10 +505,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - pub fn load(&self, ptr: ValueRef) -> ValueRef { + pub fn load(&self, ptr: ValueRef, align: Option) -> ValueRef { self.count_insn("load"); unsafe { - llvm::LLVMBuildLoad(self.llbuilder, ptr, noname()) + let load = llvm::LLVMBuildLoad(self.llbuilder, ptr, noname()); + if let Some(align) = align { + llvm::LLVMSetAlignment(load, align as c_uint); + } + load } } @@ -539,8 +537,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pub fn load_range_assert(&self, ptr: ValueRef, lo: u64, - hi: u64, signed: llvm::Bool) -> ValueRef { - let value = self.load(ptr); + hi: u64, signed: llvm::Bool, + align: Option) -> ValueRef { + let value = self.load(ptr, align); unsafe { let t = llvm::LLVMGetElementType(llvm::LLVMTypeOf(ptr)); @@ -558,8 +557,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { value } - pub fn load_nonnull(&self, ptr: ValueRef) -> ValueRef { - let value = self.load(ptr); + pub fn load_nonnull(&self, ptr: ValueRef, align: Option) -> ValueRef { + let value = self.load(ptr, align); unsafe { llvm::LLVMSetMetadata(value, llvm::MD_nonnull as c_uint, llvm::LLVMMDNodeInContext(self.ccx.llcx(), ptr::null(), 0)); diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 58d0c46850353..a31e3caba3671 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -41,6 +41,8 @@ use std::iter; use syntax_pos::DUMMY_SP; +use mir::lvalue::Alignment; + #[derive(Debug)] pub enum CalleeData { /// Constructor for enum variant/tuple-like-struct. @@ -358,29 +360,27 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( let fn_ty = callee.direct_fn_type(bcx.ccx, &[]); let self_idx = fn_ty.ret.is_indirect() as usize; let env_arg = &orig_fn_ty.args[0]; - let llenv = if env_arg.is_indirect() { - llargs[self_idx] + let env = if env_arg.is_indirect() { + LvalueRef::new_sized_ty(llargs[self_idx], closure_ty, Alignment::AbiAligned) } else { - let scratch = bcx.alloca_ty(closure_ty, "self"); + let scratch = LvalueRef::alloca(&bcx, closure_ty, "self"); let mut llarg_idx = self_idx; - env_arg.store_fn_arg(&bcx, &mut llarg_idx, scratch); + env_arg.store_fn_arg(&bcx, &mut llarg_idx, scratch.llval); scratch }; - debug!("trans_fn_once_adapter_shim: env={:?}", Value(llenv)); + debug!("trans_fn_once_adapter_shim: env={:?}", env); // Adjust llargs such that llargs[self_idx..] has the call arguments. // For zero-sized closures that means sneaking in a new argument. if env_arg.is_ignore() { - llargs.insert(self_idx, llenv); + llargs.insert(self_idx, env.llval); } else { - llargs[self_idx] = llenv; + llargs[self_idx] = env.llval; } // Call the by-ref closure body with `self` in a cleanup scope, // to drop `self` when the body returns, or in case it unwinds. - let self_scope = CleanupScope::schedule_drop_mem( - &bcx, LvalueRef::new_sized_ty(llenv, closure_ty) - ); + let self_scope = CleanupScope::schedule_drop_mem(&bcx, env); let llfn = callee.reify(bcx.ccx); let llret; @@ -512,7 +512,7 @@ fn trans_fn_pointer_shim<'a, 'tcx>( let llfnpointer = llfnpointer.unwrap_or_else(|| { // the first argument (`self`) will be ptr to the fn pointer if is_by_ref { - bcx.load(self_arg) + bcx.load(self_arg, None) } else { self_arg } diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs index 1415ca6029f53..db088bb22724f 100644 --- a/src/librustc_trans/glue.rs +++ b/src/librustc_trans/glue.rs @@ -13,7 +13,6 @@ // Code relating to drop glue. use std; -use std::ptr; use std::iter; use llvm; @@ -41,6 +40,7 @@ use Disr; use builder::Builder; use syntax_pos::DUMMY_SP; +use mir::lvalue::Alignment; pub fn trans_exchange_free_ty<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, ptr: LvalueRef<'tcx>) { let content_ty = ptr.ty.to_ty(bcx.tcx()); @@ -199,9 +199,9 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKi let value = get_param(llfn, 0); let ptr = if ccx.shared().type_is_sized(t) { - LvalueRef::new_sized_ty(value, t) + LvalueRef::new_sized_ty(value, t, Alignment::AbiAligned) } else { - LvalueRef::new_unsized_ty(value, get_param(llfn, 1), t) + LvalueRef::new_unsized_ty(value, get_param(llfn, 1), t, Alignment::AbiAligned) }; let skip_dtor = match g { @@ -216,11 +216,13 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKi assert!(!skip_dtor); let content_ty = t.boxed_ty(); let ptr = if !bcx.ccx.shared().type_is_sized(content_ty) { - let llbox = bcx.load(get_dataptr(&bcx, ptr.llval)); - let info = bcx.load(get_meta(&bcx, ptr.llval)); - LvalueRef::new_unsized_ty(llbox, info, content_ty) + let llbox = bcx.load(get_dataptr(&bcx, ptr.llval), None); + let info = bcx.load(get_meta(&bcx, ptr.llval), None); + LvalueRef::new_unsized_ty(llbox, info, content_ty, Alignment::AbiAligned) } else { - LvalueRef::new_sized_ty(bcx.load(ptr.llval), content_ty) + LvalueRef::new_sized_ty( + bcx.load(ptr.llval, None), + content_ty, Alignment::AbiAligned) }; drop_ty(&bcx, ptr); trans_exchange_free_ty(&bcx, ptr); @@ -231,7 +233,7 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKi // versus without calling Drop::drop. Assert caller is // okay with always calling the Drop impl, if any. assert!(!skip_dtor); - let dtor = bcx.load(ptr.llextra); + let dtor = bcx.load(ptr.llextra, None); bcx.call(dtor, &[ptr.llval], None); bcx } @@ -384,7 +386,7 @@ pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, inf let info = bcx.pointercast(info, Type::int(bcx.ccx).ptr_to()); let size_ptr = bcx.gepi(info, &[1]); let align_ptr = bcx.gepi(info, &[2]); - (bcx.load(size_ptr), bcx.load(align_ptr)) + (bcx.load(size_ptr, None), bcx.load(align_ptr, None)) } ty::TySlice(_) | ty::TyStr => { let unit_ty = t.sequence_element_type(bcx.tcx()); @@ -416,8 +418,8 @@ fn drop_structural_ty<'a, 'tcx>( let tcx = cx.tcx(); for (i, field) in variant.fields.iter().enumerate() { let arg = monomorphize::field_ty(tcx, substs, field); - let field_ptr = av.trans_field_ptr(&cx, i); - drop_ty(&cx, LvalueRef::new_sized_ty(field_ptr, arg)); + let (field_ptr, align) = av.trans_field_ptr(&cx, i); + drop_ty(&cx, LvalueRef::new_sized_ty(field_ptr, arg, align)); } } @@ -426,8 +428,8 @@ fn drop_structural_ty<'a, 'tcx>( match t.sty { ty::TyClosure(def_id, substs) => { for (i, upvar_ty) in substs.upvar_tys(def_id, cx.tcx()).enumerate() { - let llupvar = ptr.trans_field_ptr(&cx, i); - drop_ty(&cx, LvalueRef::new_sized_ty(llupvar, upvar_ty)); + let (llupvar, align) = ptr.trans_field_ptr(&cx, i); + drop_ty(&cx, LvalueRef::new_sized_ty(llupvar, upvar_ty, align)); } } ty::TyArray(_, n) => { @@ -435,29 +437,29 @@ fn drop_structural_ty<'a, 'tcx>( let len = C_uint(cx.ccx, n); let unit_ty = t.sequence_element_type(cx.tcx()); cx = tvec::slice_for_each(&cx, base, unit_ty, len, - |bb, vv| drop_ty(bb, LvalueRef::new_sized_ty(vv, unit_ty))); + |bb, vv| drop_ty(bb, LvalueRef::new_sized_ty(vv, unit_ty, ptr.alignment))); } ty::TySlice(_) | ty::TyStr => { let unit_ty = t.sequence_element_type(cx.tcx()); cx = tvec::slice_for_each(&cx, ptr.llval, unit_ty, ptr.llextra, - |bb, vv| drop_ty(bb, LvalueRef::new_sized_ty(vv, unit_ty))); + |bb, vv| drop_ty(bb, LvalueRef::new_sized_ty(vv, unit_ty, ptr.alignment))); } ty::TyTuple(ref args) => { for (i, arg) in args.iter().enumerate() { - let llfld_a = ptr.trans_field_ptr(&cx, i); - drop_ty(&cx, LvalueRef::new_sized_ty(llfld_a, *arg)); + let (llfld_a, align) = ptr.trans_field_ptr(&cx, i); + drop_ty(&cx, LvalueRef::new_sized_ty(llfld_a, *arg, align)); } } ty::TyAdt(adt, substs) => match adt.adt_kind() { AdtKind::Struct => { for (i, field) in adt.variants[0].fields.iter().enumerate() { let field_ty = monomorphize::field_ty(cx.tcx(), substs, field); - let mut field_ptr = ptr.clone(); - field_ptr.llval = ptr.trans_field_ptr(&cx, i); - field_ptr.ty = LvalueTy::from_ty(field_ty); - if cx.ccx.shared().type_is_sized(field_ty) { - field_ptr.llextra = ptr::null_mut(); - } + let (llval, align) = ptr.trans_field_ptr(&cx, i); + let field_ptr = if cx.ccx.shared().type_is_sized(field_ty) { + LvalueRef::new_sized_ty(llval, field_ty, align) + } else { + LvalueRef::new_unsized_ty(llval, ptr.llextra, field_ty, align) + }; drop_ty(&cx, field_ptr); } } @@ -490,9 +492,8 @@ fn drop_structural_ty<'a, 'tcx>( layout::General { .. } | layout::RawNullablePointer { .. } | layout::StructWrappedNullablePointer { .. } => { - let lldiscrim_a = adt::trans_get_discr(&cx, t, ptr.llval, None, false); - let tcx = cx.tcx(); - drop_ty(&cx, LvalueRef::new_sized_ty(lldiscrim_a, tcx.types.isize)); + let lldiscrim_a = adt::trans_get_discr( + &cx, t, ptr.llval, ptr.alignment, None, false); // Create a fall-through basic block for the "else" case of // the switch instruction we're about to generate. Note that diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 842a21e98db46..cca068df6bed9 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -38,6 +38,8 @@ use rustc_i128::u128; use std::cmp::Ordering; use std::iter; +use mir::lvalue::Alignment; + fn get_simple_intrinsic(ccx: &CrateContext, name: &str) -> Option { let llvm_name = match name { "sqrtf32" => "llvm.sqrt.f32", @@ -245,7 +247,7 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, bcx.volatile_store(llargs[2], get_meta(bcx, llargs[0])); } else { let val = if fn_ty.args[1].is_indirect() { - bcx.load(llargs[1]) + bcx.load(llargs[1], None) } else { from_immediate(bcx, llargs[1]) }; @@ -350,7 +352,7 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, let val_ty = substs.type_at(0); match val_ty.sty { ty::TyAdt(adt, ..) if adt.is_enum() => { - adt::trans_get_discr(bcx, val_ty, llargs[0], + adt::trans_get_discr(bcx, val_ty, llargs[0], Alignment::AbiAligned, Some(llret_ty), true) } _ => C_null(llret_ty) @@ -549,8 +551,11 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, // destructors, and the contents are SIMD // etc. assert!(!bcx.ccx.shared().type_needs_drop(arg_type)); - let arg = LvalueRef::new_sized_ty(llarg, arg_type); - (0..contents.len()).map(|i| bcx.load(arg.trans_field_ptr(bcx, i))).collect() + let arg = LvalueRef::new_sized_ty(llarg, arg_type, Alignment::AbiAligned); + (0..contents.len()).map(|i| { + let (ptr, align) = arg.trans_field_ptr(bcx, i); + bcx.load(ptr, align.to_align()) + }).collect() } intrinsics::Type::Pointer(_, Some(ref llvm_elem), _) => { let llvm_elem = one(ty_to_type(bcx.ccx, llvm_elem, &mut false)); @@ -626,7 +631,7 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, let ptr = bcx.pointercast(llresult, ty.ptr_to()); bcx.store(llval, ptr, Some(type_of::align_of(ccx, ret_ty))); } else { - store_ty(bcx, llval, llresult, ret_ty); + store_ty(bcx, llval, llresult, Alignment::AbiAligned, ret_ty); } } } @@ -782,10 +787,10 @@ fn trans_msvc_try<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, None => bug!("msvc_try_filter not defined"), }; let tok = catchpad.catch_pad(cs, &[tydesc, C_i32(ccx, 0), slot]); - let addr = catchpad.load(slot); - let arg1 = catchpad.load(addr); + let addr = catchpad.load(slot, None); + let arg1 = catchpad.load(addr, None); let val1 = C_i32(ccx, 1); - let arg2 = catchpad.load(catchpad.inbounds_gep(addr, &[val1])); + let arg2 = catchpad.load(catchpad.inbounds_gep(addr, &[val1]), None); let local_ptr = catchpad.bitcast(local_ptr, i64p); catchpad.store(arg1, local_ptr, None); catchpad.store(arg2, catchpad.inbounds_gep(local_ptr, &[val1]), None); diff --git a/src/librustc_trans/meth.rs b/src/librustc_trans/meth.rs index aecba2f57e52c..3033ae61d20c8 100644 --- a/src/librustc_trans/meth.rs +++ b/src/librustc_trans/meth.rs @@ -36,7 +36,7 @@ pub fn get_virtual_method<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, debug!("get_virtual_method(vtable_index={}, llvtable={:?})", vtable_index, Value(llvtable)); - bcx.load(bcx.gepi(llvtable, &[vtable_index + VTABLE_OFFSET])) + bcx.load(bcx.gepi(llvtable, &[vtable_index + VTABLE_OFFSET]), None) } /// Generate a shim function that allows an object type like `SomeTrait` to diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 7d4a1ab5ae70e..865635f085ee7 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -11,7 +11,7 @@ use llvm::{self, ValueRef, BasicBlockRef}; use rustc_const_eval::{ErrKind, ConstEvalErr, note_const_eval_err}; use rustc::middle::lang_items; -use rustc::ty::{self, layout}; +use rustc::ty::{self, layout, TypeFoldable}; use rustc::mir; use abi::{Abi, FnType, ArgType}; use adt; @@ -37,7 +37,7 @@ use std::cmp; use super::{MirContext, LocalRef}; use super::analyze::CleanupKind; use super::constant::Const; -use super::lvalue::LvalueRef; +use super::lvalue::{Alignment, LvalueRef}; use super::operand::OperandRef; use super::operand::OperandValue::{Pair, Ref, Immediate}; @@ -120,7 +120,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { bcx.cleanup_ret(cleanup_pad, None); } else { let ps = self.get_personality_slot(&bcx); - let lp = bcx.load(ps); + let lp = bcx.load(ps, None); Lifetime::End.call(&bcx, ps); if !bcx.sess().target.target.options.custom_unwind_resume { bcx.resume(lp); @@ -147,7 +147,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { mir::TerminatorKind::Switch { ref discr, ref adt_def, ref targets } => { let discr_lvalue = self.trans_lvalue(&bcx, discr); let ty = discr_lvalue.ty.to_ty(bcx.tcx()); - let discr = adt::trans_get_discr(&bcx, ty, discr_lvalue.llval, None, true); + let discr = adt::trans_get_discr( + &bcx, ty, discr_lvalue.llval, discr_lvalue.alignment, + None, true); let mut bb_hist = FxHashMap(); for target in targets { @@ -179,7 +181,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { let (otherwise, targets) = targets.split_last().unwrap(); - let discr = bcx.load(self.trans_lvalue(&bcx, discr).llval); + let lv = self.trans_lvalue(&bcx, discr); + let discr = bcx.load(lv.llval, lv.alignment.to_align()); let discr = base::to_immediate(&bcx, discr, switch_ty); let switch = bcx.switch(discr, llblock(self, *otherwise), values.len()); for (value, target) in values.iter().zip(targets) { @@ -202,7 +205,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { LocalRef::Operand(None) => bug!("use of return before def"), LocalRef::Lvalue(tr_lvalue) => { OperandRef { - val: Ref(tr_lvalue.llval), + val: Ref(tr_lvalue.llval, tr_lvalue.alignment), ty: tr_lvalue.ty.to_ty(bcx.tcx()) } } @@ -210,21 +213,23 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let llslot = match op.val { Immediate(_) | Pair(..) => { let llscratch = bcx.alloca(ret.original_ty, "ret"); - self.store_operand(&bcx, llscratch, op, None); + self.store_operand(&bcx, llscratch, None, op); llscratch } - Ref(llval) => llval + Ref(llval, align) => { + assert_eq!(align, Alignment::AbiAligned, + "return pointer is unaligned!"); + llval + } }; - let load = bcx.load(bcx.pointercast(llslot, cast_ty.ptr_to())); - let llalign = llalign_of_min(bcx.ccx, ret.ty); - unsafe { - llvm::LLVMSetAlignment(load, llalign); - } + let load = bcx.load( + bcx.pointercast(llslot, cast_ty.ptr_to()), + Some(llalign_of_min(bcx.ccx, ret.ty))); load } else { let op = self.trans_consume(&bcx, &mir::Lvalue::Local(mir::RETURN_POINTER)); - if let Ref(llval) = op.val { - base::load_ty(&bcx, llval, op.ty) + if let Ref(llval, align) = op.val { + base::load_ty(&bcx, llval, align, op.ty) } else { op.pack_if_pair(&bcx).immediate() } @@ -425,17 +430,14 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // The first argument is a thin destination pointer. let llptr = self.trans_operand(&bcx, &args[0]).immediate(); let val = self.trans_operand(&bcx, &args[1]); - self.store_operand(&bcx, llptr, val, None); + self.store_operand(&bcx, llptr, None, val); funclet_br(self, bcx, target); return; } if intrinsic == Some("transmute") { let &(ref dest, target) = destination.as_ref().unwrap(); - self.with_lvalue_ref(&bcx, dest, |this, dest| { - this.trans_transmute(&bcx, &args[0], dest); - }); - + self.trans_transmute(&bcx, &args[0], dest); funclet_br(self, bcx, target); return; } @@ -550,7 +552,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { if let ReturnDest::IndirectOperand(dst, _) = ret_dest { // Make a fake operand for store_return let op = OperandRef { - val: Ref(dst), + val: Ref(dst, Alignment::AbiAligned), ty: sig.output(), }; self.store_return(&bcx, ret_dest, fn_ty.ret, op); @@ -652,33 +654,39 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } // Force by-ref if we have to load through a cast pointer. - let (mut llval, by_ref) = match op.val { + let (mut llval, align, by_ref) = match op.val { Immediate(_) | Pair(..) => { if arg.is_indirect() || arg.cast.is_some() { let llscratch = bcx.alloca(arg.original_ty, "arg"); - self.store_operand(bcx, llscratch, op, None); - (llscratch, true) + self.store_operand(bcx, llscratch, None, op); + (llscratch, Alignment::AbiAligned, true) } else { - (op.pack_if_pair(bcx).immediate(), false) + (op.pack_if_pair(bcx).immediate(), Alignment::AbiAligned, false) } } - Ref(llval) => (llval, true) + Ref(llval, Alignment::Packed) if arg.is_indirect() => { + // `foo(packed.large_field)`. We can't pass the (unaligned) field directly. I + // think that ATM (Rust 1.16) we only pass temporaries, but we shouldn't + // have scary latent bugs around. + + let llscratch = bcx.alloca(arg.original_ty, "arg"); + base::memcpy_ty(bcx, llscratch, llval, op.ty, Some(1)); + (llscratch, Alignment::AbiAligned, true) + } + Ref(llval, align) => (llval, align, true) }; if by_ref && !arg.is_indirect() { // Have to load the argument, maybe while casting it. if arg.original_ty == Type::i1(bcx.ccx) { // We store bools as i8 so we need to truncate to i1. - llval = bcx.load_range_assert(llval, 0, 2, llvm::False); + llval = bcx.load_range_assert(llval, 0, 2, llvm::False, None); llval = bcx.trunc(llval, arg.original_ty); } else if let Some(ty) = arg.cast { - llval = bcx.load(bcx.pointercast(llval, ty.ptr_to())); - let llalign = llalign_of_min(bcx.ccx, arg.ty); - unsafe { - llvm::LLVMSetAlignment(llval, llalign); - } + llval = bcx.load(bcx.pointercast(llval, ty.ptr_to()), + align.min_with(llalign_of_min(bcx.ccx, arg.ty))); } else { - llval = bcx.load(llval); + llval = bcx.load(llval, align.to_align()); } } @@ -702,16 +710,16 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // Handle both by-ref and immediate tuples. match tuple.val { - Ref(llval) => { + Ref(llval, align) => { for (n, &ty) in arg_types.iter().enumerate() { - let ptr = LvalueRef::new_sized_ty(llval, tuple.ty); - let ptr = ptr.trans_field_ptr(bcx, n); + let ptr = LvalueRef::new_sized_ty(llval, tuple.ty, align); + let (ptr, align) = ptr.trans_field_ptr(bcx, n); let val = if common::type_is_fat_ptr(bcx.ccx, ty) { - let (lldata, llextra) = base::load_fat_ptr(bcx, ptr, ty); + let (lldata, llextra) = base::load_fat_ptr(bcx, ptr, align, ty); Pair(lldata, llextra) } else { // trans_argument will load this if it needs to - Ref(ptr) + Ref(ptr, align) }; let op = OperandRef { val: val, @@ -839,15 +847,15 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { return if fn_ret_ty.is_indirect() { // Odd, but possible, case, we have an operand temporary, // but the calling convention has an indirect return. - let tmp = bcx.alloca_ty(ret_ty, "tmp_ret"); - llargs.push(tmp); - ReturnDest::IndirectOperand(tmp, index) + let tmp = LvalueRef::alloca(bcx, ret_ty, "tmp_ret"); + llargs.push(tmp.llval); + ReturnDest::IndirectOperand(tmp.llval, index) } else if is_intrinsic { // Currently, intrinsics always need a location to store // the result. so we create a temporary alloca for the // result - let tmp = bcx.alloca_ty(ret_ty, "tmp_ret"); - ReturnDest::IndirectOperand(tmp, index) + let tmp = LvalueRef::alloca(bcx, ret_ty, "tmp_ret"); + ReturnDest::IndirectOperand(tmp.llval, index) } else { ReturnDest::DirectOperand(index) }; @@ -868,7 +876,34 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } fn trans_transmute(&mut self, bcx: &Builder<'a, 'tcx>, - src: &mir::Operand<'tcx>, dst: LvalueRef<'tcx>) { + src: &mir::Operand<'tcx>, + dst: &mir::Lvalue<'tcx>) { + if let mir::Lvalue::Local(index) = *dst { + match self.locals[index] { + LocalRef::Lvalue(lvalue) => self.trans_transmute_into(bcx, src, &lvalue), + LocalRef::Operand(None) => { + let lvalue_ty = self.monomorphized_lvalue_ty(dst); + assert!(!lvalue_ty.has_erasable_regions()); + let lvalue = LvalueRef::alloca(bcx, lvalue_ty, "transmute_temp"); + self.trans_transmute_into(bcx, src, &lvalue); + let op = self.trans_load(bcx, lvalue.llval, lvalue.alignment, lvalue_ty); + self.locals[index] = LocalRef::Operand(Some(op)); + } + LocalRef::Operand(Some(_)) => { + let ty = self.monomorphized_lvalue_ty(dst); + assert!(common::type_is_zero_size(bcx.ccx, ty), + "assigning to initialized SSAtemp"); + } + } + } else { + let dst = self.trans_lvalue(bcx, dst); + self.trans_transmute_into(bcx, src, &dst); + } + } + + fn trans_transmute_into(&mut self, bcx: &Builder<'a, 'tcx>, + src: &mir::Operand<'tcx>, + dst: &LvalueRef<'tcx>) { let mut val = self.trans_operand(bcx, src); if let ty::TyFnDef(def_id, substs, _) = val.ty.sty { let llouttype = type_of::type_of(bcx.ccx, dst.ty.to_ty(bcx.tcx())); @@ -892,7 +927,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let in_type = val.ty; let out_type = dst.ty.to_ty(bcx.tcx());; let llalign = cmp::min(align_of(bcx.ccx, in_type), align_of(bcx.ccx, out_type)); - self.store_operand(bcx, cast_ptr, val, Some(llalign)); + self.store_operand(bcx, cast_ptr, Some(llalign), val); } @@ -908,15 +943,15 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { Nothing => (), Store(dst) => ret_ty.store(bcx, op.immediate(), dst), IndirectOperand(tmp, index) => { - let op = self.trans_load(bcx, tmp, op.ty); + let op = self.trans_load(bcx, tmp, Alignment::AbiAligned, op.ty); self.locals[index] = LocalRef::Operand(Some(op)); } DirectOperand(index) => { // If there is a cast, we have to store and reload. let op = if ret_ty.cast.is_some() { - let tmp = bcx.alloca_ty(op.ty, "tmp_ret"); - ret_ty.store(bcx, op.immediate(), tmp); - self.trans_load(bcx, tmp, op.ty) + let tmp = LvalueRef::alloca(bcx, op.ty, "tmp_ret"); + ret_ty.store(bcx, op.immediate(), tmp.llval); + self.trans_load(bcx, tmp.llval, tmp.alignment, op.ty) } else { op.unpack_if_pair(bcx) }; diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 9ac2bea3b82fb..da9b3fcaa455b 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -40,6 +40,7 @@ use syntax_pos::Span; use std::fmt; use std::ptr; +use super::lvalue::Alignment; use super::operand::{OperandRef, OperandValue}; use super::MirContext; @@ -142,7 +143,7 @@ impl<'tcx> Const<'tcx> { // a constant LLVM global and cast its address if necessary. let align = type_of::align_of(ccx, self.ty); let ptr = consts::addr_of(ccx, self.llval, align, "const"); - OperandValue::Ref(consts::ptrcast(ptr, llty.ptr_to())) + OperandValue::Ref(consts::ptrcast(ptr, llty.ptr_to()), Alignment::AbiAligned) }; OperandRef { diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs index bd6e70639bba5..2538f32031fdb 100644 --- a/src/librustc_trans/mir/lvalue.rs +++ b/src/librustc_trans/mir/lvalue.rs @@ -15,20 +15,61 @@ use rustc::mir::tcx::LvalueTy; use rustc_data_structures::indexed_vec::Idx; use adt; use builder::Builder; -use common::{self, CrateContext, C_uint, C_undef}; +use common::{self, CrateContext, C_uint}; use consts; use machine; -use type_of::type_of; use type_of; use type_::Type; use value::Value; use glue; use std::ptr; +use std::ops; use super::{MirContext, LocalRef}; use super::operand::OperandValue; +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Alignment { + Packed, + AbiAligned, +} + +impl ops::BitOr for Alignment { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self { + match (self, rhs) { + (Alignment::Packed, _) => Alignment::Packed, + (Alignment::AbiAligned, a) => a, + } + } +} + +impl Alignment { + pub fn from_packed(packed: bool) -> Self { + if packed { + Alignment::Packed + } else { + Alignment::AbiAligned + } + } + + pub fn to_align(self) -> Option { + match self { + Alignment::Packed => Some(1), + Alignment::AbiAligned => None, + } + } + + pub fn min_with(self, align: u32) -> Option { + match self { + Alignment::Packed => Some(1), + Alignment::AbiAligned => Some(align), + } + } +} + #[derive(Copy, Clone, Debug)] pub struct LvalueRef<'tcx> { /// Pointer to the contents of the lvalue @@ -39,25 +80,38 @@ pub struct LvalueRef<'tcx> { /// Monomorphized type of this lvalue, including variant information pub ty: LvalueTy<'tcx>, + + /// Whether this lvalue is known to be aligned according to its layout + pub alignment: Alignment, } impl<'a, 'tcx> LvalueRef<'tcx> { - pub fn new_sized(llval: ValueRef, lvalue_ty: LvalueTy<'tcx>) -> LvalueRef<'tcx> { - LvalueRef { llval: llval, llextra: ptr::null_mut(), ty: lvalue_ty } + pub fn new_sized(llval: ValueRef, lvalue_ty: LvalueTy<'tcx>, + alignment: Alignment) -> LvalueRef<'tcx> { + LvalueRef { llval: llval, llextra: ptr::null_mut(), ty: lvalue_ty, alignment: alignment } } - pub fn new_sized_ty(llval: ValueRef, ty: Ty<'tcx>) -> LvalueRef<'tcx> { - LvalueRef::new_sized(llval, LvalueTy::from_ty(ty)) + pub fn new_sized_ty(llval: ValueRef, ty: Ty<'tcx>, alignment: Alignment) -> LvalueRef<'tcx> { + LvalueRef::new_sized(llval, LvalueTy::from_ty(ty), alignment) } - pub fn new_unsized_ty(llval: ValueRef, llextra: ValueRef, ty: Ty<'tcx>) -> LvalueRef<'tcx> { + pub fn new_unsized_ty(llval: ValueRef, llextra: ValueRef, ty: Ty<'tcx>, alignment: Alignment) + -> LvalueRef<'tcx> { LvalueRef { llval: llval, llextra: llextra, ty: LvalueTy::from_ty(ty), + alignment: alignment, } } + pub fn alloca(bcx: &Builder<'a, 'tcx>, ty: Ty<'tcx>, name: &str) -> LvalueRef<'tcx> { + debug!("alloca({:?}: {:?})", name, ty); + let tmp = bcx.alloca(type_of::type_of(bcx.ccx, ty), name); + assert!(!ty.has_param_types()); + Self::new_sized_ty(tmp, ty, Alignment::AbiAligned) + } + pub fn len(&self, ccx: &CrateContext<'a, 'tcx>) -> ValueRef { let ty = self.ty.to_ty(ccx.tcx()); match ty.sty { @@ -81,10 +135,12 @@ impl<'a, 'tcx> LvalueRef<'tcx> { fields: &Vec>, ix: usize, needs_cast: bool - ) -> ValueRef { + ) -> (ValueRef, Alignment) { let fty = fields[ix]; let ccx = bcx.ccx; + let alignment = self.alignment | Alignment::from_packed(st.packed); + let ptr_val = if needs_cast { let fields = st.field_index_by_increasing_offset().map(|i| { type_of::in_memory_type_of(ccx, fields[i]) @@ -101,14 +157,14 @@ impl<'a, 'tcx> LvalueRef<'tcx> { // * Field is sized - pointer is properly aligned already if st.offsets[ix] == layout::Size::from_bytes(0) || st.packed || bcx.ccx.shared().type_is_sized(fty) { - return bcx.struct_gep(ptr_val, st.memory_index[ix] as usize); + return (bcx.struct_gep(ptr_val, st.memory_index[ix] as usize), alignment); } // If the type of the last field is [T] or str, then we don't need to do // any adjusments match fty.sty { ty::TySlice(..) | ty::TyStr => { - return bcx.struct_gep(ptr_val, st.memory_index[ix] as usize); + return (bcx.struct_gep(ptr_val, st.memory_index[ix] as usize), alignment); } _ => () } @@ -117,7 +173,7 @@ impl<'a, 'tcx> LvalueRef<'tcx> { if !self.has_extra() { debug!("Unsized field `{}`, of `{:?}` has no metadata for adjustment", ix, Value(ptr_val)); - return bcx.struct_gep(ptr_val, ix); + return (bcx.struct_gep(ptr_val, ix), alignment); } // We need to get the pointer manually now. @@ -163,11 +219,11 @@ impl<'a, 'tcx> LvalueRef<'tcx> { // Finally, cast back to the type expected let ll_fty = type_of::in_memory_type_of(bcx.ccx, fty); debug!("struct_field_ptr: Field type is {:?}", ll_fty); - bcx.pointercast(byte_ptr, ll_fty.ptr_to()) + (bcx.pointercast(byte_ptr, ll_fty.ptr_to()), alignment) } /// Access a field, at a point when the value's case is known. - pub fn trans_field_ptr(self, bcx: &Builder<'a, 'tcx>, ix: usize) -> ValueRef { + pub fn trans_field_ptr(self, bcx: &Builder<'a, 'tcx>, ix: usize) -> (ValueRef, Alignment) { let discr = match self.ty { LvalueTy::Ty { .. } => 0, LvalueTy::Downcast { variant_index, .. } => variant_index, @@ -186,17 +242,18 @@ impl<'a, 'tcx> LvalueRef<'tcx> { layout::Vector { count, .. } => { assert_eq!(discr, 0); assert!((ix as u64) < count); - bcx.struct_gep(self.llval, ix) + (bcx.struct_gep(self.llval, ix), self.alignment) } layout::General { discr: d, ref variants, .. } => { let mut fields = adt::compute_fields(bcx.ccx, t, discr, false); fields.insert(0, d.to_ty(&bcx.tcx(), false)); self.struct_field_ptr(bcx, &variants[discr], &fields, ix + 1, true) } - layout::UntaggedUnion { .. } => { + layout::UntaggedUnion { ref variants } => { let fields = adt::compute_fields(bcx.ccx, t, 0, false); let ty = type_of::in_memory_type_of(bcx.ccx, fields[ix]); - bcx.pointercast(self.llval, ty.ptr_to()) + (bcx.pointercast(self.llval, ty.ptr_to()), + self.alignment | Alignment::from_packed(variants.packed)) } layout::RawNullablePointer { nndiscr, .. } | layout::StructWrappedNullablePointer { nndiscr, .. } if discr as u64 != nndiscr => { @@ -205,19 +262,19 @@ impl<'a, 'tcx> LvalueRef<'tcx> { // (e.d., Result of Either with (), as one side.) let ty = type_of::type_of(bcx.ccx, nullfields[ix]); assert_eq!(machine::llsize_of_alloc(bcx.ccx, ty), 0); - bcx.pointercast(self.llval, ty.ptr_to()) + (bcx.pointercast(self.llval, ty.ptr_to()), Alignment::Packed) } layout::RawNullablePointer { nndiscr, .. } => { let nnty = adt::compute_fields(bcx.ccx, t, nndiscr as usize, false)[0]; assert_eq!(ix, 0); assert_eq!(discr as u64, nndiscr); let ty = type_of::type_of(bcx.ccx, nnty); - bcx.pointercast(self.llval, ty.ptr_to()) + (bcx.pointercast(self.llval, ty.ptr_to()), self.alignment) } layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => { assert_eq!(discr as u64, nndiscr); self.struct_field_ptr(bcx, &nonnull, - &adt::compute_fields(bcx.ccx, t, discr, false), ix, false) + &adt::compute_fields(bcx.ccx, t, discr, false), ix, false) } _ => bug!("element access in type without elements: {} represented as {:#?}", t, l) } @@ -250,7 +307,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { mir::Lvalue::Static(def_id) => { let const_ty = self.monomorphized_lvalue_ty(lvalue); LvalueRef::new_sized(consts::get_static(ccx, def_id), - LvalueTy::from_ty(const_ty)) + LvalueTy::from_ty(const_ty), + Alignment::AbiAligned) }, mir::Lvalue::Projection(box mir::Projection { ref base, @@ -264,18 +322,20 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let (llptr, llextra) = match ptr.val { OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()), OperandValue::Pair(llptr, llextra) => (llptr, llextra), - OperandValue::Ref(_) => bug!("Deref of by-Ref type {:?}", ptr.ty) + OperandValue::Ref(..) => bug!("Deref of by-Ref type {:?}", ptr.ty) }; LvalueRef { llval: llptr, llextra: llextra, ty: projected_ty, + alignment: Alignment::AbiAligned, } } mir::Lvalue::Projection(ref projection) => { let tr_base = self.trans_lvalue(bcx, &projection.base); let projected_ty = tr_base.ty.projection_ty(tcx, &projection.elem); let projected_ty = self.monomorphize(&projected_ty); + let align = tr_base.alignment; let project_index = |llindex| { let element = if let ty::TySlice(_) = tr_base.ty.to_ty(tcx).sty { @@ -285,10 +345,10 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let zero = common::C_uint(bcx.ccx, 0u64); bcx.inbounds_gep(tr_base.llval, &[zero, llindex]) }; - element + (element, align) }; - let (llprojected, llextra) = match projection.elem { + let ((llprojected, align), llextra) = match projection.elem { mir::ProjectionElem::Deref => bug!(), mir::ProjectionElem::Field(ref field, _) => { let llextra = if self.ccx.shared().type_is_sized(projected_ty.to_ty(tcx)) { @@ -318,7 +378,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } mir::ProjectionElem::Subslice { from, to } => { let llindex = C_uint(bcx.ccx, from); - let llbase = project_index(llindex); + let (llbase, align) = project_index(llindex); let base_ty = tr_base.ty.to_ty(bcx.tcx()); match base_ty.sty { @@ -328,25 +388,26 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let base_ty = self.monomorphized_lvalue_ty(lvalue); let llbasety = type_of::type_of(bcx.ccx, base_ty).ptr_to(); let llbase = bcx.pointercast(llbase, llbasety); - (llbase, ptr::null_mut()) + ((llbase, align), ptr::null_mut()) } ty::TySlice(..) => { assert!(tr_base.llextra != ptr::null_mut()); let lllen = bcx.sub(tr_base.llextra, C_uint(bcx.ccx, from+to)); - (llbase, lllen) + ((llbase, align), lllen) } _ => bug!("unexpected type {:?} in Subslice", base_ty) } } mir::ProjectionElem::Downcast(..) => { - (tr_base.llval, tr_base.llextra) + ((tr_base.llval, align), tr_base.llextra) } }; LvalueRef { llval: llprojected, llextra: llextra, ty: projected_ty, + alignment: align, } } }; @@ -354,45 +415,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { result } - // Perform an action using the given Lvalue. - // If the Lvalue is an empty LocalRef::Operand, then a temporary stack slot - // is created first, then used as an operand to update the Lvalue. - pub fn with_lvalue_ref(&mut self, bcx: &Builder<'a, 'tcx>, - lvalue: &mir::Lvalue<'tcx>, f: F) -> U - where F: FnOnce(&mut Self, LvalueRef<'tcx>) -> U - { - if let mir::Lvalue::Local(index) = *lvalue { - match self.locals[index] { - LocalRef::Lvalue(lvalue) => f(self, lvalue), - LocalRef::Operand(None) => { - let lvalue_ty = self.monomorphized_lvalue_ty(lvalue); - assert!(!lvalue_ty.has_erasable_regions()); - let lltemp = bcx.alloca_ty(lvalue_ty, "lvalue_temp"); - let lvalue = LvalueRef::new_sized(lltemp, LvalueTy::from_ty(lvalue_ty)); - let ret = f(self, lvalue); - let op = self.trans_load(bcx, lvalue.llval, lvalue_ty); - self.locals[index] = LocalRef::Operand(Some(op)); - ret - } - LocalRef::Operand(Some(_)) => { - // See comments in LocalRef::new_operand as to why - // we always have Some in a ZST LocalRef::Operand. - let ty = self.monomorphized_lvalue_ty(lvalue); - if common::type_is_zero_size(bcx.ccx, ty) { - // Pass an undef pointer as no stores can actually occur. - let llptr = C_undef(type_of(bcx.ccx, ty).ptr_to()); - f(self, LvalueRef::new_sized(llptr, LvalueTy::from_ty(ty))) - } else { - bug!("Lvalue local already set"); - } - } - } - } else { - let lvalue = self.trans_lvalue(bcx, lvalue); - f(self, lvalue) - } - } - /// Adjust the bitwidth of an index since LLVM is less forgiving /// than we are. /// diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index eedd7956805b6..7920b9fd10724 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -38,7 +38,7 @@ use rustc_data_structures::indexed_vec::{IndexVec, Idx}; pub use self::constant::trans_static_initializer; use self::analyze::CleanupKind; -use self::lvalue::LvalueRef; +use self::lvalue::{Alignment, LvalueRef}; use rustc::mir::traversal; use self::operand::{OperandRef, OperandValue}; @@ -269,8 +269,7 @@ pub fn trans_mir<'a, 'tcx: 'a>( debug!("alloc: {:?} ({}) -> lvalue", local, name); assert!(!ty.has_erasable_regions()); - let lltemp = bcx.alloca_ty(ty, &name.as_str()); - let lvalue = LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty)); + let lvalue = LvalueRef::alloca(&bcx, ty, &name.as_str()); if dbg { let (scope, span) = mircx.debug_loc(source_info); declare_local(&bcx, &mircx.debug_context, name, ty, scope, @@ -283,12 +282,12 @@ pub fn trans_mir<'a, 'tcx: 'a>( if local == mir::RETURN_POINTER && mircx.fn_ty.ret.is_indirect() { debug!("alloc: {:?} (return pointer) -> lvalue", local); let llretptr = llvm::get_param(llfn, 0); - LocalRef::Lvalue(LvalueRef::new_sized(llretptr, LvalueTy::from_ty(ty))) + LocalRef::Lvalue(LvalueRef::new_sized(llretptr, LvalueTy::from_ty(ty), + Alignment::AbiAligned)) } else if lvalue_locals.contains(local.index()) { debug!("alloc: {:?} -> lvalue", local); assert!(!ty.has_erasable_regions()); - let lltemp = bcx.alloca_ty(ty, &format!("{:?}", local)); - LocalRef::Lvalue(LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty))) + LocalRef::Lvalue(LvalueRef::alloca(&bcx, ty, &format!("{:?}", local))) } else { // If this is an immediate local, we do not create an // alloca in advance. Instead we wait until we see the @@ -386,9 +385,9 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, _ => bug!("spread argument isn't a tuple?!") }; - let lltemp = bcx.alloca_ty(arg_ty, &format!("arg{}", arg_index)); + let lvalue = LvalueRef::alloca(bcx, arg_ty, &format!("arg{}", arg_index)); for (i, &tupled_arg_ty) in tupled_arg_tys.iter().enumerate() { - let dst = bcx.struct_gep(lltemp, i); + let dst = bcx.struct_gep(lvalue.llval, i); let arg = &mircx.fn_ty.args[idx]; idx += 1; if common::type_is_fat_ptr(bcx.ccx, tupled_arg_ty) { @@ -407,7 +406,7 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, // we can create one debuginfo entry for the argument. arg_scope.map(|scope| { let variable_access = VariableAccess::DirectVariable { - alloca: lltemp + alloca: lvalue.llval }; declare_local( bcx, @@ -420,7 +419,7 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, ); }); - return LocalRef::Lvalue(LvalueRef::new_sized(lltemp, LvalueTy::from_ty(arg_ty))); + return LocalRef::Lvalue(lvalue); } let arg = &mircx.fn_ty.args[idx]; @@ -467,21 +466,21 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, }; return LocalRef::Operand(Some(operand.unpack_if_pair(bcx))); } else { - let lltemp = bcx.alloca_ty(arg_ty, &format!("arg{}", arg_index)); + let lltemp = LvalueRef::alloca(bcx, arg_ty, &format!("arg{}", arg_index)); if common::type_is_fat_ptr(bcx.ccx, arg_ty) { // we pass fat pointers as two words, but we want to // represent them internally as a pointer to two words, // so make an alloca to store them in. let meta = &mircx.fn_ty.args[idx]; idx += 1; - arg.store_fn_arg(bcx, &mut llarg_idx, base::get_dataptr(bcx, lltemp)); - meta.store_fn_arg(bcx, &mut llarg_idx, base::get_meta(bcx, lltemp)); + arg.store_fn_arg(bcx, &mut llarg_idx, base::get_dataptr(bcx, lltemp.llval)); + meta.store_fn_arg(bcx, &mut llarg_idx, base::get_meta(bcx, lltemp.llval)); } else { // otherwise, arg is passed by value, so make a // temporary and store it there - arg.store_fn_arg(bcx, &mut llarg_idx, lltemp); + arg.store_fn_arg(bcx, &mut llarg_idx, lltemp.llval); } - lltemp + lltemp.llval }; arg_scope.map(|scope| { // Is this a regular argument? @@ -571,7 +570,8 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, ); } }); - LocalRef::Lvalue(LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty))) + LocalRef::Lvalue(LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty), + Alignment::AbiAligned)) }).collect() } diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs index 28a247ee612a9..cb77fcbbff85d 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -10,6 +10,7 @@ use llvm::ValueRef; use rustc::ty::Ty; +use rustc::ty::layout::Layout; use rustc::mir; use rustc_data_structures::indexed_vec::Idx; @@ -23,6 +24,7 @@ use type_::Type; use std::fmt; use super::{MirContext, LocalRef}; +use super::lvalue::Alignment; /// The representation of a Rust value. The enum variant is in fact /// uniquely determined by the value's type, but is kept as a @@ -31,7 +33,7 @@ use super::{MirContext, LocalRef}; pub enum OperandValue { /// A reference to the actual operand. The data is guaranteed /// to be valid for the operand's lifetime. - Ref(ValueRef), + Ref(ValueRef, Alignment), /// A single LLVM value. Immediate(ValueRef), /// A pair of immediate LLVM values. Used by fat pointers too. @@ -58,9 +60,9 @@ pub struct OperandRef<'tcx> { impl<'tcx> fmt::Debug for OperandRef<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.val { - OperandValue::Ref(r) => { - write!(f, "OperandRef(Ref({:?}) @ {:?})", - Value(r), self.ty) + OperandValue::Ref(r, align) => { + write!(f, "OperandRef(Ref({:?}, {:?}) @ {:?})", + Value(r), align, self.ty) } OperandValue::Immediate(i) => { write!(f, "OperandRef(Immediate({:?}) @ {:?})", @@ -137,27 +139,33 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { pub fn trans_load(&mut self, bcx: &Builder<'a, 'tcx>, llval: ValueRef, + align: Alignment, ty: Ty<'tcx>) -> OperandRef<'tcx> { debug!("trans_load: {:?} @ {:?}", Value(llval), ty); let val = if common::type_is_fat_ptr(bcx.ccx, ty) { - let (lldata, llextra) = base::load_fat_ptr(bcx, llval, ty); + let (lldata, llextra) = base::load_fat_ptr(bcx, llval, align, ty); OperandValue::Pair(lldata, llextra) } else if common::type_is_imm_pair(bcx.ccx, ty) { + let f_align = match *bcx.ccx.layout_of(ty) { + Layout::Univariant { ref variant, .. } => + Alignment::from_packed(variant.packed) | align, + _ => align + }; let [a_ty, b_ty] = common::type_pair_fields(bcx.ccx, ty).unwrap(); let a_ptr = bcx.struct_gep(llval, 0); let b_ptr = bcx.struct_gep(llval, 1); OperandValue::Pair( - base::load_ty(bcx, a_ptr, a_ty), - base::load_ty(bcx, b_ptr, b_ty) + base::load_ty(bcx, a_ptr, f_align, a_ty), + base::load_ty(bcx, b_ptr, f_align, b_ty) ) } else if common::type_is_immediate(bcx.ccx, ty) { - OperandValue::Immediate(base::load_ty(bcx, llval, ty)) + OperandValue::Immediate(base::load_ty(bcx, llval, align, ty)) } else { - OperandValue::Ref(llval) + OperandValue::Ref(llval, align) }; OperandRef { val: val, ty: ty } @@ -212,7 +220,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // out from their home let tr_lvalue = self.trans_lvalue(bcx, lvalue); let ty = tr_lvalue.ty.to_ty(bcx.tcx()); - self.trans_load(bcx, tr_lvalue.llval, ty) + self.trans_load(bcx, tr_lvalue.llval, tr_lvalue.alignment, ty) } pub fn trans_operand(&mut self, @@ -230,9 +238,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { mir::Operand::Constant(ref constant) => { let val = self.trans_constant(bcx, constant); let operand = val.to_operand(bcx.ccx); - if let OperandValue::Ref(ptr) = operand.val { + if let OperandValue::Ref(ptr, align) = operand.val { // If this is a OperandValue::Ref to an immediate constant, load it. - self.trans_load(bcx, ptr, operand.ty) + self.trans_load(bcx, ptr, align, operand.ty) } else { operand } @@ -243,8 +251,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { pub fn store_operand(&mut self, bcx: &Builder<'a, 'tcx>, lldest: ValueRef, - operand: OperandRef<'tcx>, - align: Option) { + align: Option, + operand: OperandRef<'tcx>) { debug!("store_operand: operand={:?}, align={:?}", operand, align); // Avoid generating stores of zero-sized values, because the only way to have a zero-sized // value is through `undef`, and store itself is useless. @@ -252,7 +260,10 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { return; } match operand.val { - OperandValue::Ref(r) => base::memcpy_ty(bcx, lldest, r, operand.ty, align), + OperandValue::Ref(r, Alignment::Packed) => + base::memcpy_ty(bcx, lldest, r, operand.ty, Some(1)), + OperandValue::Ref(r, Alignment::AbiAligned) => + base::memcpy_ty(bcx, lldest, r, operand.ty, align), OperandValue::Immediate(s) => { bcx.store(base::from_immediate(bcx, s), lldest, align); } diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 1b97a8d010cfe..90dde6fa30607 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -33,7 +33,7 @@ use Disr; use super::MirContext; use super::constant::const_scalar_checked_binop; use super::operand::{OperandRef, OperandValue}; -use super::lvalue::{LvalueRef}; +use super::lvalue::LvalueRef; impl<'a, 'tcx> MirContext<'a, 'tcx> { pub fn trans_rvalue(&mut self, @@ -50,7 +50,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let tr_operand = self.trans_operand(&bcx, operand); // FIXME: consider not copying constants through stack. (fixable by translating // constants into OperandValue::Ref, why don’t we do that yet if we don’t?) - self.store_operand(&bcx, dest.llval, tr_operand, None); + self.store_operand(&bcx, dest.llval, dest.alignment.to_align(), tr_operand); bcx } @@ -61,7 +61,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // into-coerce of a thin pointer to a fat pointer - just // use the operand path. let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); - self.store_operand(&bcx, dest.llval, temp, None); + self.store_operand(&bcx, dest.llval, dest.alignment.to_align(), temp); return bcx; } @@ -81,13 +81,15 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // index into the struct, and this case isn't // important enough for it. debug!("trans_rvalue: creating ugly alloca"); - let lltemp = bcx.alloca_ty(operand.ty, "__unsize_temp"); - base::store_ty(&bcx, llval, lltemp, operand.ty); - lltemp + let scratch = LvalueRef::alloca(&bcx, operand.ty, "__unsize_temp"); + base::store_ty(&bcx, llval, scratch.llval, scratch.alignment, operand.ty); + scratch + } + OperandValue::Ref(llref, align) => { + LvalueRef::new_sized_ty(llref, operand.ty, align) } - OperandValue::Ref(llref) => llref }; - base::coerce_unsized_into(&bcx, llref, operand.ty, dest.llval, cast_ty); + base::coerce_unsized_into(&bcx, &llref, &dest); bcx } @@ -97,7 +99,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let size = C_uint(bcx.ccx, size); let base = base::get_dataptr(&bcx, dest.llval); tvec::slice_for_each(&bcx, base, tr_elem.ty, size, |bcx, llslot| { - self.store_operand(bcx, llslot, tr_elem, None); + self.store_operand(bcx, llslot, dest.alignment.to_align(), tr_elem); }) } @@ -111,15 +113,16 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let op = self.trans_operand(&bcx, operand); // Do not generate stores and GEPis for zero-sized fields. if !common::type_is_zero_size(bcx.ccx, op.ty) { - let mut val = LvalueRef::new_sized(dest.llval, dest.ty); + let mut val = LvalueRef::new_sized( + dest.llval, dest.ty, dest.alignment); let field_index = active_field_index.unwrap_or(i); val.ty = LvalueTy::Downcast { adt_def: adt_def, substs: self.monomorphize(&substs), variant_index: disr.0 as usize, }; - let lldest_i = val.trans_field_ptr(&bcx, field_index); - self.store_operand(&bcx, lldest_i, op, None); + let (lldest_i, align) = val.trans_field_ptr(&bcx, field_index); + self.store_operand(&bcx, lldest_i, align.to_align(), op); } } }, @@ -131,6 +134,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } else { None }; + let alignment = dest.alignment; for (i, operand) in operands.iter().enumerate() { let op = self.trans_operand(&bcx, operand); // Do not generate stores and GEPis for zero-sized fields. @@ -144,7 +148,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { i }; let dest = bcx.gepi(dest.llval, &[0, i]); - self.store_operand(&bcx, dest, op, None); + self.store_operand(&bcx, dest, alignment.to_align(), op); } } } @@ -169,7 +173,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { _ => { assert!(rvalue_creates_operand(rvalue)); let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); - self.store_operand(&bcx, dest.llval, temp, None); + self.store_operand(&bcx, dest.llval, dest.alignment.to_align(), temp); bcx } } @@ -228,7 +232,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { operand.ty, cast_ty); OperandValue::Pair(lldata, llextra) } - OperandValue::Ref(_) => { + OperandValue::Ref(..) => { bug!("by-ref operand {:?} in trans_rvalue_operand", operand); } diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index faf4949e86192..3ced5ff9c6b91 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -198,7 +198,7 @@ impl UnwindSafe for *const T {} #[stable(feature = "catch_unwind", since = "1.9.0")] impl UnwindSafe for *mut T {} #[unstable(feature = "unique", issue = "27730")] -impl UnwindSafe for Unique {} +impl UnwindSafe for Unique {} #[unstable(feature = "shared", issue = "27730")] impl UnwindSafe for Shared {} #[stable(feature = "catch_unwind", since = "1.9.0")] diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 3480db8ec3b7d..341e4b1b9480c 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -304,11 +304,20 @@ impl<'a> Parser<'a> { if i + 1 < tts.len() { self.tts.push((tts, i + 1)); } - if let TokenTree::Token(sp, tok) = tt { - TokenAndSpan { tok: tok, sp: sp } - } else { - self.tts.push((tt, 0)); - continue + // FIXME(jseyfried): remove after fixing #39390 in #39419. + if self.quote_depth > 0 { + if let TokenTree::Sequence(sp, _) = tt { + self.span_err(sp, "attempted to repeat an expression containing no \ + syntax variables matched as repeating at this depth"); + } + } + match tt { + TokenTree::Token(sp, tok) => TokenAndSpan { tok: tok, sp: sp }, + _ if tt.len() > 0 => { + self.tts.push((tt, 0)); + continue + } + _ => continue, } } else { TokenAndSpan { tok: token::Eof, sp: self.span } @@ -804,6 +813,10 @@ impl<'a> Parser<'a> { let mut first: bool = true; let mut v = vec![]; while !kets.contains(&&self.token) { + match self.token { + token::CloseDelim(..) | token::Eof => break, + _ => {} + }; match sep.sep { Some(ref t) => { if first { @@ -2597,9 +2610,12 @@ impl<'a> Parser<'a> { return Ok((None, kleene_op)); } - let separator = self.bump_and_get(); + let separator = match self.token { + token::CloseDelim(..) => None, + _ => Some(self.bump_and_get()), + }; match parse_kleene_op(self)? { - Some(zerok) => Ok((Some(separator), zerok)), + Some(zerok) => Ok((separator, zerok)), None => return Err(self.fatal("expected `*` or `+`")) } } @@ -2636,7 +2652,7 @@ impl<'a> Parser<'a> { tts: tts, }))) }, - token::CloseDelim(_) | token::Eof => unreachable!(), + token::CloseDelim(..) | token::Eof => Ok(TokenTree::Token(self.span, token::Eof)), token::Dollar | token::SubstNt(..) if self.quote_depth > 0 => self.parse_unquoted(), _ => Ok(TokenTree::Token(self.span, self.bump_and_get())), } diff --git a/src/test/codegen/packed.rs b/src/test/codegen/packed.rs new file mode 100644 index 0000000000000..db2cd3b41656d --- /dev/null +++ b/src/test/codegen/packed.rs @@ -0,0 +1,29 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -C no-prepopulate-passes + +#![crate_type = "lib"] + +#[repr(packed)] +pub struct Packed { + dealign: u8, + data: u32 +} + +// CHECK-LABEL: @write_pkd +#[no_mangle] +pub fn write_pkd(pkd: &mut Packed) -> u32 { +// CHECK: %{{.*}} = load i32, i32* %{{.*}}, align 1 +// CHECK: store i32 42, i32* %{{.*}}, align 1 + let result = pkd.data; + pkd.data = 42; + result +} diff --git a/src/test/compile-fail/issue-17728.rs b/src/test/compile-fail/issue-17728.rs index f508d7123d88f..9724d17bef1ea 100644 --- a/src/test/compile-fail/issue-17728.rs +++ b/src/test/compile-fail/issue-17728.rs @@ -108,9 +108,6 @@ impl Debug for Player { fn str_to_direction(to_parse: &str) -> RoomDirection { match to_parse { //~ ERROR match arms have incompatible types - //~^ expected enum `RoomDirection`, found enum `std::option::Option` - //~| expected type `RoomDirection` - //~| found type `std::option::Option<_>` "w" | "west" => RoomDirection::West, "e" | "east" => RoomDirection::East, "n" | "north" => RoomDirection::North, @@ -119,7 +116,7 @@ fn str_to_direction(to_parse: &str) -> RoomDirection { "out" => RoomDirection::Out, "up" => RoomDirection::Up, "down" => RoomDirection::Down, - _ => None //~ NOTE match arm with an incompatible type + _ => None } } diff --git a/src/test/compile-fail/issue-39388.rs b/src/test/compile-fail/issue-39388.rs new file mode 100644 index 0000000000000..6994d2199d276 --- /dev/null +++ b/src/test/compile-fail/issue-39388.rs @@ -0,0 +1,17 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! assign { + (($($a:tt)*) = ($($b:tt))*) => { //~ ERROR expected `*` or `+` + $($a)* = $($b)* + } +} + +fn main() {} diff --git a/src/test/compile-fail/issue-39616.rs b/src/test/compile-fail/issue-39616.rs new file mode 100644 index 0000000000000..d601249c036b1 --- /dev/null +++ b/src/test/compile-fail/issue-39616.rs @@ -0,0 +1,15 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn foo(a: [0; 1]) {} //~ ERROR expected type, found `0` +//~| ERROR expected one of `->`, `where`, or `{`, found `]` +// FIXME(jseyfried): avoid emitting the second error (preexisting) + +fn main() {} diff --git a/src/test/compile-fail/issue-39709.rs b/src/test/compile-fail/issue-39709.rs new file mode 100644 index 0000000000000..0f66fe8439336 --- /dev/null +++ b/src/test/compile-fail/issue-39709.rs @@ -0,0 +1,15 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + println!("{}", { macro_rules! x { ($()*) => {} } 33 }); + //~^ ERROR no syntax variables matched as repeating at this depth +} + diff --git a/src/test/compile-fail/match-privately-empty.rs b/src/test/compile-fail/match-privately-empty.rs new file mode 100644 index 0000000000000..3affb1c03e952 --- /dev/null +++ b/src/test/compile-fail/match-privately-empty.rs @@ -0,0 +1,30 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(never_type)] + +mod private { + pub struct Private { + _bot: !, + pub misc: bool, + } + pub const DATA: Option = None; +} + +fn main() { + match private::DATA { + //~^ ERROR non-exhaustive patterns: `Some(Private { misc: true, .. })` not covered + None => {} + Some(private::Private { + misc: false, + .. + }) => {} + } +} diff --git a/src/test/compile-fail/uninhabited-matches-feature-gated.rs b/src/test/compile-fail/uninhabited-matches-feature-gated.rs index 0f8b0a6c238d0..0c3ea53a903ae 100644 --- a/src/test/compile-fail/uninhabited-matches-feature-gated.rs +++ b/src/test/compile-fail/uninhabited-matches-feature-gated.rs @@ -19,16 +19,13 @@ fn main() { }; let x: &Void = unsafe { std::mem::uninitialized() }; - let _ = match x {}; - //~^ ERROR non-exhaustive + let _ = match x {}; //~ ERROR non-exhaustive let x: (Void,) = unsafe { std::mem::uninitialized() }; - let _ = match x {}; - //~^ ERROR non-exhaustive + let _ = match x {}; //~ ERROR non-exhaustive let x: [Void; 1] = unsafe { std::mem::uninitialized() }; - let _ = match x {}; - //~^ ERROR non-exhaustive + let _ = match x {}; //~ ERROR non-exhaustive let x: &[Void] = unsafe { std::mem::uninitialized() }; let _ = match x { //~ ERROR non-exhaustive @@ -47,4 +44,3 @@ fn main() { let Ok(x) = x; //~^ ERROR refutable } - diff --git a/src/test/run-pass/const-enum-vec-index.rs b/src/test/run-pass/const-enum-vec-index.rs index 4af6a6d10d5bf..9c1a4dbdffa84 100644 --- a/src/test/run-pass/const-enum-vec-index.rs +++ b/src/test/run-pass/const-enum-vec-index.rs @@ -8,7 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[derive(Copy, Clone)] enum E { V1(isize), V0 } + const C: &'static [E] = &[E::V0, E::V1(0xDEADBEE)]; static C0: E = C[0]; static C1: E = C[1]; diff --git a/src/test/run-pass/issue-38972.rs b/src/test/run-pass/issue-38972.rs new file mode 100644 index 0000000000000..d5df84e0fb083 --- /dev/null +++ b/src/test/run-pass/issue-38972.rs @@ -0,0 +1,25 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This issue tracks a regression (a new warning) without +// feature(never_type). When we make that the default, please +// remove this test. + +enum Foo { } + +fn make_foo() -> Option { None } + +#[deny(warnings)] +fn main() { + match make_foo() { + None => {}, + Some(_) => {} + } +} diff --git a/src/test/run-pass/panic-safe.rs b/src/test/run-pass/panic-safe.rs index 493a00ac5d00d..9bda07f077f06 100644 --- a/src/test/run-pass/panic-safe.rs +++ b/src/test/run-pass/panic-safe.rs @@ -9,7 +9,6 @@ // except according to those terms. #![allow(dead_code)] -#![feature(recover)] use std::panic::{UnwindSafe, AssertUnwindSafe}; use std::cell::RefCell; @@ -40,6 +39,10 @@ fn main() { assert::<&RwLock>(); assert::>(); assert::>(); + assert::>(); + + trait Trait: UnwindSafe {} + assert::>(); fn bar() { assert::>(); diff --git a/src/test/ui/did_you_mean/issue-39544.rs b/src/test/ui/did_you_mean/issue-39544.rs new file mode 100644 index 0000000000000..bcdafefa2472b --- /dev/null +++ b/src/test/ui/did_you_mean/issue-39544.rs @@ -0,0 +1,22 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +enum X { + Y +} + +struct Z { + x: X +} + +fn main() { + let z = Z { x: X::Y }; + let _ = &mut z.x; +} diff --git a/src/test/ui/did_you_mean/issue-39544.stderr b/src/test/ui/did_you_mean/issue-39544.stderr new file mode 100644 index 0000000000000..c0088f39ad3e1 --- /dev/null +++ b/src/test/ui/did_you_mean/issue-39544.stderr @@ -0,0 +1,8 @@ +error: cannot borrow immutable field `z.x` as mutable + --> $DIR/issue-39544.rs:21:18 + | +21 | let _ = &mut z.x; + | ^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.rs b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.rs new file mode 100644 index 0000000000000..30239f4c0946c --- /dev/null +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.rs @@ -0,0 +1,15 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 { + if x > y { x } else { y } +} + +fn main() { } diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.stderr b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.stderr new file mode 100644 index 0000000000000..85e05422ab3b2 --- /dev/null +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.stderr @@ -0,0 +1,25 @@ +error[E0312]: lifetime of reference outlives lifetime of borrowed content... + --> $DIR/ex1-return-one-existing-name-if-else.rs:12:27 + | +12 | if x > y { x } else { y } + | ^ + | +note: ...the reference is valid for the lifetime 'a as defined on the body at 11:43... + --> $DIR/ex1-return-one-existing-name-if-else.rs:11:44 + | +11 | fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 { + | ____________________________________________^ starting here... +12 | | if x > y { x } else { y } +13 | | } + | |_^ ...ending here +note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the body at 11:43 + --> $DIR/ex1-return-one-existing-name-if-else.rs:11:44 + | +11 | fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 { + | ____________________________________________^ starting here... +12 | | if x > y { x } else { y } +13 | | } + | |_^ ...ending here + +error: aborting due to previous error + diff --git a/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.rs b/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.rs new file mode 100644 index 0000000000000..098950e13b315 --- /dev/null +++ b/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.rs @@ -0,0 +1,15 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn foo(x: &i32, y: &i32) -> &i32 { + if x > y { x } else { y } +} + +fn main() { } diff --git a/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.stderr b/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.stderr new file mode 100644 index 0000000000000..fccc44caac81a --- /dev/null +++ b/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.stderr @@ -0,0 +1,10 @@ +error[E0106]: missing lifetime specifier + --> $DIR/ex1b-return-no-names-if-else.rs:11:29 + | +11 | fn foo(x: &i32, y: &i32) -> &i32 { + | ^ expected lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y` + +error: aborting due to previous error + diff --git a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.rs b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.rs new file mode 100644 index 0000000000000..71a1c865e0995 --- /dev/null +++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.rs @@ -0,0 +1,19 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Ref<'a, T: 'a> { + data: &'a T +} + +fn foo<'a>(x: &mut Vec>, y: Ref) { + x.push(y); +} + +fn main() { } diff --git a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr new file mode 100644 index 0000000000000..6f42a9f679a6a --- /dev/null +++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr @@ -0,0 +1,27 @@ +error[E0308]: mismatched types + --> $DIR/ex2a-push-one-existing-name.rs:16:12 + | +16 | x.push(y); + | ^ lifetime mismatch + | + = note: expected type `Ref<'a, i32>` + found type `Ref<'_, i32>` +note: the anonymous lifetime #2 defined on the body at 15:51... + --> $DIR/ex2a-push-one-existing-name.rs:15:52 + | +15 | fn foo<'a>(x: &mut Vec>, y: Ref) { + | ____________________________________________________^ starting here... +16 | | x.push(y); +17 | | } + | |_^ ...ending here +note: ...does not necessarily outlive the lifetime 'a as defined on the body at 15:51 + --> $DIR/ex2a-push-one-existing-name.rs:15:52 + | +15 | fn foo<'a>(x: &mut Vec>, y: Ref) { + | ____________________________________________________^ starting here... +16 | | x.push(y); +17 | | } + | |_^ ...ending here + +error: aborting due to previous error + diff --git a/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.rs b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.rs new file mode 100644 index 0000000000000..09038d8ce9027 --- /dev/null +++ b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.rs @@ -0,0 +1,19 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Ref<'a, T: 'a> { + data: &'a T +} + +fn foo(x: &mut Vec>, y: Ref) { + x.push(y); +} + +fn main() { } diff --git a/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr new file mode 100644 index 0000000000000..edc1c2362de57 --- /dev/null +++ b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr @@ -0,0 +1,27 @@ +error[E0308]: mismatched types + --> $DIR/ex2b-push-no-existing-names.rs:16:12 + | +16 | x.push(y); + | ^ lifetime mismatch + | + = note: expected type `Ref<'_, i32>` + found type `Ref<'_, i32>` +note: the anonymous lifetime #3 defined on the body at 15:43... + --> $DIR/ex2b-push-no-existing-names.rs:15:44 + | +15 | fn foo(x: &mut Vec>, y: Ref) { + | ____________________________________________^ starting here... +16 | | x.push(y); +17 | | } + | |_^ ...ending here +note: ...does not necessarily outlive the anonymous lifetime #2 defined on the body at 15:43 + --> $DIR/ex2b-push-no-existing-names.rs:15:44 + | +15 | fn foo(x: &mut Vec>, y: Ref) { + | ____________________________________________^ starting here... +16 | | x.push(y); +17 | | } + | |_^ ...ending here + +error: aborting due to previous error + diff --git a/src/test/ui/lifetime-errors/ex2c-push-inference-variable.rs b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.rs new file mode 100644 index 0000000000000..cb083f778deeb --- /dev/null +++ b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.rs @@ -0,0 +1,20 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Ref<'a, T: 'a> { + data: &'a T +} + +fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { + let z = Ref { data: y.data }; + x.push(z); +} + +fn main() { } diff --git a/src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr new file mode 100644 index 0000000000000..755b71d4a1d9e --- /dev/null +++ b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr @@ -0,0 +1,37 @@ +error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements + --> $DIR/ex2c-push-inference-variable.rs:16:13 + | +16 | let z = Ref { data: y.data }; + | ^^^ + | +note: first, the lifetime cannot outlive the lifetime 'c as defined on the body at 15:66... + --> $DIR/ex2c-push-inference-variable.rs:15:67 + | +15 | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { + | ___________________________________________________________________^ starting here... +16 | | let z = Ref { data: y.data }; +17 | | x.push(z); +18 | | } + | |_^ ...ending here +note: ...so that reference does not outlive borrowed content + --> $DIR/ex2c-push-inference-variable.rs:16:25 + | +16 | let z = Ref { data: y.data }; + | ^^^^^^ +note: but, the lifetime must be valid for the lifetime 'b as defined on the body at 15:66... + --> $DIR/ex2c-push-inference-variable.rs:15:67 + | +15 | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { + | ___________________________________________________________________^ starting here... +16 | | let z = Ref { data: y.data }; +17 | | x.push(z); +18 | | } + | |_^ ...ending here +note: ...so that expression is assignable (expected Ref<'b, i32>, found Ref<'_, i32>) + --> $DIR/ex2c-push-inference-variable.rs:17:12 + | +17 | x.push(z); + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.rs b/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.rs new file mode 100644 index 0000000000000..bcb7583beefcf --- /dev/null +++ b/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.rs @@ -0,0 +1,21 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Ref<'a, T: 'a> { + data: &'a T +} + +fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { + let a: &mut Vec> = x; + let b = Ref { data: y.data }; + a.push(b); +} + +fn main() { } diff --git a/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.stderr b/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.stderr new file mode 100644 index 0000000000000..daa6ea2d91aa3 --- /dev/null +++ b/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.stderr @@ -0,0 +1,39 @@ +error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements + --> $DIR/ex2d-push-inference-variable-2.rs:17:13 + | +17 | let b = Ref { data: y.data }; + | ^^^ + | +note: first, the lifetime cannot outlive the lifetime 'c as defined on the body at 15:66... + --> $DIR/ex2d-push-inference-variable-2.rs:15:67 + | +15 | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { + | ___________________________________________________________________^ starting here... +16 | | let a: &mut Vec> = x; +17 | | let b = Ref { data: y.data }; +18 | | a.push(b); +19 | | } + | |_^ ...ending here +note: ...so that reference does not outlive borrowed content + --> $DIR/ex2d-push-inference-variable-2.rs:17:25 + | +17 | let b = Ref { data: y.data }; + | ^^^^^^ +note: but, the lifetime must be valid for the lifetime 'b as defined on the body at 15:66... + --> $DIR/ex2d-push-inference-variable-2.rs:15:67 + | +15 | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { + | ___________________________________________________________________^ starting here... +16 | | let a: &mut Vec> = x; +17 | | let b = Ref { data: y.data }; +18 | | a.push(b); +19 | | } + | |_^ ...ending here +note: ...so that expression is assignable (expected &mut std::vec::Vec>, found &mut std::vec::Vec>) + --> $DIR/ex2d-push-inference-variable-2.rs:16:33 + | +16 | let a: &mut Vec> = x; + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.rs b/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.rs new file mode 100644 index 0000000000000..2d05adb7ecd37 --- /dev/null +++ b/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.rs @@ -0,0 +1,21 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Ref<'a, T: 'a> { + data: &'a T +} + +fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { + let a: &mut Vec> = x; + let b = Ref { data: y.data }; + Vec::push(a, b); +} + +fn main() { } diff --git a/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.stderr b/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.stderr new file mode 100644 index 0000000000000..b679532a4d910 --- /dev/null +++ b/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.stderr @@ -0,0 +1,39 @@ +error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements + --> $DIR/ex2e-push-inference-variable-3.rs:17:13 + | +17 | let b = Ref { data: y.data }; + | ^^^ + | +note: first, the lifetime cannot outlive the lifetime 'c as defined on the body at 15:66... + --> $DIR/ex2e-push-inference-variable-3.rs:15:67 + | +15 | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { + | ___________________________________________________________________^ starting here... +16 | | let a: &mut Vec> = x; +17 | | let b = Ref { data: y.data }; +18 | | Vec::push(a, b); +19 | | } + | |_^ ...ending here +note: ...so that reference does not outlive borrowed content + --> $DIR/ex2e-push-inference-variable-3.rs:17:25 + | +17 | let b = Ref { data: y.data }; + | ^^^^^^ +note: but, the lifetime must be valid for the lifetime 'b as defined on the body at 15:66... + --> $DIR/ex2e-push-inference-variable-3.rs:15:67 + | +15 | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { + | ___________________________________________________________________^ starting here... +16 | | let a: &mut Vec> = x; +17 | | let b = Ref { data: y.data }; +18 | | Vec::push(a, b); +19 | | } + | |_^ ...ending here +note: ...so that expression is assignable (expected &mut std::vec::Vec>, found &mut std::vec::Vec>) + --> $DIR/ex2e-push-inference-variable-3.rs:16:33 + | +16 | let a: &mut Vec> = x; + | ^ + +error: aborting due to previous error +